I created a UIBarButtonItem with a custom view using the following code (I specify the size of the profileImageButton frame to have dimensions 40x40).
let profileImageButton = UIButton(type: .custom)
profileImageButton.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
profileImageButton.setImage(imageCache.getProfileImage(), for: .normal)
profileImageButton.layer.cornerRadius = 20
profileImageButton.layer.masksToBounds = true
profileImageButton.addTarget(self, action: #selector(self.openSettings), for: .touchUpInside)
let settingsButton = UIBarButtonItem(customView: profileImageButton)
On most devices, the view has a 40x40 frame. However, when I run it on the iPhone 6s, it looks a bit rectangular. So I printed out its frame in the debug hierarchy feature of Xcode. The result I get is: frame = (0 0; 50 44), which I assume means that the frame has dimensions 50x44. Therefore, my question is why doesn't the iPhone 6s keep the specified 40x40 size?
In iOS 11, the size of a UIBarButtonItem with a custom view is determined by the internal constraints of the custom view. You have not provided any internal constraints, so all bets are off; you have not put yourself in charge of the bar button item's size. To do so, give profileImageButton a width constraint and a height constraint.
Adding onto matt's answer, I needed to also add these three lines of code for use in iOS 11:
profileImageButton.translatesAutoresizingMaskIntoConstraints = false
profileImageButton.heightAnchor.constraint(equalToConstant: 40).isActive = true
profileImageButton.widthAnchor.constraint(equalToConstant: 40).isActive = true
I have been trying to make the UIButton in the cells a perfect circle. Unfortunately the circle has been formed based on the background image rather than the UIButton frame.
The code I have for creating a circle:
cell.StoryViewButton.setImage(image, forState: .Normal)
cell.StoryViewButton.frame = CGRectMake(50, 8, 100, 100)
cell.StoryViewButton.layer.masksToBounds = false
cell.StoryViewButton.layer.cornerRadius = cell.StoryViewButton.frame.width/2
cell.StoryViewButton.clipsToBounds = true
The output looks like this:
What can I do to get the perfect circle button frames that I want?
Try something like this
cell.StoryViewButton.layer.masksToBounds = true
cell.StoryViewButton.layer.cornerRadius = cell.StoryViewButton.frame.width/2
If you need to create a circle of view you have to set masksToBounds to true, do not set clipsToBounds
Hope this will help you.
Swift 4
You could apply the following function to any view, including buttons.
func makeViewCircular(view: UIView) {
view.layer.cornerRadius = view.bounds.size.width / 2.0
view.clipsToBounds = true
}
Great.
That didn't work for me at first as I applied it in the viewDidLoad, though. At this point, constraints are still playing with the size of your buttons and the corner radius is applied to a button it thinks is twice the size, resulting in the odd shapes you have. To apply the right values to the right measurements, place the code in override func viewDidLayoutSubviews().
I know this may be a more specific case within my personal process, yet I'm sure it'll help somebody.
There is the working code.I test it. Hope it helps.
I am not sure why but I check that by changing height and width of the frame in here "CGRectMake(50, 8, 120, 120)" that the height and width of the frame and height and width of a button should be same to get the perfect circle.
cell.StoryViewButton.setImage(image, forState: .Normal)
cell.StoryViewButton.frame = CGRectMake(50, 8, 120, 120)
cell.StoryViewButton.layer.cornerRadius = self.btn.frame.width/2;
cell.StoryViewButton.layer.masksToBounds = true
Hope it helps.
I've been trying to find a solution to this problem for the past two days and still nothing I have tried works for me. Some solutions I have tried either stretch or completely mess up my custom image but nothing removes the right padding. Here is my code below as well as the result. Notice how the right bar button has a bigger space than the left system one.
var saveButton: UIButton = UIButton(frame: CGRectMake(0, 0, 32, 32))
saveButton.addTarget(self, action: "saveAction:", forControlEvents: .TouchUpInside)
let img = UIImage(named: "save")
saveButton.setBackgroundImage(img, forState: .Normal)
var saveBarButton: UIBarButtonItem = UIBarButtonItem(customView: saveButton)
self.navigationItem.setRightBarButtonItem(saveBarButton, animated: false)
Alright guys, I've found a solution that works pretty well for me. Instead of creating and setting the bar button item in code I added one in storyboard and set it's left margin in size inspector to -6 (or whatever value you like) and it's right margin to 6 (again whatever value you prefer note that the two values must be the same values but one is positive and the other is negative). Then I set it's image to the image I desired in the attributes inspector. If for some reason you want to change the image in code you can do so like this:
barButtonOutlet.image = UIIMage(named: "yourImage") as UIImage?
Hope this helps some of you out.
This is my solution. Try..
rightBtn.imageInsets = UIEdgeInsets(top: 0, left: -13.0, bottom: 0, right: 13.0)
self.navigationItem.rightBarButtonItem = rightBtn
It does not seem like UIButton instrinsicSize and/or sizeToFit takes a title left edge inset into account, or something is messed up with my expectations.
To demonstrate, I have two Custom type buttons in a view, both with the title "Button". I want to add an image to the button to the left of a title.
var image = UIImage(named: "circledPlay")
image = image?.imageWithRenderingMode(UIImageRenderingMode.AlwaysTemplate)
self.button1.setImage(image, forState: UIControlState.Normal)
self.button1.invalidateIntrinsicContentSize()
self.button1.sizeToFit()
self.button2.setImage(image, forState: UIControlState.Normal)
self.button2.titleEdgeInsets = UIEdgeInsetsMake(0, 10, 0, 0)
self.button2.invalidateIntrinsicContentSize()
self.button2.sizeToFit()
The result is as follows:
Note the second button is being truncated.
So my question would be if anybody has seen this before (and hopefully has a solution) or am I confused and this is behaving as expected (and hopefully knows the right way to do this)?
As it says in the documentation, for titleEdgeInsets: "The button does not use this property to determine intrinsicContentSize and sizeThatFits:".
So, setting the titleEdgeInsets just moves the title label, but doesn't affect the size of the button. If you want the button to have more padding around the content, set the contentEdgeInsets as well. I don't think you need to call either sizeToFit, or invalidateIntrinsicContentSize (but 'm not sure about that).
You can use the contentEdgeInsets and titleEdgeInsets to implement that.
button2.setImage(image, for: .normal)
button2.backgroundColor = UIColor(white: 0.9, alpha: 1.0)
button2.contentEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 10)
button2.titleEdgeInsets = UIEdgeInsetsMake(0, 10, 0, -10)
button2.setTitle("Button", for: .normal)
button2.sizeToFit()
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.