Steve
Steve

Reputation: 45

UIButton not calling handler function in ImageView

In my Swift app, I want to point rear camera at object, then click a button. In my view controller, I'm trying to pick up the UIButton press for the programatically placed button, positioned over a cameraOverlayView.

I don't need to take a picture - I'm just using camera to point at object, then click button.

Compiles to iPhone. I seem to either get camera working, or button, but not both at same time. The imagePicker sits over the button and hides it. Can anyone advise how to get the button and imagePicker to work together? Thanks in advance.

import UIKit
import MobileCoreServices

class FirstViewController: UIViewController {

@IBOutlet weak var imageView: UIImageView!
let imagePicker: UIImagePickerController! = UIImagePickerController()

func noCamera() {
    let alertVC = UIAlertController(
        title: "No Camera",
        message: "Sorry, this device has no camera",
        preferredStyle: .Alert)
    let okAction = UIAlertAction(
        title: "OK",
        style:.Default,
        handler: nil)
    alertVC.addAction(okAction)
    presentViewController(
        alertVC,
        animated: true,
        completion: nil)
}

// THIS IS THE FUNCTION I'M TRYING TO CALL
func buttonAction(sender: UIButton!) {
    if sender.tag == 1 {
        print("Button tapped")
        let alertVC = UIAlertController(
            title: "Button pressed",
            message: "Button pressed",
            preferredStyle: .Alert)
        let okAction = UIAlertAction(
            title: "OK",
            style:.Default,
            handler: nil)
        alertVC.addAction(okAction)
        presentViewController(
            alertVC,
            animated: true,
            completion: nil)
    }
}

@IBAction func useCamera(sender: UIButton) { // A SEPARATE STORYBOARD BUTTON IS USED TO CALL THIS INITIALLY
    if (UIImagePickerController.isSourceTypeAvailable(.Camera)) {
        if UIImagePickerController.availableCaptureModesForCameraDevice(.Rear) != nil {
            //Create camera overlay
            let pickerFrame = CGRectMake(
                0,
                UIApplication.sharedApplication().statusBarFrame.size.height,
                imagePicker.view.bounds.width,
                imagePicker.view.bounds.height - imagePicker.navigationBar.bounds.size.height - imagePicker.toolbar.bounds.size.height)

            // Sights
            let sightDiam: CGFloat = 50 // size of sights
            let sightFrame = CGRectMake(
                pickerFrame.width/2 - sightDiam/2,
                pickerFrame.height/2 - sightDiam/2,
                sightDiam,
                sightDiam)
            UIGraphicsBeginImageContext(pickerFrame.size)
            let context = UIGraphicsGetCurrentContext()
            CGContextSaveGState(context)
            CGContextSetLineWidth(context, 2) // linewidth
            CGContextSetStrokeColorWithColor(context, UIColor.yellowColor().CGColor) // colour
            // Outer circle
            CGContextStrokeEllipseInRect(context, sightFrame)
            // Inner dot
            CGContextStrokeEllipseInRect(context, CGRectMake(sightFrame.minX + sightFrame.width/2-1,sightFrame.minY + sightFrame.height/2-1,2,2))

            // Top tick
            CGContextMoveToPoint(context, sightFrame.minX + sightFrame.width/2, sightFrame.minY + 7)
            CGContextAddLineToPoint(context, sightFrame.minX + sightFrame.width/2, sightFrame.minY - 7)
            // Bottom tick
            CGContextMoveToPoint(context, sightFrame.origin.x + sightFrame.width/2, sightFrame.minY + sightFrame.size.height+7)
            CGContextAddLineToPoint(context, sightFrame.origin.x + sightFrame.width/2, sightFrame.minY + sightFrame.size.height-7)
            // Left tick
            CGContextMoveToPoint(context, sightFrame.minX-7, sightFrame.minY + sightFrame.height/2)
            CGContextAddLineToPoint(context, sightFrame.minX+7, sightFrame.minY + sightFrame.height/2)
            // Right tick
            CGContextMoveToPoint(context, sightFrame.minX + sightFrame.width-7, sightFrame.minY + sightFrame.height/2)
            CGContextAddLineToPoint(context, sightFrame.minX + sightFrame.width+7, sightFrame.minY + sightFrame.height/2)
            // Draw
            CGContextStrokePath(context)
            CGContextRestoreGState(context)

            let overlayImage = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext();
            let overlayView = UIImageView(frame: pickerFrame)
            overlayView.image = overlayImage
            let screenSize = UIScreen.mainScreen().bounds.size
            let aspectRatio:CGFloat = 4.0/3.0
            let scale = screenSize.height/screenSize.width * aspectRatio

            imagePicker.allowsEditing = false
            imagePicker.sourceType = .Camera
            imagePicker.cameraCaptureMode = .Photo
            imagePicker.modalPresentationStyle = .FullScreen
            imagePicker.showsCameraControls = false // keep off
            imagePicker.cameraOverlayView = overlayView
            imagePicker.cameraViewTransform = CGAffineTransformMakeScale(scale, scale);
            overlayView.userInteractionEnabled = true

            // Add Button (programatically)
            let buttonBorder: CGFloat = 30 // size of button
            let buttonHeight: CGFloat = 50 // height of button
            var button: UIButton = UIButton(type: UIButtonType.System) as UIButton
            button.frame = CGRectMake(
                buttonBorder,
                screenSize.height - buttonBorder - buttonHeight,
                screenSize.width - (buttonBorder * 2),
                buttonHeight)
            button.backgroundColor = UIColor.yellowColor()
            button.setTitle("Aim at object", forState: UIControlState.Normal)
            button.tag = 1
            button.addTarget(self, action: "buttonAction:", forControlEvents: .TouchUpInside)
            button.userInteractionEnabled = true
            overlayView.addSubview(button)
            overlayView.bringSubviewToFront(button)
            button.userInteractionEnabled = true
            presentViewController(imagePicker, animated: false,
                completion: {})
            // I WANTED THE BUTTON CLICK ABOVE TO CALL 'buttonAction'
            // BUT THE BUTTON NEVER GETS ACTIVATED - WHY NOT?

        } else {
            noCamera() // no rear camera
        }
    } else {
        noCamera() // no camera
    }
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
}

override func viewDidLoad() {
    super.viewDidLoad()
} 
}

There is good advice already in post - UIButton not calling event in imageView but that didn't help me. Thanks

Upvotes: 1

Views: 198

Answers (3)

Steve
Steve

Reputation: 45

I think I've cracked it and post this if it helps others. This SO post was the clue.

The problem was the button on the cameraOverlay on the UIImagePickerController showed on-screen, but didn't register touches. As @Dia Kharrat noted in that post, 'the overlay UIView's frame was not large enough. The clipsToBounds property for a UIView is by default set to NO, which means its subviews are still visible even if they are outside the parent's frame... essentially, make sure your overlay's frame is large enough, or that your subviews of the overlay are positioned within its bounds.' I had placed button outside overlay - but it was still showing.

I added overlayView.clipsToBounds = true to test and the button disappeared on running, proving comment above. So solution is to define the initial picker frame as full screen size, adding code:

let screenSize = UIScreen.mainScreen().bounds.size // line moved to top of code
let pickerFrame = CGRectMake(0,0,screenSize.width,screenSize.height) // Full screen size

The rest of code above remains unchanged. Now buttonAction does get called. Thanks for help @TroyT and @Fr4nc3sc0NL.

Upvotes: 2

tktsubota
tktsubota

Reputation: 9391

I looked around at other SO posts for this similar case, and there are some. This post does something similar, and the answers recommend setting userInteractionEnabled on the imageView. The documentation also states that UIImageView overrides the userInteractionEnabled property to be false.

So pretty much:

imageView.userInteractionEnabled = true

You already said in the comments that the button is on the top of the views, so this is probably the only thing that's keeping the button from receiving touch events.

Upvotes: 0

Fr4nc3sc0NL
Fr4nc3sc0NL

Reputation: 575

Maybe you have to add @IBAction in front of func buttonAction(sender: UIButton!)

Upvotes: 0

Related Questions