UIButton in Swift is not registering touches - ios

I'm trying to create a UIButton using Swift. It compiles fine and I can see my button in the simulator, but when I click it, nothing happens. This is the code I am using:
let settings = UIButton()
settings.addTarget(self, action: "touchedSet:", forControlEvents: UIControlEvents.TouchUpInside)
settings.setTitle("Settings", forState: .Normal)
settings.frame = CGRectMake(0, 530, 150, 50)
scrollView.addSubview(settings)
In the same class, here is the function 'touchedSet':
func touchedSet(sender: UIButton!) {
println("You tapped the button")
}
I'm using the simulator as I don't have an iOS 8.0 device, could that be the problem?
Thanks!

I see it is an old question but I just faced very similar problem yesterday. My buttons were highlighted on touch but not firing the action.
I have two view controllers. One view covers the others view and button is on the top view.
eg.
Rootviewcontroller has back view
Topviewcontroller has top view
The button on top view does not call the action if I don't add the topviewcontroler as childviewcontroller of the rootviewcontroller. After adding the topviewcontroller as childviewcontroller it started to working.
So in short: just try to add the view controller of buttons superview as childviewcontroller to the parent views viewcontroller with the following method
func addChildViewController(_ childController: UIViewController)

Selectors are a struct that have to be created.
Do it this way...
Updated for Swift 4
settingsButton.addTarget(self, action: #selector(showSettings), for: .touchUpInside)
That should do it.

For anyone else doing manual view layout running into this issue, you might have defined your subview like such:
let settingsButton: UIButton = {
let button = UIButton(type: .system)
// do some more setup
button.addTarget(self, selector: #selector(openSettings), for: .touchUpInside)
return button
}()
The error lies with the fact that you are adding a target and passing self before it is actually available.
This can be fixed in two ways
Making the variable lazy and still adding the target in the initialization block:
lazy var button: UIButton = {
let button = UIButton(type: .system)
// since this variable is lazy, we are guaranteed that self is available
button.addTarget(self, selector: #selector(openSettings), for: .touchUpInside)
return button
}()
Adding the target after your parent view has been initialized:
init() {
self.settingsButton = .init(type: .system)
super.init(frame: .zero)
self.settingsButton.addTarget(self, selector: #selector(openSettings), for: .touchUpInside)
}

In the simulator, sometimes it doesn't recognise the selector. There is a bug it seems. I just changed the action name (selector), and it worked.
let buttonPuzzle:UIButton = UIButton(frame: CGRectMake(100, 400, 100, 50))
buttonPuzzle.backgroundColor = UIColor.greenColor()
buttonPuzzle.setTitle("Puzzle", forState: UIControlState.Normal)
buttonPuzzle.addTarget(self, action: "buttonAction:", forControlEvents: UIControlEvents.TouchUpInside)
buttonPuzzle.tag = 22;
self.view.addSubview(buttonPuzzle)
Selector Function is Here:
func buttonAction(sender:UIButton!)
{
var btnsendtag:UIButton = sender
if btnsendtag.tag == 22 {
//println("Button tapped tag 22")
}
}

I have had this problem when parent view of my button has isUserInteractionEnabled == false. All subviews will have the same userInteraction as their parent view. So I have just added this line parentView.isUserInteractionEnabled = true and the problem disappeared. Hope this helps someone.

For those who are also stuck in the tutorial:
I ran in to the same problem, when I was following Apple's "Start Developing iOS Apps (Swift)". It turned out that I had overlooked these code lines in the tutorial:
override var intrinsicContentSize : CGSize {
return CGSize(width: 240, height: 44)
}
Adding them to my RatingControl fixed the problem.

I had a similar problem when i had a subViewController with buttons and tapHandlers to match, and I wanted to place this to a stack in a separate superViewController..
This cause none of the tapHandlers to to trigger, and the solution was to instead of only using addArrangedSubview(subViewController.view), I also used addChildViewController(subViewController) in the superview to allow the childViewController to continue to operate as a viewController and not just a view.

So you should use following line
settings.userInteractionEnabled = true

Old question, but thought I'd give some help to other looking for this issue. First off, I'm not entire sure this is correct so please correct me if I'm wrong.
But if you set the selector wrongly the application would crash when you press the button since it's subscribed to a selector which doesn't exist. Which means that your selector is working. My guess is that something else is blocking the touch event, assuming UIButton.enabled = true and UIButton.userInteractionEnabled = true. Usually you could check this by long pressing the button and see if it hits. If it does then you have a UIGestureRecognizer added somewhere which takes up the hits first. Could also be the UIScrollView which is delaying the touch input (there's a checkbox for this in the Inspector).
Hope this helps someone, and hope it's accurate.

I had the same issue when implementing UIButton and SearchBar on container view(UIView).
In my case, the cause was the constraint issue. Constraint to searchBar had issue and because of this, function set had never been called.
You can see if there's any from "Debug View Hierarchy" button. (constraint problem is shown as purple warning)
(env: iOS12, xcode10.1)

Interestingly enough, I just ran into the same issue on the very latest versions of iOS/Xcode (v12.4 / v10.3). Turns out the issue for me was a LayoutConstraint! No joke. I had a leading label with the uiSwitch to the right, and found that I needed to change the constraints between the two such that it wasn't a fixed constant value of 8 points (Label-8-Switch-0-|).
As soon as I changed this to a ">=" the Switch was able to change states between on/off. Laughably, it's almost like it wasn't able to change because it needs "room" (that apparently varies) to make the change with.
Not sure, but file it away as one of those "hummmm?" items in your memory.
One other item (that really shouldn't matter, frankly) is the valueChanged and tappedUpInside were both not firing (true on both an actual handset and on the simulators). Also, they were hooked up through a storyboard. But, this shouldn't matter as far as I know.

I had the same issue. The problem was the view had top constraint, but not left/right and height constraints. So, the view was shrinking to 1x1 and it was not passing the touch event to children.
By adding more constraints, the children now getting the touch event and working ...
let guide = view.safeAreaLayoutGuide
viewHeader.topAnchor.constraint(equalTo: guide.topAnchor).isActive = true
viewHeader.rightAnchor.constraint(equalTo: guide.rightAnchor).isActive = true
viewHeader.leftAnchor.constraint(equalTo: guide.leftAnchor).isActive = true
let heightConstraint = NSLayoutConstraint(item: viewHeader,
attribute: NSLayoutConstraint.Attribute.height,
relatedBy: NSLayoutConstraint.Relation.equal,
toItem: nil,
attribute: NSLayoutConstraint.Attribute.notAnAttribute,
multiplier: 1,
constant: 44)
viewHeader.addConstraints([heightConstraint])

Old question, but I also found myself stuck with an unresponding button. I simply changed my initialisation from
let button = UIButton(frame: .zero)
to
let button = UIButton(type: .system)

I can see only one problem: You have to set the action with Selector as following:
settings.addTarget(self, action: Selector("touchedSet:"), forControlEvents: UIControlEvents.TouchUpInside)
Don't forget to insert : if you need to pass parameters to the function.

You said within the same class. Make sure that the button code itself is in the viewDidLoad() and the action be outside of it but inside the class if you are working with a view.

The problem with that tutorial from Apple is the way they put the UIView (RatingControl) inside the StackView, which is incorrect. The UIView (RatingControl) should be outside of the StackView. So, the solution is:
1. Drag the UIView (RatingControl) outside of the StackView
2. Set the constraints for the new position of the RatingControl
- Leading margin to the Container equal zero
- Trailing margin to the Container equal zero
- Top margin to the StackView equal zero
With this new constraints, now the RatingControl will have the same with as the Container, no matter what is the device screen size, and always will be just behind of the StackView where the photo selector will appear.
I hope this help you guys.

Make sure you link you button with your IBOutlet instance variable in your code if you're using storyboard.

I've found similar example for Swift 2 and adapted for Swift 3. Tested it is working very well. I hope it helps.
// http://lab.dejaworks.com/ios-swift-3-playground-uibutton-action
// Trevor D. Beydag
import UIKit
import PlaygroundSupport
class Responder : NSObject {
func action() {
print("Button pressed!")
}
}
let containerView = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 300.0, height: 600.0))
PlaygroundPage.current.liveView = containerView
let responder = Responder()
let button = UIButton(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
button.backgroundColor = UIColor.green
button.setTitle("TEST", for: .normal)
button.addTarget(responder, action: #selector(Responder.action), for: .touchUpInside)
containerView.addSubview(button)

I had changed the height and width constraints to 0, for some reason I needed to do this so it showed correctly on a multipurpose view for one use case, the buttons were visible but the touch stopped working.

In my case, I had a transparent UIView above the UIButton in the view hierarchy, which is why when I was "clicking on the UIButton", I was actually clicking on the transparent UIView on top of it.
Sending the transparent UIView back using parentUIView.sendSubviewToBack(transparentUIView) worked for me, because after doing this the UIButton came on top of the view hierarchy. Click on the "Debug View Hierarchy" button in Xcode to check if the UIButton is on top or not.

If you're using Autolayout to build your UI, make sure to declare all your constraints in your view and parent views.
Some times we forget to set all of them and the system complains about it, drawing your UI but not responding correctly in your touch events.
In my case, i’ve forgot to set my bottom constraint in my view so i’m here to set this comment as a reminder.

When a view like a button is placed next to another view like an image view etc. especially if if the image view is on top of another view Xcode sometimes thinks that your button is underneath that image view even though it is not. Sometimes you need to clear all the constraints and reset all constraints in order for the button to work.

Related

UITextField is not editable, has only Touch Events, no Editing Events

I have a UITextField above a Google Maps View (GMSView). When I tap on the Textfield it does not become the first responder, neither do any of the delegate or action events work (Delegate Event like textFieldShouldBeginEditing and Action Events like .allEditingEvents).
The only way the Textfield is becoming the first responder is by hardcoding it in with
tf.becomeFirstResponder()
But when I tap on it nothing happens.
The Action Touch events like .touchUpInside etc. do work normally on the TextField, so I know the frame is ok.
I am creating and adding the TextView like this:
tf = UITextField(frame: CGRect(x: 100, y: 200, width: 300, height: 50))
tf.backgroundColor = .white
tf.placeholder = "sdfdsf"
tf.translatesAutoresizingMaskIntoConstraints = false
tf.addTarget(self, action: #selector(self.tapped), for: .touchUpInside)
self.mapView.addSubview(tf)
tf.centerXAnchor.constraint(equalTo: mapView.centerXAnchor).isActive = true
tf.centerYAnchor.constraint(equalTo: mapView.centerYAnchor).isActive = true
tf.widthAnchor.constraint(equalToConstant: 300).isActive = true
tf.heightAnchor.constraint(equalToConstant: 50).isActive = true
If you have any idea why this happens (or in this case does not happen) please leave a comment or an answer. I have worked on this for over 2 days straight now.
UPDATE:
I actually added the textfield to the Mapview itself. The Mapview
kinda does something strange with the layering of the view and messes
up the view of the TextView.
The solution was to add the TextView to the main view and not to the
MapView subview.
So it's (from back to front layer):
ViewController
(Main) View
Google Maps view
View
TextField
This works
Please try this:
self.mapView.bringSubviewToFront(tf) // Add this line below "self.mapView.addSubview(tf)"
This will ensure that the tf(TextField is on Top and Tappable)
Did you assigne delegate to text field like :-
tf. delegate = self
If you want to bring a subview to the front, you can use:
self.view.bringSubviewToFront(tf)

Why do my objects get placed and sized so perfectly when I add them to a UIKitView?

So I have the following UIKit elements I am adding to my UIViewController to control the simulation that appears. I expected to have to write a lot of placement code but instead everything appears perfectly no-mater what device... my question is why?
let menuButton = UIButton()
let statusLabel = UILabel()
let segmentedLabel = UISegmentedControl(items: ["None", "Glow", "Cloud"])
func initializeUI() {
//The menu button that opens up the options for the simulations
menuButton.layer.borderColor = UIColor.lightGray.cgColor
menuButton.layer.borderWidth = 1
menuButton.layer.cornerRadius = 5
menuButton.layer.backgroundColor = UIColor.darkGray.cgColor
menuButton.showsTouchWhenHighlighted = true
menuButton.imageView?.contentMode = UIViewContentMode.scaleAspectFit
menuButton.setImage(UIImage(named: "hamburger.png"), for: UIControlState.normal)
menuButton.addTarget(self, action: #selector(menuPress), for: UIControlEvents.touchDown)
view.addSubview(menuButton)
//Will display the status of the simulation
statusLabel.text = "Particle Simulation"
statusLabel.textColor = UIColor.darkGray
view.addSubview(statusLabel)
//Will display visual options
view.addSubview(segmentedLabel)
}
The size of each of the elements is perfect and they dont overlap. The label is in the bottom left corner the button is in the bottom right and the segmented view is in the top left corner (of my landscape app).
An additional question I have is if I wanted to start placing these objects programmatically how would I do so? The elements don't have an attribute position that I can work with and if I do something like menuButton.frame.size.height *= 20 that does not make the menu button super tall.
The reason this works is that all of your UI items in your example have an “intrinsic” content size so they are able to create their own frame. Add a standard UIView the same way and you will be out of luck. Also the views are creating their own constraints based on initial frames using Auto Resizing Masks(Also known as Springs and Struts).
Finally to place items you would set the frame directly. Just math. Good luck.
Super important to understand intrinsic content size. Some frame changes might require you to turn off automatic constraints but this is usually not the case but could be the problem with your button
UIView.translatesAutoresizingMaskIntoConstraints = false

How to programmaticaly add button in iOS game app swift 3?

Can someone explain the code for a programatically added button in an iOS game application for swift 3 Xcode 8? All the other threads on this topic we're in single view and didn't work for me. I couldn't figure out how to add buttons to the game app Main.storyboard, so I'm trying to make a programattically added button. This is the code I'm trying to use now but doesn't work:
var playAgain = UIButton()
playAgain.setTitle("Play Again", for: .normal)
playAgain.setTitleColor(UIColor.black, for: .normal)
playAgain.backgroundColor = SKColor.green
playAgain.layer.borderWidth = 2
playAgain.layer.cornerRadius = 18
playAgain.frame = CGRect(x: (self.frame.width)/2, y: (self.frame.height)/2, width: 100, height: 36)
self.view?.addSubview(playAgain)
Why would the buttons in single view be different in game apps? Also, when(and if) this is created, how would I modify the Touches ended method to know when the button was touched?
Your code is adding the button to the .view, but using the coordinate system of the SKScene. So, your button is there, just not in view.
Assuming you want the button to be centered on the screen (at least, for now), change the placement to:
playAgain.frame = CGRect(x: 0, y: 0, width: 100, height: 36)
playAgain.center = (self.view?.center)!
self.view?.addSubview(playAgain)
This will put the button above the game scene (z-layer, that is), so you can use normal button tap without needing to deal with touches. So, right after you add the button:
self.view?.addSubview(playAgain)
playAgain.addTarget(self, action: #selector(playAgainTapped(_:)), for: .touchUpInside)
and then elsewhere in your class:
func playAgainTapped(_ sender: Any?) -> Void {
print("Play again was Tapped!")
// take whatever action you want here
}
There is another way to create a button programmatically. You can create an empty UIView and override touch method. Also you are able to process touch event on this view and simulate buttons action. I think this is a fastest way for you.

Error when trying to click in a view created programmatically swift 3

I created a view programmatically, like a popup coming from the top with some text and images in there!
alert = UIView(frame: CGRect(x: 0, y: 0, width: self.frame.width , height: 0))
alert?.frame.origin.y = (textLable?.frame.height)!
alert?.frame.size.height = (textLable?.frame.height)!
alert?.backgroundColor = self.arrayOptions.colorBackground
and then I'm trying to add a UITapGestureRecognizer to that view like this inside a setup func that is called in the init.
let tap = UITapGestureRecognizer(target: self, action: #selector(self.teste))
tap.numberOfTapsRequired = 1
alert?.addGestureRecognizer(tap)
im adding that view like this in a UitableViewController:
self.popView = PopupView(frame: CGRect(x: 0 , y: 0 , width:self.view.frame.width, height: 0), with: PopUpOptions.error, originY: 0,description:"blablabla")
self.view.addSubview(self.popView!)
But when I tap on the view nothing happens, but when I tap repeatedly over and over this error occurs:
<_UISystemGestureGateGestureRecognizer: 0x174186f50>: Gesture: Failed
to receive system gesture state notification before next touch
But I cant seem to find an answer for this, could anyone help me pls!
Thank you!
here is the GitHub link https://github.com/Coimbraa/AlertsPopup_framework for my framework
Took a look at your GitHub repo - Couple notes...
I see a lot of "!" in your code. Each one of those is a potential crash. For example, I had to make a number of edits just to get it to run at all (I don't have the image assets the code is expecting).
In PopUpView.swift change the init() func to this (just added the clipsToBounds line):
override public init(frame: CGRect) {
super.init(frame: frame)
setup()
self.clipsToBounds = true
}
Now, when you call popView?.toggleStatus() you probably won't see anything.
Your PopupView "container" has a height of Zero. Without clipping its contents, you see the content, but you can't interact with it.
I changed your animation block in toggleStatus() to this:
UIView.animate(withDuration: 0.5, animations: {
if let sz = self.alert?.frame.size {
self.frame.size.height = sz.height
}
self.alert?.frame.origin.y = (self.startY)! + (self.status ? 0 : -((self.textLable?.frame.height)! + self.startY))
}) { (finished:Bool) in
// ...
and I could now tap into and edit the TextView, and tap elsewhere and get print("pressed") output to the debug console.
I did not dig further, so I don't know if/where you need to put additional code to reset the frame-height back to Zero (or maybe it gets hidden or removed, whatever).
Try this:
alert.isUserInteractionEnabled = true
Set this property to true for those views you want to tap on (where you add the tap gesture recognizer).
You are adding that Custom Alert View in Table View Controller.
First try what Tung Fam has suggested, if it doesn't work then,
Trying adding it on window like:
self.view.window.addSubview(self.popView!)
Also set the frame of the window to the popupView.
As I'm seeing you are setting target for TapGesture as self in
let tap = UITapGestureRecognizer(target: self, action: #selector(self.teste))
Here self is object of UIView (your alert view).
So according to this line your tapGesture listener must be implemented in alert? class. And you need to use custom protocol or custom delegate to get the action of tapGesture in other classes.

Swift - Set label to be on top of other view items

I'm creating a basic sketching app using swift 1.2 and Xcode 6.4. The feature I'm working on is adding text to the image for future manipulation.
So far I have the text the user enters adding to the drawing via label, but it is being placed (I assume) behind the UIImage that is to be drawn upon. This is preventing the associated tap gesture from being recognised. The label is also obscured when drawn over.
How do I place the label on top of everything else to prevent it being drawn over? The end goal is to allow the user to move the label.
If I was using my usual web stack I'd set the z-index of the element to a high value via CSS.
Edit: Using view.bringSubviewToFront(label) prevents the text from being drawn over, but does not allow the tap gesture to be picked up.
Heres my code:
func printText(){
println(textString)
var label = UILabel(frame: CGRectMake(5, 100, 200, 50 ))
label.textAlignment = NSTextAlignment.Center
label.text = textString;
let tap = UITapGestureRecognizer(target: self, action: Selector("handleTap"))
label.addGestureRecognizer(tap)
self.view.addSubview(label);
}
func handleTap() {
print("tap working")
}
Any help much appreciated.
If you want z-index use:
label.layer.zPosition = 1;
Also you could play with bringToFront method on the parent view:
self.view.bringSubviewToFront(label);
You can check that function here :
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/
Also, you should add the following:
label.userInteractionEnabled = true
to enable touches.
Try using those in the parent view:
view.bringSubviewToFront(label)
view.sendSubviewToBack(imageView)
It all depends on what you can, or want to do.
You can call label to front by
self.view.bringSubviewToFront(label)
or
You can send image view to the back by
self.view.sendSubviewToBack(imageView)

Resources