Pearls on an Elastic String

Swipe across the screen and drag the string of pearls after you.

The CADisplayLink property of the ViewController has to be lazy because its initialization uses the view property of the ViewController. The initialization of the CADisplayLink must therefore come after the initialization of the view. The keyword lazy causes this to happen, even though the initialization of the CADisplayLink is written in the same statement as its declaration. We have to say self.view here to get it to compile.

Source code in

  1. Class AppDelegate: does the expensive animation only when the app is visible on the screen.
  2. Class ViewController: creates the big black view and sets the text color of the status bar to white. The view controller contains a CADisplayLink which triggers a call to the View’s drawRect: 60 times per second.
  3. Class View creates the five Pearl objects.
  4. Class Pearl draws one circle and one line. Newton’s Second Law of Motion states that F = ma. The F and a are boldface because they are vectors (CGPoints in our language). The m is italic because it is a scalar (a CGFloat in our language).
  5. LaunchScreen.xib is in white on black.

Create the project

Select the project Pearl at the top of the Xcode Project Navigator. At the top of the center panel of Xcode, select General. Under Deployment Info, uncheck all the Device Orientations except Portrait.

The same program, in HTML5

Pull down your browser’s View menu (Develop on Safari) and select View Source or Page Source.

Things to try

  1. When the device is shaken, the dangling string of pearls should sway back and forth. You’ll have to run the app on a real device, not on the iOS Simulator.
    1. Add a CMMotionManager (a.k.a. accelerometer) object to the ViewController. At the top of ViewController.swift,
      import CoreMotion;	//needed for CMMotionManager
      Add the following property to class ViewController.
      	let motionManager: CMMotionManager = CMMotionManager();
    2. Turn the motion manager on and off in the application delegate.
    3. 		//in applicationWillResignActive(_:)
      		//in applicationDidBecomeActive
    4. At the top of Pearl.swift,
      import CoreMotion;	//needed for CMAccelerometerData
      In the pullTowards method of class Pearl, change
      		let force: CGPoint = CGPointMake(
      			(p.x - location.x) * stiffness,
      			(p.y - location.y) * stiffness + gravity * mass);
      to the following.
      		let appDelegate: AppDelegate = UIApplication.sharedApplication().delegate! as AppDelegate;
      		let viewController: ViewController = appDelegate.window!.rootViewController! as ViewController;
      		let accelerometerData: CMAccelerometerData? = viewController.motionManager.accelerometerData;
      		var a: CMAcceleration;
      		if accelerometerData == nil {
      			a = CMAcceleration(x: 0, y: 0, z: 0);
      		} else {
      			a = accelerometerData!.acceleration;
      		let force: CGPoint = CGPointMake(
      			(p.x - location.x) * stiffness + CGFloat(a.x) * gravity * mass,
      			(p.y - location.y) * stiffness - CGFloat(a.y) * gravity * mass);
    5. In the Info.plist file in the Supporting Files folder in the Xcode Project Navigator, you already have the property Required Device Capabilities. Open this property and see that it already contains the capability armv7. Add the capability accelerometer.
    6. Select the Pearl project at the top of the Xcode Project Manager. At the top of the center panel of Xcode, select General. Under Deployment Info, go to Device Orientation and uncheck all of them except Portrait.
    After loading the app onto your device, unplug the device from your Mac and hold it upright. Would the effect of an acceleration be more dramatic if you increase the value of the gravity property of class Pearl?