I have the following code.
import UIKit
class ViewController: UIViewController {
var button : UIButton?
override func viewDidLoad() {
super.viewDidLoad()
button = UIButton.buttonWithType(UIButtonType.System) as UIButton?
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I get the following error
ERROR:
'AnyObject' is not convertible to 'UIButton?'
I know I might be doing something fundamentally wrong. I would like to know what that is.
According to me:
I have declared button as an Optional UIButton
- Which I think means, that the value of button can be unset or nil
Therefore,
while initialising it the type is mentioned as UIButton?
Is this it right way ?
You can't cast to an optional UIButton in the way you're doing it. The correct way to cast to an optional UIButton is:
button = UIButton.buttonWithType(UIButtonType.System) as? UIButton
Interpret this as: This cast can either return nil or an UIButton object, resulting in an optional UIButton object.
Follow the below code
var myBtn = UIButton.buttonWithType(UIButtonType.System) as UIButton
//OR
var myBtn = UIButton.buttonWithType(UIButtonType.Custom) as UIButton
//OR
var myBtn = UIButton()
myBtn.setTitle("Add Button To View Controller", forState: .Normal)
myBtn.setTitleColor(UIColor.greenColor(), forState: .Normal)
myBtn.frame = CGRectMake(30, 100, 200, 400)
myBtn.addTarget(self, action: "actionPress:", forControlEvents: .TouchUpInside)
self.view.addSubview(myBtn)
//Button Action
func actionPress(sender: UIButton!)
{
NSLog("When click the button, the button is %#", sender.tag)
}
Please try below code:
var button = UIButton(frame: CGRectMake(150, 240, 75, 30))
button.setTitle("Next", forState: UIControlState.Normal)
button.addTarget(self, action: "buttonTapAction:", forControlEvents: UIControlEvents.TouchUpInside)
button.backgroundColor = UIColor.greenColor()
self.view.addSubview(button)
This code should do the job.
button = UIButton.buttonWithType(UIButtonType.System) as! UIButton
In this case the force cast done with the ! is a safe option because the documentation does guarantee that the method returns a UIButton.
You can also create the button during the declaration of the property:
class ViewController: UIViewController {
var button = UIButton.buttonWithType(UIButtonType.System) as! UIButton
...
This way there is no need to declare the property as an optional type.
Related
I have a class where written is a function creating my button:
LoginButton.swift
func createButton() {
let myButton: UIButton = {
let button = UIButton()
button.addTarget(self, action: #selector(Foo().buttonPressed(_:)), for: .touchUpInside)
}()
}
Now in my second class, Foo.swift, I have a function that just prints a statement
Foo.swift
#objc func buttonPressed(_ sender: UIButton) {
print("button was pressed")
}
When ran I get no errors except when I try to press the button, nothing happens. Nothing prints, the UIButton doesn't react in any way. Really not sure where the error occurs because Xcode isn't printing out any type of error or warning message.
The action method is called in the target object. Thus, you have either to move buttonPressed to the class which contains createButton or to pass an instance of Foo as a target object.
But note that a button is not the owner of its targets. So, if you just write:
button.addTarget(Foo(), action: #selector(buttonPressed(_:)), for: .touchUpInside)
This will not work, because the Foo object is immediately released after that line. You must have a strong reference (e.g. a property) to Foo() like
let foo = Foo()
func createButton() {
let myButton: UIButton = {
let button = UIButton()
button.addTarget(foo, action: #selector(buttonPressed(_:)), for: .touchUpInside)
}()
}
You are missing with target. So make instant of target globally and make use of it as target for button action handler.
class ViewController: UIViewController {
let foo = Foo()
override func viewDidLoad() {
super.viewDidLoad()
createButton()
}
func createButton() {
let myButton: UIButton = {
let button = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 50))
button.backgroundColor = UIColor.red
button.setTitle("Tap me", for: .normal)
button.addTarget(self.foo, action: #selector(self.foo.buttonPressed(_:)), for: .touchUpInside)
return button
}()
myButton.center = self.view.center
self.view.addSubview(myButton)
}
}
Class Foo:
class Foo {
#objc func buttonPressed(_ sender: UIButton) {
print("button was pressed")
}
}
Just pass Selector as function argument.
func createButtonWith(selector: Selector) {
let myButton: UIButton = {
let button = UIButton()
button.addTarget(self, action: selector), for: .touchUpInside)
}()
}
And call this function like below...
createButtonWith(selector: #selector(Foo().buttonPressed(_:)))
I am trying to create dynamic radio buttons based on Firebase data. I am hoping to arrange the buttons in a vertical stack, and essentially when they are clicked I would hope for them to disappear and display another view:
class PollController: UIViewController {
#IBOutlet weak var passLabel: UILabel!
#IBOutlet weak var pollImage: UIImageView!
var ref: FIRDatabaseReference!
var pollRef: FIRDatabaseReference!
var pass = ""
var passedImageURL = ""
var posX = 0;
var posY = 0;
var buttons = [UIButton]()
override func viewDidLoad() {
super.viewDidLoad()
ref = FIRDatabase.database().reference()
pollRef = ref.child("Polls").child(pass)
passLabel.text = pass
pollImage.sd_setImage(with: URL(string: passedImageURL), placeholderImage: UIImage(named: "test"))
pollRef.observe(FIRDataEventType.value, with: {(snapshot) in
let numberOfChildren = snapshot.childSnapshot(forPath: "answers").childrenCount
self.passLabel.text = String(numberOfChildren)
print(numberOfChildren)
var stackView = UIStackView(arrangedSubviews: self.buttons)
// create button1
for x in 0..<numberOfChildren {
let button = UIButton(frame: CGRect(x: self.posX, y: self.posY, width: 60, height: 20))
button.setTitleColor(UIColor.black, for: .normal)
button.setTitle("No", for: .normal)
button.setImage(UIImage(named: "checkbox untick.png")!, for: .normal)
// if the selected button cannot be reclick again, you can use .Disabled state
button.setImage(UIImage(named: "checkboxredtick.png")!, for: .selected)
button.tag = Int(x)
button.addTarget(self, action: #selector(self.buttonAction(sender:)), for: .touchUpInside)
stackView.addSubview(button)
self.buttons.append(button)
// create other buttons and add into buttons ...
}
})
// Do any additional setup after loading the view.
}
func buttonAction(sender: UIButton!){
for button in buttons {
button.isSelected = false
}
sender.isSelected = true
// you may need to know which button to trigger some action
// let buttonIndex = buttons.indexOf(sender)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
If you are using SWIFT 3.0 or later,
Then you must define add target method like below as _: is missing in the selector parameter.
button.addTarget(self, action: #selector(buttonAction(_:)), for: .TouchUpInside)
Also check id you have declares myStackview variable here. if you declared then try to add it as :
self.myStackview
Your button declaration is func buttonAction(sender: UIButton!). Note that there are no underscore before sender. This means that you need to specify the name of the argument when you call the function. Replace the underscore in the selector with the argument name sender
button.addTarget(self, action: #selector(buttonAction(sender:)), for: .touchUpInside)
You can also add an underscore in front of the argument name in the function.
func buttonAction(_ sender: UIButton!)
Also, I noticed that the code is in Swift 3 seeing most of the syntax. So, the control event for touch up inside in Swift 3 is touchUpInside and not TouchUpInside
I'm trying to learn how to create button target actions, however, when I press the button, I get those LLDB errors and I get told that it was an 'unrecognized selector sent to class'.
Where am I going wrong here?
StatusCell.swift:
let phoneIcon: UIButton = {
let iv = UIImageView()
iv.translatesAutoresizingMaskIntoConstraints = false
iv.image = UIImage(named: "Phone3")?.imageWithRenderingMode(.AlwaysTemplate)
let phoneBtn = UIButton(type: .Custom)
phoneBtn.addTarget(CallButton.self, action: #selector(CallButton.buttonPressed(_:)), forControlEvents: .TouchDown)
phoneBtn.addTarget(CallButton.self, action: #selector(CallButton.buttonReleased(_:)), forControlEvents: .TouchUpInside)
phoneBtn.translatesAutoresizingMaskIntoConstraints = false
phoneBtn.setImage(iv.image!, forState: .Normal)
phoneBtn.tintColor = UIColor(r: 224, g: 224, b: 224)
return phoneBtn
}()
Here's the CallButton class where I call for buttonPressed and buttonReleased.
class CallButton: UIControl {
func buttonPressed(sender: AnyObject?) {
print("Pressed")
}
func buttonReleased(sender: AnyObject?) {
print("Let go")
}
}
The value for parameter target must be an instance of CallButton, not the type itself.
You are setting the class itself, not an instance, as the target of the action.
Therefore, the method you set as the action should be implemented as a class method, not an instance method:
class func buttonPressed(sender: AnyObject?) {
print("Pressed")
}
I have programmatically created a button in my main class and passing an instance of a game class (gameSCNScene - where most of the game logic lies) to the button. Inside this game class instance is where the action for the button resides however when ever I press the button I get the error - Unrecognized selector.
class GameViewController: UIViewController, SCNSceneRendererDelegate {
var gameSCNScene: GameSCNScene!
override func viewDidLoad() {
super.viewDidLoad()
let scnView = self.view as! SCNView
scnView.delegate = self
// Create my game scene instance
gameSCNScene = GameSCNScene(currentview: scnView)
// Make button
makeButtonsUI(gameSCNScene)
}
func makeButtonsUI(gameSCNScene: GameSCNScene) {
let image = UIImage(named: "art.scnassets/addBtn.png") as UIImage?
let button = UIButton(type: UIButtonType.System) as UIButton
button.frame = CGRectMake(100, 100, 100, 100)
button.setImage(image, forState: .Normal)
button.addTarget(self, action:("gameSCNScene.addCube:"), forControlEvents:.TouchUpInside)
self.view.addSubview(button)
}
Button function inside my gameSCNScene instance
func addCube(sender:UIButton) {
//Code here
}
The line of code where you add the target is incorrect. This:
button.addTarget(self, action:("gameSCNScene.addCube:"), forControlEvents:.TouchUpInside)
Should be:
button.addTarget(gameSCNScene, action:("addCube:"), forControlEvents:.TouchUpInside)
When i want to add a UIButton in a view controller's view, here are the ways:
First
let button: UIButton = UIButton()
then configure properties in viewDidLoad method.
Second
lazy var button: UIButton = {
let buttonTemp = UIButton()
buttonTemp.backgroundColor = UIColor.clearColor()
button.addTarget(self, action: "connect", forControlEvents: UIControlEvents.TouchUpInside)
self.view.addSubview(buttonTemp)
return buttonTemp
}()
Third
let button: UIButton = {
let button = UIButton(type: .Custom)
button.backgroundColor = UIColor.greenColor()
return button
}()
My question is which way should i use or which way is better?
I dislike the first way because i have to add an extra method to configure other properties.
Second is ok for me, i just need call button anywhere i want to.
I think use let is suit the best so i use the third way, but the thing is i can't call self, which is if i add this link in the closure:
button.addTarget(self, action: "connect", forControlEvents: UIControlEvents.TouchUpInside)
I got the error:
ViewController.swift:24:26: Cannot convert value of type 'NSObject -> () -> ViewController' to expected argument type 'AnyObject?'
So i have add this line(any line with self) out of this closure. Any way can solve this?
Summary, which way is better or suit? Or any better way? thanks!
EDIT:
When i am using Objective C, i'd like use getter in this way
- (UIButton *) button {
if (!_button) {
_button = [[UIButton alloc] init];
_button.backgroundColor = [UIColor redColor];
...
}
return _button;
}
so my viewDidLoad will be clean and looks good:
- (void) viewDidLoad {
...
[self.view addSubview:self.button];
...
}
Styles obviously vary, but where I work we've standardized on the following approach:
class ViewController: UIViewController {
private var button: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
button = {
let button = UIButton(type: .Custom)
button.addTarget(self, action: "buttonTouched:", forControlEvents: .TouchUpInside)
button.otherProperty = ...
return button
}()
view.addSubview(button)
// add constraints for button
}
func buttonTouched(sender: UIButton) {
print("boop")
}
}
The problem with all of your approaches is that:
They're very verbose and not contained in a function, and
You don't have access to self as you've seen
With the approach above (using a force-unwrapped optional), you get the benefit of deferred initialization (i.e. everything happens in viewDidLoad()), you know because you own the object that button will never be nil (thus you don't have to use conditional binding all over the place), and you get contained initialization for all of your UIView properties in one place.
You can obviously (and we do) make your viewDidLoad() function look like this:
override func viewDidLoad() {
super.viewDidLoad()
createViews()
addSubviews()
addViewConstraints()
}
Then you get even better specialization in your functions and your code stays organized.