bitops bitops - 23 days ago 6
Swift Question

UIView subview not receiving touches

I am trying to understand why my subview is not receiving touches.

Below is a gif showing the behavior I am encountering: when I click the cyan button on the right, nothing happens (the background of the view on the left is supposed to change color). When I click the green button on the left, it correctly receives the touch event, the attached TouchDown action fires and the color of the view changes.

enter image description here

This "app" is obviously not a real app and is just meant to illustrate the behavior I'm encountering. Really what I'm after is learning how to implement the common "slide out menu" pattern that you see in many apps; usually with an accompanying hamburger button.

In many of these apps, it looks like the main application content is moved aside to reveal the menu, "hiding" underneath the app, as it were. I am trying to replicate that same behavior here.

I am pasting my ViewController code in here. The code you see below is everything I have.

import UIKit

class ViewController: UIViewController {

var originalX : CGFloat!
var originalY : CGFloat!

var containerView = UIView()

var button : UIButton!
var button2 : UIButton!

override func viewDidLoad() {
super.viewDidLoad()

originalX = self.view.frame.origin.x
originalY = self.view.frame.origin.y

button = UIButton(frame: CGRectMake(200, 25, 100, 100))
button.backgroundColor = UIColor.greenColor()
button.addTarget(self, action: "foo", forControlEvents: .TouchDown)

view.addSubview(button)

containerView.frame = CGRectMake(self.view.frame.width, originalY, 150, self.view.frame.height)
containerView.userInteractionEnabled = true
containerView.backgroundColor = UIColor.magentaColor()
containerView.clipsToBounds = false

button2 = UIButton(frame: CGRectMake(25, 25, 100, 100))
button2.backgroundColor = UIColor.cyanColor()
button2.addTarget(self, action: "bar", forControlEvents: .TouchDown)

view.addSubview(containerView)
containerView.addSubview(button2)
}

@IBAction func left() {
UIView.animateWithDuration(0.3) {
self.view.frame = CGRectMake(self.originalX - 150, self.originalY, self.view.frame.width, self.view.frame.height)
}
}

@IBAction func right() {
UIView.animateWithDuration(0.3) {
self.view.frame = CGRectMake(self.originalX, self.originalY, self.view.frame.width, self.view.frame.height)
}
}

func foo() {
UIView.animateWithDuration(1.0) {
self.view.backgroundColor = UIColor.redColor()
}
}

func bar() {
UIView.animateWithDuration(1.0) {
self.view.backgroundColor = UIColor.blueColor()
}
}

}

Answer

containerView is a subview of view, but containerView's origin is right of the right edge of its parent view (view), because of the line:

containerView.frame = CGRectMake(self.view.frame.width, originalY, 150, self.view.frame.height)

Any portion of subview that is outside of its superview's bounds won't receive touch events. Touch events will only be passed along and recognized by portions of the subview that are within the bounds of its superview This is why your cyan button won't "fire" when you tap it.

One way to solve it is instead of manipulating the view's frame, use a secondary subview (call it overlayView) to overlay and cover the containerView, and manipulate its frame. Make sure at least some portion both views are within the bounds of the main view so they may receive touch events.