Touch

This app differs from our previous ones in four ways:

  1. It has more than one UIView object. This app has a big white view and a little yellow one. Because of the call to addSubview, the big view is the superview of the little view, and the little view is the subview of the big view. See View Hierarchy.

  2. The big white view has a stored property named littleView. Our only previous view with a stored property was in the temperature exercise of Hello.

  3. The big white view has no drawRect(_:) method. More precisely, it has only the drawRect(_:) method inherited from its superclass, class UIView.

  4. The big white view is touch-sensitive, because it is a subclass of UIResponder and has the method touchesBegan(_:with:). This method is called automatically when a finger touches the screen.

Source code in Touch.zip

  1. AppDelegate.swift: unchanged.
  2. ViewController.swift: unchanged.
  3. View.swift: the big white view that occupies the whole screen. It is touch-sensitive. The call to super.init must come after we have initialized the stored property littleView. The call to addSubview(_:) must come after the call to super.init.
  4. LittleView.swift: the little yellow view that moves around.
  5. Main.storyboard. I changed the class of the view controller’s UIView to my class View.
  6. Info.plist: unchanged.

Create the project

As in Hello, create a subclass named View of class UIView, and designate this to be the class of the view created by the view controller. Also create a subclass named LittleView of class UIView.

Things to try

  1. Create a new LittleView at each point where the finger touches. In the touchesBegan(_:with:) method of the big View, change
    		littleView.center = point;	//Move the littleView to a new location.
    
    to
    		//Rectangle f is centered at the point where the user touched.
    		let f: CGRect = CGRect(x: point.x - 40, y: point.y - 20, width: 80, height: 40);
    
    		//Create a LittleView framed by f
    		//and insert it into the big white View.
    		addSubview(LittleView(frame: f));
    
    You can now remove the stored property littleView, since it is no longer used. Run the app and touch the screen in a few places. Then change it back.

  2. The LittleView jumps as soon as the touch begins because we named our method touchesBegan(_:with:). Change the name of the method to touchesEnded(_:with:) and verify that the LittleView now jumps when the touch ends. Then change it back.

  3. Make the LittleView draggable. Change the name of the method from touchesBegan(_:with:) to touchesMoved(_:with:) and verify that you can drag on LittleView. Be careful not to grab the status bar. Then change it back. (touchesMoved(_:with:) can call previousLocationInView: as well as locationInView:.)

    There is also touchesCancelled(_:with:). A parallel series of methods detects when the iPhone is moved: motionBegan(_:with:), motionEnded(_:with:), etc. See Motion Events and Remote Control Events.


  4. Let’s keep the LittleView draggable. In addition, it will become green when we start to drag it, and change back to to yellow when the drag is ended. Give class View the following three methods.
    	override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
    		littleView.backgroundColor = UIColor.greenColor();
    	}
    
    	override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
    		let touch: UITouch = touches.first!;
    		let point: CGPoint = touch.locationInView(self);
    		littleView.center = point;	//Move the littleView to a new location.
    	}
    
    	override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
    		littleView.backgroundColor = UIColor.yellowColor();
    	}
    

  5. The LittleView should be initially positioned below the status bar. The topLayoutGuide of the view controller gives us the hight of the status bar, but unfortunately this property is not yet available when the View is being created.

    Add the following method to class View. It will be called automatically when the topLayoutGuide becomes available.

    	override func layoutSubviews() {
    		let application: UIApplication = UIApplication.sharedApplication();
    		let appDelegate: AppDelegate = application.delegate as! AppDelegate;
    		let window: UIWindow = appDelegate.window!;
    		let viewController: ViewController = window.rootViewController as! ViewController;
    		if littleView.frame.origin.y < viewController.topLayoutGuide.length {
    			littleView.frame.origin.y = viewController.topLayoutGuide.length;
    		}
    	}
    

  6. We don’t need the stored property littleView. Remove it and change the call to addSubview(_:) to the following.
    		addSubview(LittleView(frame: f));
    
    Then change each use of littleView to subviews.last!. For example,
    		subviews.last!.center = point;	//Move the LittleView to a new location.