I am trying to create radio buttons programmatically based on data in Firebase. The Firebase data will essentially be a number, and then I plan on using that number in a for loop to populate the necessary number of radio buttons. I have previous experience in Android and am trying to translate to swift:
import UIKit
import FirebaseDatabase
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;
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 buttons = [UIButton]()
// 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(buttonAction(_:))), forControlEvents: .TouchUpInside)
myStackview.addSubview(button)
buttons.append(button)
// create other buttons and add into buttons ...
}
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)
}
})
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
EDIT: I was able to narrow down the issue, however I am still having some issues understanding selectors and senders. It seems that's where my error is coming in, as there is a red exclamation stating "Expected Expression" on the line of my #selector.
It would help if you spend some time to learn the language instead of translating it statement by statement. If you want a quick way (not ideal) refer to the syntax of statements / lines with a with red exclamation mark
For statement
for (x in 0 ..< numberOfChildren) { }
Swift uses type inference, it determines the type based on the value assigned.
Selector
It needs to be the name of the function with function argument labels on it
myStackView
Refer UIStackView
addArrangedSubview
buttons
Learn Swift Optionals
Related
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
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.
I'm trying to have a custom UIButton become hidden once it's pressed a certain number of times...but I'm at a loss.
Having exhausted my limited knowledge and consulting the Apple's documentation as well as the internet for the better part of 3 hours, I've finally made my way here. I've been learning Swift for a short while now and am making an effort to become more familiar with it. This is my first object-oriented language and it's testing me to say the least. Any help with this more likely than not ridiculously simple problem is very much appreciated.
import UIKit
class ViewController: UIViewController{
#IBOutlet weak var buttonMessageDisplay: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
buttonPressed()
}
var tapcount = 0
let buttonMessage : [String] = [/* long array of strings */]
func buttonPressed() {
let button = UIButton(type:.Custom) as UIButton
button.frame = CGRectMake(0, 0, 100, 100)
button.center = CGPointMake(self.view.frame.size.width/2, self.view.frame.size.height/2);
button.backgroundColor = UIColor.redColor()
button.layer.borderColor = UIColor.blackColor().CGColor
button.layer.borderWidth = 3
button.layer.cornerRadius = 0.5 * button.bounds.size.width
button.setTitle("", forState: UIControlState.Normal)
button.addTarget(self, action: "buttonPressed", forControlEvents: .TouchUpInside)
view.addSubview(button)
switch tapcount {
case 19...23:
//Hides the button
button.hidden = true
buttonMessageDisplay.text = buttonMessage[tapcount]
case 24...31:
//Unhides the button
button.hidden = false
buttonMessageDisplay.text = buttonMessage[tapcount]
default:
buttonMessageDisplay.text = buttonMessage[tapcount]
}
print("Tap Count: \(tapcount)")
++tapcount
}
Updated with Gesture Recognizer:
import UIKit
class ViewController: UIViewController{
#IBOutlet weak var buttonMessageDisplay: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
buttonMessageDisplay.text = ""
let button = UIButton(type:.Custom) as UIButton
button.frame = CGRectMake(0, 0, 100, 100)
button.center = CGPointMake(self.view.frame.size.width/2, self.view.frame.size.height/2);
button.backgroundColor = UIColor.redColor()
button.layer.borderColor = UIColor.blackColor().CGColor
button.layer.borderWidth = 3
button.layer.cornerRadius = 0.5 * button.bounds.size.width
button.setTitle("", forState: UIControlState.Normal)
button.addTarget(self, action: "buttonPressed", forControlEvents: .TouchUpInside)
self.view.addSubview(button)
}
var tapcount : Int = 0
let buttonMessage : [String] = [/* array of strings */]
#IBAction func userTap(sender: UITapGestureRecognizer) {
print("Tap Received")
if case 19...23 = tapcount {
buttonPressed()
}
}
func buttonPressed() {
switch tapcount {
case 0...18:
buttonMessageDisplay.text = buttonMessage[tapcount]
case 19...23:
//Hides the button
button.hidden = true
buttonMessageDisplay.text = buttonMessage[tapcount]
case 24...32:
//Unhides the button
button.hidden = false
buttonMessageDisplay.text = buttonMessage[tapcount]
case 33...100:
buttonMessageDisplay.text = buttonMessage[tapcount]
default:
print("There are no more messages or an error has been encountered")
}
print("Tap Count: \(tapcount)")
++tapcount
}
}
Your code makes no sense. As #formal says in his answer, you're creating a new button on every tap, which is wrong.
You want to define your button in your Storyboard.
Then you want an IBAction method, which takes the button as a parameter:
#IBAction func buttonPressed(sender: UIButton)
{
++tapcount
if tapcount < 19
{
sender.hidden = true
}
}
Note that if the button you're hiding is the same one the user is tapping, once it is hidden, you're done. The user can't tap a hidden button, so there's no way to un-hide it. (And thus no point in your switch statement)
Your main issue is that you are creating a new button every time you call button pressed. Create an #IBOutlet for your button and just set its hidden property in butPressed (which can be set as an action of the button).
Something like:
class ViewController: UIViewController {
#IBOutlet weak var button: UIButton!
#IBOutlet weak var buttonMessageDisplay: UILabel!
var tapcount = 0
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func butPressed(sender: AnyObject) {
switch tapcount {
case 19...23:
//Hides the button
button.hidden = true
case 24...31:
//Unhides the button
button.hidden = false
default: break
}
print("Tap Count: \(tapcount)")
buttonMessageDisplay.text = "Tap: \(tapcount)"
++tapcount
}
}
The method buttonPressed() creates a new button each time it is called. You should define button as a property similar to buttonMessageDisplay and place the code to initialise it within viewDidLoad().
You should give space between range in case condition:
For example:
(IBAction)buttonTapped:(id)sender {
self.count++;
switch (self.count) {
case 5 ... 23 :
self.button.titleLabel.text = #"disable";
self.button.hidden = true;
break;
default:
break;
}
}
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.
In my swift project I have a button and I want to print on a label how much time this button has been pressed.
How can I resolve this problem?
Adding to DHEERAJ's answer, you just need to add following code in func pressed(sender: UIButton!) to achieve what you need:
let date = NSDate()
let dateFormatter = NSDateFormatter()
dateFormatter.dateStyle = .MediumStyle
let dateString = dateFormatter.stringFromDate(date)
yourLabelName.text = dateString
Hope this might help.
To show how many times the button has been clicked you could class variable.
class SampleViewController: UIViewController {
var counter : Int!
#IBOutlet weak var yourLabelName: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
counter = 1
let myFirstButton = UIButton(frame: CGRectMake(10, 50, 320, 40))
myFirstButton.setTitle("Tap Me", forState: .Normal)
myFirstButton.setTitleColor(UIColor.blueColor(), forState: .Normal)
myFirstButton.addTarget(self, action: "pressed:",forControlEvents: .TouchUpInside)
self.view.addSubview(myFirstButton)
}
func pressed(sender: UIButton!)
{
yourLabelName.text = "Pressed : \(counter)"
counter = counter + 1
}
}
Here is how button is created programmatically and prints a text in label when button is pressed.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let myFirstButton = UIButton()
myFirstButton.setTitle("Tap Me", forState: .Normal)
myFirstButton.setTitleColor(UIColor.blueColor(), forState: .Normal)
myFirstButton.frame = CGRectMake(10, 50, 320, 40)
myFirstButton.addTarget(self, action: "pressed:", forControlEvents: .TouchUpInside)
self.view.addSubview(myFirstButton)
}
func pressed(sender: UIButton!)
{
yourLabelName.text=#"Your Text is here"
}