Make App Pie

Training for Developers and Artists

Introducing Core Motion: Make A Pedometer

Apple packed a lot of sensors into the small packages that are iOS devices. You as a developer can detect movement in three dimensions, acceleration, rotations, and impact and use those in your applications. All of this brought to your application in the CoreMotion framework.

CoreMotion has many uses. Game developers use it to make a phone into a gaming device where the phone’s motion controls the game play. Fitness developers use core motion to measure types of movements common to a sport on both the iPhone and Apple Watch. Using the sensors and Core motion could measure a swing of a baseball bat or golf club for example.  Basic movements can also let other application execute a function. The Apple watch wakes up to a raise of the wrist for example.

Core motion is a complex  framework to use, not only for coding reasons. As it deals with motion, a developer might need to know more about that motion before developing an application. The framework has some low-level objects for working directly with the sensors.  There are, however, some motions that are so universal, core motion combines several sensors to get more bottom-line data. One good example of this kind of motion is putting one foot in front of the other. For walking and running motions CoreMotion has the CMPedometer class. The class gives you direct data about the number of steps,   To give you a taste of some of the issues involved in developing for Core motion, let’s make a pedometer from the CMPedometer class. You’ll need to hook up a phone to Xcode since the simulator does not work with Core Motion.

Setting Up the Project

Create a new single-view project named CoreMotionPedometer. Use Swift as a language and a iPhone for a device.  Save the project.

When the applications loads, change the Display Name to CMPedometer

2017-02-10_06-03-10

This will give us a smaller name for the Icon. In this application you may need to test off of Xcode, and this will help you find your app better.

Core motion in general and the pedometer more specifically uses several motions that Apple considers private data. On a pedometer, you’re also using location data, so privacy is an even bigger issue. Developers need to add a key NSMotionUsageDescription to the info.plist to ask permission of the user. You can add the key in XML like this:

<key>NSMotionUsageDescription</key>
<string>This is a step counter, and we&apos;d like to track motion. </string>

Most people will just go to the info.plist and add a entry to the dictionary.
2017-02-10_06-11-09

In the drop down, select Privacy – Motion Usage Descriptor
2017-02-10_06-10-45

This creates a key with a String value. The string will be the body of an alert to accept the privacy settings for the pedometer. Add This is a step counter, and we’d like to track motion.
2017-02-10_06-17-55

Designing the Storyboard

Go to the main storyboard. Add Five labels and a button. Place the button towards the bottom of the scene and the labels towards the top. Make the labels white, the button a green background and the scene background to black like this:

2017-02-10_06-26-20

Layout the button on the bottom of the phone a quarter of the height of the device. Change the tile color for the Start button to White, and the title font to System Black 28pt. Using the auto layout pin tool pinMenuButton, turn off the Constrain to Margins  and set the left to 0, bottom to 0 and right to 0.

2017-02-10_06-31-35

Add the three constraints. Control drag from the green of the button up to the black of the background.

2017-02-10_06-39-09

When you release the mouse button, select Equal Heights in the menu that appears

2017-02-10_06-37-13

The start button will fill the screen.  Go to the size inspector. Find the Equal Height to Superview constraint, and click the Edit button there.

2017-02-10_06-41-07

In the edit dialog that appears change the Multiplier to 1:4.

2017-02-10_06-45-07

You’ll have a nice big button on the bottom.

2017-02-10_06-48-00

I like having a big start/stop button. If you are doing a workout, it may be difficult to hit a small button, and this helps the user hit the button. It’s on the bottom in the thumb zone so the user can hit it one handed.

Select the Pedometer Demo label. In the attribute inspector, set the font to 22pt System heavy.  Using the auto layout pin tool pinMenuButton, turn  Using the auto layout pin tool pinMenuButton, turn on the Constrain to Margins  and set the Top to 20,  Left to 0 and Right to 0. Add the constraints.

Select the other four labels.  Set the font to System Heavy 17pt, Lines to 2 and Line Break to Word Wrap

2017-02-10_06-53-00

You’ll be using two lines of text in this demo, one for a metric measurement and one for an Imperial measurement used in most sporting events.  You’ll place them on two lines in the same label.

With all four labels selected, select the Stackview tool  stack view button from the autolayout toolbar in the lower right. The labels will embed in a vertical stack view.  Change the stack view to these attributes:

2017-02-10_07-02-31

Use the pin tool to pin all four sides of the stack view 10 points. Update the constraints.  You’ll have a storyboard like this one.

2017-02-10_07-04-04

Close the attributes inspector and open the assistant editor. Control drag from the labels and make the following outlets, with the statusTitle outlet to the Pedometer Demo label and the rest to their respective labels :

@IBOutlet weak var statusTitle: UILabel!
@IBOutlet weak var stepsLabel: UILabel!
@IBOutlet weak var avgPaceLabel: UILabel!
@IBOutlet weak var paceLabel: UILabel!
@IBOutlet weak var distanceLabel: UILabel!

Now add at the action. Control-drag from the button and add an action

@IBAction func startStopButton(_ sender: UIButton) {
}

Some Preliminary Coding

Before we code the pedometer, I’m going to add a few properties and functions that will help us later. Close the assistant editor and go to the ViewController.swift file.

A very critical line goes just under the import UIKit line, where we import core motion.

import CoreMotion

Then add these properties and constants.

    
    let stopColor = UIColor(red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0)
    let startColor = UIColor(red: 0.0, green: 0.75, blue: 0.0, alpha: 1.0)
    // values for the pedometer data
    var numberOfSteps:Int! = nil
    var distance:Double! = nil
    var averagePace:Double! = nil
    var pace:Double! = nil

The constants will change the color of the stat stop button. You’ll store values in these for the properties of the pedometer object. Of course you need a pedometer object. Add this after these properties:

var pedometer = CMPedometer()

Pedometers don’t compute elapsed time. You might want that too in your pedometer. Add these two more properties to use a timer. :

    // timers
    var timer = Timer()
    let timerInterval = 1.0
    var timeElapsed:TimeInterval = 0.0

As you’ll find out, there’s more to this timer than just recording time.

You’ll need some more  methods to handle some unit conversions you’ll do later, so add these:

   //MARK: - Display and time format functions
    
    // convert seconds to hh:mm:ss as a string
    func timeIntervalFormat(interval:TimeInterval)-> String{
        var seconds = Int(interval + 0.5) //round up seconds
        let hours = seconds / 3600
        let minutes = (seconds / 60) % 60
        seconds = seconds % 60
        return String(format:"%02i:%02i:%02i",hours,minutes,seconds)
    }
    // convert a pace in meters per second to a string with
    // the metric m/s and the Imperial minutes per mile
    func paceString(title:String,pace:Double) -> String{
        var minPerMile = 0.0
        let factor = 26.8224 //conversion factor
        if pace != 0 {
            minPerMile = factor / pace
        }
        let minutes = Int(minPerMile)
        let seconds = Int(minPerMile * 60) % 60
        return String(format: "%@: %02.2f m/s \n\t\t %02i:%02i min/mi",title,pace,minutes,seconds)
    }
    
func computedAvgPace()-> Double {
    if let distance = self.distance{
        pace = distance / timeElapsed
        return pace
    } else {
        return 0.0
    }
}

func miles(meters:Double)-> Double{
        let mile = 0.000621371192
        return meters * mile
    }

The pedometer works in Metric, returning meters and meters per second. If you want other units such as a distance in miles, or a pace in minute per mile, you’ll need these to calculate the values. I convert times to strings in hh:mm:ss here too.

Coding the Pedometer

With all these functions in place, your’e ready to code the pedometer. You’ll use the startStopButton‘s action to toggle between starting and stopping the pedometer. Add this to the startStopButton code

if sender.titleLabel?.text == "Start"{
    //Start the pedometer
   //Toggle the UI to on state
   statusTitle.text = "Pedometer On"
   sender.setTitle("Stop", for: .normal)
   sender.backgroundColor = stopColor
} else {
   //Stop the pedometer
   //Toggle the UI to off state
   statusTitle.text = "Pedometer Off: "
   sender.backgroundColor = startColor
   sender.setTitle("Start", for: .normal)
}

You have button that toggles and appears correctly on the user interface. Below the Start the pedometer comment add this:

pedometer = CMPedometer()
pedometer.startUpdates(from: Date(), withHandler: { (pedometerData, error) in
    if let pedData = pedometerData{
            self.stepsLabel.text = "Steps:\(pedData.numberOfSteps)"
    } else {
            self.stepsLabel.text = "Steps: Not Available"
    }
})

The first line of this code clears the pedometer by creating a new instance in the pedometer property. The startUpdates:FromDate:withHandler: method starts sending updates from the pedometer. When an update occurs the handler within the closure executes. It checks of the pedometer is nil. If nil, the pedometer is not available. There are several other ways of checking this, but this is the most direct. If there is a value, the pedometer property number of steps is sent to the stepsLabel‘s text property.

To stop a pedometer, add the following under Stop the pedometer comment.

pedometer.stopUpdates()

Run the Application

You are ready to run our first iteration. However, core motion only works on a real device, not the simulator. Plug your phone into a device, and select the device in the run schemes. I’ll assume you know how to do this and how to set up your device for testing. Run the application.
You’ll get a screen like this:

052fa7ca-aa18-46fd-9127-06023afccf80

Press Start. You’ll get a screen asking for permission. Tap OK

112f8982-77fb-40af-b2ef-35842bc3e2f4

Pick up your phone. Move your arms back and forth for 30 seconds like you are jogging. This is one of the motions that the pedometer uses to count steps. Press STOP.

0a9e7fae-7abc-4372-b9b4-311ac9d13e9f

While you see a few steps on the pedometer, you also see a lot of errors in the console Most of them you can ignore — they are errors related to internal caches you can’t touch. The very last error on the other hand is very important:
This application is modifying the autolayout engine from a background thread after the engine was accessed from the main thread. This can lead to engine corruption and weird crashes.

What’s happening here? Remember the code for updating is a closure, and runs on a separate thread from the main thread where UI updates happen. The closure tries to access the main thread when it really shouldn’t, because its timing is not the same as the main thread. This can crash the app. While this specifically mentions auto layout, I’d suggest never directly changing outlets from the closure to avoid any bad behavior.

Using Timer Loops

Instead of setting the labels in the closure, you set a property. Change this:

self.stepsLabel.text = "Steps:\(pedData.numberOfSteps)"

to this:

self.numberOfSteps = Int(pedData.numberOfSteps)

The pedometer numberOfSteps property is of type NSNumber. You must covert it to an Int to use in the ViewController numberOfSteps property.
You might think you can use a property observer to change the display. For example change the numberOfSteps property to this:

// values for the pedometer data
var numberOfSteps:Int! = nil{
    didSet{
        stepsLabel.text = "Steps:\(numberOfSteps)"
    }
}

When the property changes, the label changes. You can run this code and do a simulated jog with your device. IF you do, you get that error message again. In code this is still updating the pedometer in the handler thread. You need a thread that has no problem updating to the view. That’s a timer.

Comment out the property observer:

// values for the pedometer data
var numberOfSteps:Int! = nil
/*{  //this does not work. 
    didSet{
        stepsLabel.text = "Steps:\(numberOfSteps)"
    }
}*/

We earlier declared some timer properties. You’ll use that to set up a timer with these functions:

    //MARK: - timer functions
        func startTimer(){
        if timer.isValid { timer.invalidate() }
        timer = Timer.scheduledTimer(timeInterval: timerInterval,target: self,selector: #selector(timerAction(timer:)) ,userInfo: nil,repeats: true)
    }
    
    func stopTimer(){
        timer.invalidate()
        displayPedometerData()
    }
    
    func timerAction(timer:Timer){
        displayPedometerData()
    }

I discuss timers in more detail here. Basically startTimer starts a timer with a 1 second interval that repeats. I’m being course here, you can set the interval to a finer resolution if you wish. Every second it calls the function timerAction from the selector. In timerAction I call a function displayPedometerData I’ve yet to define which will display my pedometer data. The stopTimer function shuts down the timer and updates the display one last time. Add the startTimer and stopTimer functions to the button action so it starts and stop the timer when the pedometer starts and stops.

@IBAction func startStopButton(_ sender: UIButton) {
        if sender.titleLabel?.text == "Start"{
            //Start the pedometer
            pedometer = CMPedometer()
            startTimer() //start the timer
            pedometer.startUpdates(from: Date(), withHandler: { (pedometerData, error) in
                if let pedData = pedometerData{
                    self.numberOfSteps = Int(pedData.numberOfSteps)
                    //self.stepsLabel.text = "Steps:\(pedData.numberOfSteps)"
                } else {
                    self.stepsLabel.text = "Steps: Not Available"
                }
            })
            //Toggle the UI to on state
            statusTitle.text = "Pedometer On"
            sender.setTitle("Stop", for: .normal)
            sender.backgroundColor = stopColor
        } else {
            //Stop the pedometer
            pedometer.stopUpdates()
            stopTimer() // stop the timer
            //Toggle the UI to off state
            statusTitle.text = "Pedometer Off: "
            sender.backgroundColor = startColor
            sender.setTitle("Start", for: .normal)
        }
    }

Create a new function to update the view. For now we’ll update just the steps again.

func displayPedometerData(){  
    //Number of steps
    if let numberOfSteps = self.numberOfSteps{
        stepsLabel.text = String(format:"Steps: %i",numberOfSteps)
     }
}

I did two more things than I did with the property observer. I used an optional chain to unwrap numberOfSteps, and then used as String initializer to format the string.
If you run the application and do your little in-place run, you’ll notice two differences: the step count updates faster than before and the error message disappears. We only have that CoreLocation cache warning on the console. The timer thread indirectly updated the display separating the pedometer thread from the main thread.

Adding Elapsed Time

One advantage to a timer loop is we have a timer. Usually I use a higher resolution timer(0.1 seconds for example), but for this lesson I’ll leave it at a one second interval.

I can change the displayPedometerData function to this:

func displayPedometerData(){
    //Time Elapsed
    timeElapsed += self.timerInterval
    statusTitle.text = "On: " + timeIntervalFormat(interval: timeElapsed)
        //Number of steps
        if let numberOfSteps = self.numberOfSteps{
            stepsLabel.text = String(format:"Steps: %i",numberOfSteps)
        }
    }

I increment the pedometer with a property timeElapsed I created earlier. It keeps a count of the number of seconds elapsed since I started the pedometer. I display it using one of the formatting functions we added earlier that displays the time as hh:mm:ss.

To keep this time after you stop the timer, append the timeIntervalFormat function to the status title label

statusTitle.text = "Pedometer Off: " + timeIntervalFormat(interval: timeElapsed)

Build and run. Start the pedometer. You’ll get both a timer and step count now.

9872d97b-a238-4012-8597-a74b3732a053

Stop the pedometer.

Adding Other Pedometer Properties

There’s several other properties of pedometers. I selected three more to show on our pedometer: current pace, Average pace and distance. Why you are getting that core location cache message makes sense now: the Pedometer checks your location repeatedly using CoreLocation. You have no control over that which is why I said to ignore the warning message. With that location data, the pedometer computes distance, and from the steps, distance, and it’s own timer pace and average pace.

All of these properties are optional. If the hardware or property is unavailable or nonexistent, the property returns nil. However if you can’t get pace from a pedometer, you can compute the average pace from the distance and time. I made a function earlier computedAvgPace that will compute an average pace or leave it as 0 if core location is not available.

To implement the other properties, change the startUpdates closure to add the pedometer data to the viewController’s properties:

  pedometer.startUpdates(from: Date(), withHandler: { (pedometerData, error) in
                if let pedData = pedometerData{
                    self.numberOfSteps = Int(pedData.numberOfSteps)
                    //self.stepsLabel.text = "Steps:\(pedData.numberOfSteps)"
                    if let distance = pedData.distance{
                        self.distance = Double(distance)
                    }
                    if let averageActivePace = pedData.averageActivePace {
                        self.averagePace = Double(averageActivePace)
                    }
                    if let currentPace = pedData.currentPace {
                        self.pace = Double(currentPace)
                    }
                } else {
                    self.numberOfSteps = nil
                }
            })

Each pedometer property, if a number, is converted from NSNumber! to a Double for use in the classes, like we did for the integer numberOfSteps.

In the displayPedometerData function, change it to this to include the other properties:

 func displayPedometerData(){
        timeElapsed += 1.0
        statusTitle.text = "On: " + timeIntervalFormat(interval: timeElapsed)
        //Number of steps
        if let numberOfSteps = self.numberOfSteps{
            stepsLabel.text = String(format:"Steps: %i",numberOfSteps)
        }
        
        //distance
        if let distance = self.distance{
            distanceLabel.text = String(format:"Distance: %02.02f meters,\n %02.02f mi",distance,miles(meters: distance))
        } else {
            distanceLabel.text = "Distance: N/A"
        }
        
        //average pace
        if let averagePace = self.averagePace{
            avgPaceLabel.text = paceString(title: "Avg Pace", pace: averagePace)
        } else {
            avgPaceLabel.text =  paceString(title: "Avg Comp Pace", pace: computedAvgPace())
        } 
        
        //pace
        if let pace = self.pace {
            paceLabel.text = paceString(title: "Pace:", pace: pace)
        } else {
            paceLabel.text =  paceString(title: "Avg Comp Pace", pace: computedAvgPace())
        }
    }

For each of these properties we optionally chain the property. A nil vaule shows there was no reading for some reason. For the two pace strings, if we do not get a pace, I calculate the pace from the distance. I use the paceString function defined in the conversion functions to make a string of both meters per second and minutes per mile.

Run again, and start the pedometer. Make the running motion and the device will begin to display data.

6bbd7e99-1d47-4fb3-a8f1-cd886ebfc705

The Main Points for Core Motion

Core motion has a lot more to it, but this introduction give you some of the basics all core motion methods use. THis one is high level, the lower level, closer to the sensors require more tweaking than this virtually automatic method. However there are several points you should remember with Core Motion:

  • Add the entry to the info.plist for security permissions
  • Include the Core Motion LIbrary
  • Check for availability of the device and the functions by looking for nil on properties.
  • Don’t directly update outlets from a core motion closure
  • Do indirectly update outlets from a timer loop.

The Whole Code

You’ll find all but the info.plist entry in this code. if you run this, make sure to include that. There is a download file  coremotionpedometer with the completed project, including some app icons if you want to test off of Xcode. Go ahead, run a mile. It’s good for you.

ViewController.swift

//
//  ViewController.swift
//  CoreMotionPedometer
//
//  Created by Steven Lipton on 2/10/17.
//  Copyright © 2017 Steven Lipton. All rights reserved.
//

import UIKit
import CoreMotion

class ViewController: UIViewController {

//MARK: - Properties and Constants
    let stopColor = UIColor(red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0)
    let startColor = UIColor(red: 0.0, green: 0.75, blue: 0.0, alpha: 1.0)
    // values for the pedometer data
    var numberOfSteps:Int! = nil
    /*{ //this does not work. for demo purposes only.
        didSet{
            stepsLabel.text = "Steps:\(numberOfSteps)"
        }
    }*/
    var distance:Double! = nil
    var averagePace:Double! = nil
    var pace:Double! = nil
    
    //the pedometer
    var pedometer = CMPedometer()
    
    // timers
    var timer = Timer()
    var timerInterval = 1.0
    var timeElapsed:TimeInterval = 1.0
    

    
//MARK: - Outlets
    
    @IBOutlet weak var statusTitle: UILabel!
    @IBOutlet weak var stepsLabel: UILabel!
    @IBOutlet weak var avgPaceLabel: UILabel!
    @IBOutlet weak var paceLabel: UILabel!
    @IBOutlet weak var distanceLabel: UILabel!
    
    @IBAction func startStopButton(_ sender: UIButton) {
        if sender.titleLabel?.text == "Start"{
            //Start the pedometer
            pedometer = CMPedometer()
            startTimer()
            pedometer.startUpdates(from: Date(), withHandler: { (pedometerData, error) in
                if let pedData = pedometerData{
                    self.numberOfSteps = Int(pedData.numberOfSteps)
                    //self.stepsLabel.text = "Steps:\(pedData.numberOfSteps)"
                    if let distance = pedData.distance{
                        self.distance = Double(distance)
                    }
                    if let averageActivePace = pedData.averageActivePace {
                        self.averagePace = Double(averageActivePace)
                    }
                    if let currentPace = pedData.currentPace {
                        self.pace = Double(currentPace)
                    }
                } else {
                    self.numberOfSteps = nil
                }
            })
            //Toggle the UI to on state
            statusTitle.text = "Pedometer On"
            sender.setTitle("Stop", for: .normal)
            sender.backgroundColor = stopColor
        } else {
            //Stop the pedometer
            pedometer.stopUpdates()
            stopTimer()
            //Toggle the UI to off state
            statusTitle.text = "Pedometer Off: " + timeIntervalFormat(interval: timeElapsed)
            sender.backgroundColor = startColor
            sender.setTitle("Start", for: .normal)
        }
    }
    //MARK: - timer functions
    func startTimer(){
        if timer.isValid { timer.invalidate() }
        timer = Timer.scheduledTimer(timeInterval: timerInterval,target: self,selector: #selector(timerAction(timer:)) ,userInfo: nil,repeats: true)
    }
    
    func stopTimer(){
        timer.invalidate()
        displayPedometerData()
    }
    
    func timerAction(timer:Timer){
        displayPedometerData()
    }
    // display the updated data
    func displayPedometerData(){
        timeElapsed += 1.0
        statusTitle.text = "On: " + timeIntervalFormat(interval: timeElapsed)
        //Number of steps
        if let numberOfSteps = self.numberOfSteps{
            stepsLabel.text = String(format:"Steps: %i",numberOfSteps)
        }
        
        //distance
        if let distance = self.distance{
            distanceLabel.text = String(format:"Distance: %02.02f meters,\n %02.02f mi",distance,miles(meters: distance))
        } else {
            distanceLabel.text = "Distance: N/A"
        }
        
        //average pace
        if let averagePace = self.averagePace{
            avgPaceLabel.text = paceString(title: "Avg Pace", pace: averagePace)
        } else {
            avgPaceLabel.text =  paceString(title: "Avg Comp Pace", pace: computedAvgPace())
        }
        
        //pace
        if let pace = self.pace {
            print(pace)
            paceLabel.text = paceString(title: "Pace:", pace: pace)
        } else {
            paceLabel.text = "Pace: N/A "
            paceLabel.text =  paceString(title: "Avg Comp Pace", pace: computedAvgPace())
        }
    }
    
    //MARK: - Display and time format functions
    
    // convert seconds to hh:mm:ss as a string
    func timeIntervalFormat(interval:TimeInterval)-> String{
        var seconds = Int(interval + 0.5) //round up seconds
        let hours = seconds / 3600
        let minutes = (seconds / 60) % 60
        seconds = seconds % 60
        return String(format:"%02i:%02i:%02i",hours,minutes,seconds)
    }
    // convert a pace in meters per second to a string with
    // the metric m/s and the Imperial minutes per mile
    func paceString(title:String,pace:Double) -> String{
        var minPerMile = 0.0
        let factor = 26.8224 //conversion factor
        if pace != 0 {
            minPerMile = factor / pace
        }
        let minutes = Int(minPerMile)
        let seconds = Int(minPerMile * 60) % 60
        return String(format: "%@: %02.2f m/s \n\t\t %02i:%02i min/mi",title,pace,minutes,seconds)
    }
    
    func computedAvgPace()-> Double {
        if let distance = self.distance{
            pace = distance / timeElapsed
            return pace
        } else {
            return 0.0
        }
    }
    
    func miles(meters:Double)-> Double{
        let mile = 0.000621371192
        return meters * mile
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


}


11 responses to “Introducing Core Motion: Make A Pedometer”

  1. Thank you for this tutorial. Just what I needed. I have a feasibility question. I’d like to build an app to analyze gait over time. It would need to check frequently to see if the user was walking, and if so, spin up the sensors and record for a few seconds. It would do some math and go back to sleep. Could I do this at all, and without annoying Apple?

    Thanks again.

    Dan We are all drinking from wells that we did not dig.

    >

  2. Hello nice tutorial but facing a problem In my case im getting live pedometer step count but it is not giving me update on each step count . It is giving updates abruptly and startUpdates handler is called after few steps taken. Like at start 0 steps taken then if user start waling then after 5 or 6 steps at once it returns 6 count. I want to get update at each step. Can you help me please?

    1. Pedometer is a higher level object on Core motion. I’m not sure you can get that level of accuracy with it – it sets its own refresh rates and you get updates through a closure, so its on its own schedule, not one you can control. You may have to dive deeper into the framework, But I’m not yet familiar enough with it yet to be much help.

  3. this is the best tutorial I” have ever seen. I ‘ m very appreciate it you have a gift for teaching people more easy understanding. And the tutorial have many detailed.I hope you can provide more tutorial in your spare time.

    1. Thank You. I have a feeling more on this topic will be in the future. I had fun with it.

      1. – changing property magnificationFilter in transform-only layer, will have no effect

        I try to run your project on my ipad Air but it didn’t work on the step count. Did i miss something .

      2. userInteractionEnabled = NO;

      3. I found the answer. Core motion only work on iphon5s and Ipad air2 device or more.

  4. Hello, Good tutorial ¡¡ unfortunately it does not count steps in my iphone 5s, error shown in log is: CoreMotionPedometer[937:462047] [App] if we’re in the real pre-commit handler we can’t actually add any new fences due to CA restriction, can you give any insight? Thanks ¡¡

    1. The error is an internal error/bug you can ignore — it’s Apple’s problem. Most likely your timer is not updating. Check your timer code. If you try to send the pedometer directly to a view it probably won’t work until you stop the pedometer, and then It’ll flood info to to you.

      If you have Lynda.com or LinkedIn Learning, you can check out a more detailed version of this here: http://bit.ly/iosCoreMotion where I do a whole chapter in the video course on this.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: