0

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

Community
  • 1
  • 1
Steve
  • 13
  • 5
  • You didn't mention the `UIImagePickerController` `imagePicker`. It looks to me that you are presenting `imagePicker` before adding your button, so that might have something to do with it. – tktsubota Jan 10 '16 at 16:53
  • Thanks TroyT - do you mean I should bring the imagePicker definition (currently at top of code) into the useCamera function, but define the button first? – Steve Jan 10 '16 at 17:04
  • The problem might not be the image view, it might be the image picker. Try creating a new simple app with just an image view and a button inside of it, and see if the button can be tapped. – tktsubota Jan 10 '16 at 17:19
  • I created a simple app (no imagePicker) which worked fine - the button works. I've edited code above and all works when the imagePicker section is commented (so no camera view). Clearly the imagePicker is sitting over the top of the button preventing it working. What do I need to do to get the button and imagePicker with camera to work together?? Many thanks ... – Steve Jan 11 '16 at 01:59
  • So the button is there correct? Try using the view debugger (the icon with 3 rectangles on the console bar area). I also noticed that you removed the `bringSubviewToFront` call, so try using that again (though, subviews are supposed to be ordered in the order you added them, but it's worth a shot). – tktsubota Jan 11 '16 at 02:33
  • Thanks TroyT, yes the button is there OK. The view debugger (cool!) shows button as the outer-most panel - yet it still won't work. I added the bringSubviewToFront back. The code above is as I'm running it (with button not showing press animation). Hmm – Steve Jan 12 '16 at 00:34
  • Wow, this seems to be a difficult problem. I found [this SO post](http://stackoverflow.com/questions/27498203/uibutton-inside-uiimagepickercontroller-cameraoverlayview-not-responding), but they only used a hack without any real solution. – tktsubota Jan 12 '16 at 05:38
  • After some research, I think I found the solution. See my answer below. – tktsubota Jan 12 '16 at 05:53

3 Answers3

1

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.

Community
  • 1
  • 1
Steve
  • 13
  • 5
0

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

Fr4nc3sc0NL
  • 555
  • 1
  • 6
  • 22
  • The button is created programmatically, not with a storyboard, so `@IBAction` is not needed. – tktsubota Jan 10 '16 at 17:16
  • Many thanks! But - this didn't fix it. In any case, isn't the '@IBAction' for linking to the storyboard? This is a programatically placed button. – Steve Jan 10 '16 at 17:16
  • ah right okay.. Are you able to press the button and nothing happens? (do you see the press animation) Or aren't you able to press the button at all? – Fr4nc3sc0NL Jan 10 '16 at 17:18
  • With the code above I don't see any press animation - perhaps it isn't registering the 'click' at all. I added a breakpoint to try and see what happened, and the 'buttonAction' function never gets called.... – Steve Jan 10 '16 at 17:21
  • yea okay, so the problem is that your button is really 'behind' something not that the function isn't called on press. (there never is a press on the button). I thought the problem was that the function was never called – Fr4nc3sc0NL Jan 10 '16 at 17:23
  • Right - sorry, yes that must be the case. TroyT has suggested above to 'Try creating a new simple app with just an image view and a button inside of it, and see if the button can be tapped' - I'll do that – Steve Jan 10 '16 at 17:26
0

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.

Community
  • 1
  • 1
tktsubota
  • 9,371
  • 3
  • 32
  • 40
  • Thanks @TroyT - well I tried that, the code above is as I ran it, and still the button is not connecting. 'userInteractionEnabled' is now applied both to the button and overlayView. I see the other SO posts you've noted and the workarounds - but there must be a simple solution. – Steve Jan 13 '16 at 01:27
  • @Steve Hmm...okay. Yeah, there must be a simple solution. I'll keep looking around to see what I can find. – tktsubota Jan 13 '16 at 01:29
  • I do appreciate your help! May I ask if you have tried running this? I don't know if SO allows me to share the project to save all the setup...? – Steve Jan 13 '16 at 01:35