I'm trying to implement a dropdown menu in Swift by adding a view below the navigation bar and initially setting it to hidden until a navigationBarItem button is pressed, which works. In the dropdown View I have added two buttons as seen in the code below but it doesn't seem to pick up the event.
var isAnimating: Bool = false
var dropDownViewIsDisplayed : Bool = false
var dropDownView : UIView!
var buttonOne : UIButton!
var buttonTwo : UIButton!
var screenWidth : CGFloat!
#IBOutlet weak var searchNavigationBar: UINavigationItem!
override func viewDidLoad() {
super.viewDidLoad()
screenWidth = self.view.bounds.size.width
self.navigationController?.navigationBar.translucent = false
dropDownView = UIView(frame: CGRectMake(0, -15, screenWidth, -80))
dropDownView.hidden = true
dropDownView.userInteractionEnabled = true
self.navigationController?.view.insertSubview(self.dropDownView, belowSubview: (self.navigationController?.navigationBar)!)
buttonOne = UIButton(frame: CGRectMake(0, 0, screenWidth, 40))
buttonOne.setTitle("Button One", forState: .Normal)
buttonOne.setTitleColor(UIColor.blackColor(), forState: .Normal)
buttonOne.backgroundColor = UIColor.whiteColor()
buttonOne.addTarget(self, action: Selector("buttonOnePressed"), forControlEvents: UIControlEvents.TouchUpInside)
buttonOne.userInteractionEnabled = true
dropDownView.addSubview(buttonOne)
buttonTwo = UIButton(frame: CGRectMake(0, buttonOne.bounds.size.height, screenWidth, 40))
buttonTwo.setTitle("Button Two", forState: .Normal)
buttonTwo.setTitleColor(UIColor.blackColor(), forState: .Normal)
buttonTwo.backgroundColor = UIColor.whiteColor()
buttonTwo.addTarget(self, action: Selector("buttonTwoPressed"), forControlEvents: UIControlEvents.TouchUpInside)
buttonTwo.userInteractionEnabled = true
dropDownView.addSubview(buttonTwo)
}
func buttonTwoPressed(){
self.performSegueWithIdentifier("showLocation", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "showLocation") {
var location: LocationTableViewController = (segue.destinationViewController as? LocationTableViewController)!
}
}
Button click functions are not being called.
Are you using Swift 2.0 with Xcode 7? Just by quickly tried playing with your code like this, I found that the event handlers are being called properly without any big changes. Are you sure that there is nothing wrong somewhere else?
var isAnimating: Bool = false
var dropDownViewIsDisplayed : Bool = false
var dropDownView : UIView!
var buttonOne : UIButton!
var buttonTwo : UIButton!
var screenWidth : CGFloat!
#IBOutlet weak var searchNavigationBar: UINavigationItem!
override func viewDidLoad() {
super.viewDidLoad()
screenWidth = self.view.bounds.size.width
self.navigationController?.navigationBar.translucent = false
// I modified these 2 lines to test your code immediately
dropDownView = UIView(frame: CGRectMake(0, 65, screenWidth, 80))
dropDownView.hidden = false
dropDownView.userInteractionEnabled = true
self.navigationController?.view.insertSubview(self.dropDownView, belowSubview: (self.navigationController?.navigationBar)!)
buttonOne = UIButton(frame: CGRectMake(0, 0, screenWidth, 40))
buttonOne.setTitle("Button One", forState: .Normal)
buttonOne.setTitleColor(UIColor.blackColor(), forState: .Normal)
buttonOne.backgroundColor = UIColor.whiteColor()
buttonOne.addTarget(self, action: Selector("buttonOnePressed"), forControlEvents: UIControlEvents.TouchUpInside)
buttonOne.userInteractionEnabled = true
dropDownView.addSubview(buttonOne)
buttonTwo = UIButton(frame: CGRectMake(0, buttonOne.bounds.size.height, screenWidth, 40))
buttonTwo.setTitle("Button Two", forState: .Normal)
buttonTwo.setTitleColor(UIColor.blackColor(), forState: .Normal)
buttonTwo.backgroundColor = UIColor.whiteColor()
// Here I just wanted to show you that calling Selector() is not necessary at all
buttonTwo.addTarget(self, action: "buttonTwoPressed", forControlEvents: UIControlEvents.TouchUpInside)
buttonTwo.userInteractionEnabled = true
dropDownView.addSubview(buttonTwo)
}
// I didn't see this method in your code above so I added to test and it works!
func buttonOnePressed() {
print("buttonOnePressed")
}
// This is also being called normally
func buttonTwoPressed() {
print("buttonTwoPressed")
}
Can you please try with:
buttonOne.addTarget(self, action: Selector("buttonOnePressed:"), forControlEvents: UIControlEvents.TouchUpInside)
buttonTwo.addTarget(self, action: Selector("buttonTwoPressed:"), forControlEvents: UIControlEvents.TouchUpInside)
It won't works because it outside self.navigationController?.navigationBar.frame. You should add buttons on self.view or transparent modal view/UIWindow.
Related
#IBOutlet var button: UIButton!
func randomize(){
var x_axis:CGFloat = 8.0
var y_axis:CGFloat = 330.0
for selected_Synonym in selected_Synonyms {
button = UIButton.init(type: UIButtonType.custom) as UIButton
button.frame = CGRect(x: x_axis, y: y_axis, width: 400, height: 50)
button.backgroundColor = UIColor.black
button.setTitle(selected_Synonym as? String, for: UIControlState.normal)
button.setTitleColor(UIColor.white, for: [])
button.addTarget(self, action: Selector(("pressed:")), for: UIControlEvents.touchUpInside)
self.view.addSubview(button)
x_axis = 10.0
y_axis += 70.0
}
}
func pressed(sender: Any){
let buttonTitle = button.currentTitle
print(buttonTitle)
}
However when it runs and I press on a button I get the following error:
Thread 1: signal SIGABRT.
The program creates 5 buttons. I am new to swift and ios development would be very grateful if someone could help me out. Thank you.
You have several issues. To fix the crash, replace Selector(("pressed:")) with #selector(pressed). The use of Selector is very out-of-date. Always use #selector.
Next, remove the #IBOutlet var button: UIButton! line. You don't need it.
Then change:
button = UIButton.init(type: UIButtonType.custom) as UIButton
to:
let button = UIButton(type: .custom)
Then update your pressed function to:
#objc func pressed(sender: UIButton){
let buttonTitle = sender.currentTitle
print(buttonTitle)
}
Note the addition of #objc. This is required for any function being used with a target/selector. Also note that sender is now UIButton instead of Any. It's best to set the sender's type to match the proper type.
Here's all of your code with lots of little fixes:
func randomize() {
var xAxis: CGFloat = 8.0
var yAxis: CGFloat = 330.0
for selectedSynonym in selectedSynonyms {
let button = UIButton(type: .custom)
button.frame = CGRect(x: xAxis, y: yAxis, width: 400, height: 50)
button.backgroundColor = .black
button.setTitle(selectedSynonym, for: .normal)
button.setTitleColor(.white, for: .normal)
button.addTarget(self, action: #selector(pressed), for: .touchUpInside)
self.view.addSubview(button)
xAxis = 10.0
yAxis += 70.0
}
}
#objc func pressed(sender: UIButton){
let buttonTitle = sender.currentTitle
print(buttonTitle)
}
Use camelCase, not snake_case when naming variables and functions. Make use of Swift type inference.
//This is the label
let changeLbl = UILabel(frame: CGRect(x: 20, y: 170, width: 300, height: 21))
self.view.addSubview(changeLbl)
//This is the button
let submitButton = UIButton(type: .system) // let preferred over var here
submitButton.addTarget(self, action: #selector(buttonAction) , for: UIControlEvents.touchUpInside)
self.view.addSubview(submitButton)
//and this is action of above button
func buttonAction(sender: UIButton!) {
}
I want to call the above label in the following button function? like
func buttonAction(sender: UIButton!) {
changeLbl.ishidden = true
// want to access label here, but it's not working this way.
}
If you want to Access changeLbl you need to define as instance variable like this way and give global scope inside the class.
class ViewController: UIViewController {
var changeLbl : UILabel!
var submitButton : UIButton!
override func viewDidLoad() {
super.viewDidLoad()
//This is the UILabel
changeLbl = UILabel(frame: CGRect(x: 20, y: 170, width: 300, height: 21))
self.view.addSubview(changeLbl)
//This is the button
submitButton = UIButton(type: .system) // let preferred over var here
submitButton.addTarget(self, action: #selector(buttonAction) , for: UIControlEvents.touchUpInside)
self.view.addSubview(submitButton)
}
//and this is action of above button
func buttonAction(sender: UIButton!) {
changeLbl.isHidden = true
}
}
changeLbl is a local variable. It's only visible in the scope of the method where the label is created.
If you don't want to use an instance variable – which is the normal way – and there is only one UILabel you can get the label from the subviews array
func buttonAction(sender: UIButton) {
if let changeLbl = self.view.subviews.filter({ $0 is UILabel}).first {
changeLbl.ishidden = true
}
}
If You want access label then declare it Globally.
Below Code for that:
class LabelDemo: UIViewController{
var changeLbl: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
changeLbl = UILabel(frame: CGRect(x: 20, y: 170, width: 300, height: 21))
changeLbl.backgroundColor = UIColor.black
self.view.addSubview(changeLbl)
//This is the button
let submitButton = UIButton(type: .system) // let preferred over var here
submitButton.backgroundColor = UIColor.green
submitButton.frame = CGRect(x: 50, y: 150, width: 50, height: 30)
submitButton.setTitle("hide", for: .normal)
submitButton.addTarget(self, action: #selector(buttonAction) , for: UIControlEvents.touchUpInside)
self.view.addSubview(submitButton)
}
func buttonAction(sender: UIButton!) {
if changeLbl.isHidden {
changeLbl.isHidden = false
}else{
changeLbl.isHidden = true
}
}
}
I'm trying to create a custom one-button AlertViewController in Swift, I use window.addSubview() to show the AlertView, but when touch the button on the AlertView, the func buttonTapped() is not working, below is my code, please tell me what's wrong here, thanks.
MyAlertViewController.swift
class MyAlertViewController: UIViewController {
var button: UIButton!
var contentView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
setUp()
}
func setUp(){
contentView = UIView(frame: CGRectMake(0,0,200,300))
contentView.center = view.center
contentView.backgroundColor = UIColor.whiteColor()
contentView.layer.cornerRadius = 10
button = UIButton(type: .System)
button.frame = CGRectMake(50, 150, 100, 40)
button.setTitle("button", forState: UIControlState.Normal)
button.addTarget(self, action: #selector(buttonTapped(_:)), forControlEvents: UIControlEvents.TouchUpInside)
view.addSubview(contentView)
contentView.addSubview(button)
view.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.1)
view.userInteractionEnabled = true
}
func buttonTapped(sender: AnyObject?){
print("button tapped")
}
func show(){
let window = UIApplication.sharedApplication().keyWindow! as UIWindow
view.frame = window.bounds
window.addSubview(view)
}
}
ParentViewController.swift (is the rootViewController of my window)
class ParentViewController: UIViewController {
var button: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.whiteColor()
button = UIButton(type: .System)
button.frame = CGRectMake(110,269,100,30)
button.setTitle("show", forState: .Normal)
button.addTarget(self, action: #selector(buttonTapped(_:)), forControlEvents: .TouchUpInside)
view.addSubview(button)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func buttonTapped(sender: AnyObject?){
MyAlertViewController().show()
}
}
I found out if I change ParentViewController.swift as below, the button on the alert view can work correctly.
class ParentViewController: UIViewController {
var button: UIButton!
var vc: MyAlertViewController!
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.cyanColor()
button = UIButton(type: .System)
button.frame = CGRectMake(110,269,100,30)
button.setTitle("show", forState: .Normal)
button.addTarget(self, action: #selector(buttonTapped(_:)), forControlEvents: .TouchUpInside)
view.addSubview(button)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func buttonTapped(sender: AnyObject?){
vc = MyAlertViewController()
vc.show()
}
}
But I intended to show this alert view each time the user get new messages,so it would show up at any time and on any ViewController , so I don't want to declare it in each ViewController, so How can I solve this?
TLDR;
You have to retain an instance of MyAlertViewController().
Change to this and it will work (as you've already done):
// keep some reference
var alert: MyAlertViewController?
func buttonTapped(sender: AnyObject?){
alert = MyAlertViewController()
alert?.show()
}
More explanation
The button.addTarget(self, ...) that is called inside MyAlertViewController does not retain self.
The last line of the doc of addTarget function said that:
// ... the action cannot be NULL. Note that the target is not retained.*
So there will be no self to send action to after leaving of this function:
func buttonTapped(sender: AnyObject?){
MyAlertViewController().show()
}
Another option,
is to keep self variable in MyAlertViewController:
// retain self manually
var mySelf: MyAlertViewController?
func setUp(){
...
// reference to self
mySelf = self
button.addTarget(mySelf, action: #selector(buttonTapped(_:)), forControlEvents: UIControlEvents.TouchUpInside)
}
I have the following buttons which are adding dynamically based on some condition. Now i need to change all added buttons behaviour as radio buttons. so that I can do some actions based on the selection..
var posX = 0
var posY = 0
if trim.contains("0"){
print("contaim 0")
let button1 = UIButton(frame: CGRect(x: posX, y: posY, width: 60, height: 20))
button1.setTitleColor(UIColor.blackColor(), forState: .Normal)
button1.setTitle("No", forState: .Normal)
button1.setImage(UIImage(named: "checkbox untick.png")!, forState: .Normal)
button1.addTarget(self, action: #selector(buttonAction), forControlEvents: .TouchUpInside)
myStackview.addSubview(button1)
posX = 60
}
if trim.contains("1") {
print("contaim 1")
let button2 = UIButton(frame: CGRect(x: posX, y: posY, width: 60, height: 20))
button2.setTitleColor(UIColor.blackColor(), forState: .Normal)
button2.setTitle("Less", forState: .Normal)
button2.setImage(UIImage(named: "checkbox untick.png")!, forState: .Normal)
button2.addTarget(self, action: #selector(buttonAction), forControlEvents: .TouchUpInside)
myStackview.addSubview(button2)
posX = posX + 60
}
if trim.contains("2"){
print("contaim 2")
let button3 = UIButton(frame: CGRect(x: posX, y: posY, width: 60, height: 20))
button3.setTitleColor(UIColor.blackColor(), forState: .Normal)
button3.setTitle("Half", forState: .Normal)
button3.setImage(UIImage(named: "checkbox untick.png")!, forState: .Normal)
button3.addTarget(self, action: #selector(buttonAction), forControlEvents: .TouchUpInside)
myStackview.addSubview(button3)
posX = posX + 60
}
I have set the buttonAction methods as below buts its not working
func buttonAction(sender: UIButton!) {
print("Button tapped")
sender.setImage(UIImage(named: "checkboxredtick.png")!, forState: .Normal)
}
let buttons = [UIButton]()
// create button1
let button1 = UIButton(frame: CGRect(x: posX, y: posY, width: 60, height: 20))
button1.setTitleColor(UIColor.blackColor(), forState: .Normal)
button1.setTitle("No", forState: .Normal)
button1.setImage(UIImage(named: "checkbox untick.png")!, forState: .Normal)
// if the selected button cannot be reclick again, you can use .Disabled state
button1.setImage(UIImage(named: "checkboxredtick.png")!, forState: .Selected)
button1.addTarget(self, action: #selector(buttonAction), forControlEvents: .TouchUpInside)
myStackview.addSubview(button1)
buttons.append(button1)
// create other buttons and add into buttons ...
func buttonAction(sender: UIButton!){
for button in buttons {
button.selected = false
}
sender.selected = true
// you may need to know which button to trigger some action
// let buttonIndex = buttons.indexOf(sender)
}
In case anyone finds this useful. Here is an example of how I use DLRadioButton library (https://github.com/DavydLiu/DLRadioButton) to create RadioButtons dynamically.
You can check a complete small project to show a working code in here https://github.com/omhack/DynamicRadioButtons
import UIKit
import DLRadioButton
//Delegate to manage the Polls
protocol QuestionsDialogDelegate : class{
func questionsAnswered(polls: [Poll])
}
class QuestionsDialog: UIViewController {
#IBOutlet weak var stackView: UIStackView!
#IBOutlet var rootView: UIView!
#IBOutlet weak var nestedView: UIView!
weak var delegate: QuestionsDialogDelegate?
var questions : [Question]!
var polls = [Poll]()
var serviceType : Int = 0
var indexView : Int = 0
override func viewDidLoad() {
super.viewDidLoad()
//setting the stack view properties
stackView.alignment = UIStackViewAlignment.leading
stackView.axis = .vertical
}
//this is where the heavy logic, to create the dynamic radio buttons takes place
func showQuestions(){
if(questions.count <= 1){
rootView.frame.size.height = 200
nestedView.frame.size.height = 200
}else{
rootView.frame.size.height = 400
nestedView.frame.size.height = 400
}
for question in questions{
var otherButtons : [DLRadioButton] = []
let frame1 = CGRect(x: self.view.frame.size.width / 2 - 131, y: 350, width: 262, height: 17);
//we create a base radio button to use it as an anchor view
let firstRadioButton = createRadioButton(frame: frame1, title: "", color: UIColor.purple);
let label = UILabel()
label.textAlignment = .center
label.font = UIFont.boldSystemFont(ofSize: 17.0)
label.textColor = UIColor.darkGray.withAlphaComponent(0.85)
label.adjustsFontSizeToFitWidth = true
label.minimumScaleFactor = 0.25
label.frame = CGRect(x: 0, y: 0, width: 200, height: 30)
label.text = question.question
self.stackView.insertArrangedSubview(label, at: self.indexView)
self.indexView += 1
let poll = Poll()
poll.idQuestion = question.idQuestion
var i = 0;
for answer in question.answers{
let frame = CGRect(x: self.view.frame.size.width / 2 - 131, y: 380 + 30 * CGFloat(i), width: 300, height: 17);
let radioButton = createRadioButton(frame: frame, title: answer.answer! + " ", color: UIColor.purple)
radioButton.tag = answer.idAnswer
radioButton.params["poll"] = poll
otherButtons.append(radioButton)
self.stackView.insertArrangedSubview(radioButton, at: self.indexView)
i += 1;
self.indexView += 1
}
firstRadioButton.otherButtons = otherButtons
firstRadioButton.isHidden = true
firstRadioButton.otherButtons[1].isSelected = true
}
}
//Method to create a custom button
private func createRadioButton(frame : CGRect, title : String, color : UIColor) -> MyDLUIButton {
let radioButton = MyDLUIButton(frame: frame);
radioButton.titleLabel?.translatesAutoresizingMaskIntoConstraints = false
radioButton.titleLabel!.font = UIFont.systemFont(ofSize: 14);
radioButton.setTitle(title, for: []);
radioButton.setTitleColor(UIColor.darkGray, for: []);
radioButton.iconColor = color;
radioButton.indicatorColor = color;
radioButton.contentHorizontalAlignment = UIControlContentHorizontalAlignment.left;
radioButton.addTarget(self, action: #selector(QuestionsDialog.selectedAnswer(_:)), for: UIControlEvents.touchUpInside)
self.view.addSubview(radioButton);
return radioButton;
}
#objc func selectedAnswer(_ sender: MyDLUIButton){
let poll = sender.params["poll"] as? Poll
poll?.idAnswer = sender.tag
if let row = self.polls.index(where: {$0.idQuestion == poll?.idQuestion}) {
self.polls[row] = poll!
}else{
self.polls.append(poll!)
}
print("polls size: \(self.polls.count)")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func viewDidDisappear(_ animated: Bool) {
if(self.polls.count < self.questions.count){
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "questionsDialogDismissed"), object: nil)
}
}
#IBAction func requestService(_ sender: UIButton) {
delegate?.questionsAnswered(polls: self.polls)
}
}
class MyDLUIButton: DLRadioButton{
var params: Dictionary<String, Any>
override init(frame: CGRect) {
self.params = [:]
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
self.params = [:]
super.init(coder: aDecoder)
}
}
I would create a tag for every UIButton, then add your UIButton to be send to your ButtonAction:
button.tag = 0
button.addTarget(object, action: #selector(YourViewController.buttonAction(_:)),
forControlEvents: .TouchUpInside)
func buttonAction(sender: UIButton!) {
let selectedButton = sender as! UIButton
print(selectedButton.tag) // here stands the correct button which is pressed
}
You could also create an Array containing all Buttons, for example:
let myButtons = [UIButton]()
let button1 = UIButton()...
button1.tag = 0
myButtons.add(button1)
And now change it like this way:
myButtons[0].setImage(.....)
Where 0 is your first button (so with tag 0) // you should set your tags in the same order in which you append it to your myButtons Array.
I'm trying to make an application where i have to touch a button who is moving up the screen. I am using CADisplayLinkfor this. The problem in my code is that its creating a new button instead of using a specific one:
#IBOutlet var label: UILabel!
#IBOutlet var button2: UIButton!
#IBAction func button1(sender: UIButton) {
label.hidden = true
button2 = UIButton(type: UIButtonType.System) as UIButton
button2.frame = CGRectMake(120, 400, 100, 100)
button2.setTitle("Test Button", forState: UIControlState.Normal)
button2.addTarget(self, action: "buttonAction:", forControlEvents: UIControlEvents.TouchUpInside)
self.view.addSubview(button)
let displayLink = CADisplayLink(target: self, selector: "handleDisplayLink:")
displayLink.addToRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
}
func handleDisplayLink(displayLink: CADisplayLink) {
var buttonFrame = button.frame
buttonFrame.origin.y += -2
button2.frame = buttonFrame
if button2.frame.origin.y <= 50 {
displayLink.invalidate()
}
}
func buttonAction(sender: UIButton) {
sender.alpha = 0
label.hidden = false
}
override func viewDidLoad() {
super.viewDidLoad()
label.hidden = true
}
Thank you.
Anton
You can delete
button2 = UIButton(type: UIButtonType.System) as UIButton
button2.frame = CGRectMake(120, 400, 100, 100)
button2.setTitle("Test Button", forState: UIControlState.Normal)
And just set these attributes in Interface Builder.
Also this line:
self.view.addSubview(button)
No longer makes sense after you edited your question, but it is also not necessary if your buttons are IBOutlets.
This line:
var buttonFrame = button.frame
Only makes sense if button is an IBOutlet declared elsewhere.