UIButton not working on a subview of UIWindow - ios

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)
}

Related

How to declare a programmed button as a var

I would to do a programmed button without the storyboard. The problem is that I can not call the button in a separate function like I can when I drag and drop a UIButton from a storyboard into a view controller. I do not want to use the storyboard at all.
//Trying to Create a var for btn
override func viewDidLoad() {
super.viewDidLoad()
let btn = UIButton(type: .custom) as UIButton
btn.backgroundColor = .blue
btn.setTitle("Button", for: .normal)
btn.frame = CGRect(x: 100, y: 100, width: 200, height: 100)
btn.addTarget(self, action: #selector(clickMe), for: .touchUpInside)
self.view.addSubview(btn)
}
#objc func clickMe(sender:UIButton!) {
print("Button Clicked")
}
func place() {
//do something to btn.
}
Read about variable scopes. In the question you have declared your button inside the method/function which restricts the scope of its usage within the method. When you declare the variable within the scope of the class/struct you can use it within other methods/functions.
let btn = UIButton(type: .custom)
override func viewDidLoad() {
super.viewDidLoad()
btn.backgroundColor = .blue
// .. other settings here
self.view.addSubview(btn)
}
#objc
func clickMe(sender:UIButton) {
print("Button Clicked")
}
func place() {
btn.backgroundColor = .red
}

How to add UIButton programmatically in ViewControllers

I have two view controllers, ViewControllerA and ViewControllerB, where ViewControllerB is a subclass of ViewControllerA. Here is the code implementation in ViewControllerA.
class ViewControllerA: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
createButton()
buttonPressed()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func createButton () {
let button = UIButton();
button.setTitle("Add", forState: .Normal)
button.setTitleColor(UIColor.blueColor(), forState: .Normal)
button.frame = CGRectMake(200, 65, 46, 30) // X, Y, width, height
button.addTarget(self, action: "buttonPressed:", forControlEvents: .TouchUpInside)
self.view.addSubview(button)
}
func buttonPressed(sender: UIButton!) {
var alertView = UIAlertView();
alertView.addButtonWithTitle("Done");
alertView.title = "Alert!";
alertView.message = "Button Pressed!!!";
alertView.show();
}
In my ViewControllerB I would like to use createButton() but not the way it was used in ViewControllerA. Let's say I want to have red color not blue in ViewControllerB in createButton(). How can I override createButton() to be different in ViewControllerB?
The following would work.
class ViewControllerA: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
createButton()
buttonPressed()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func createButton () {
let button = UIButton();
button.setTitle("Add", forState: .Normal)
button.setTitleColor(UIColor.blueColor(), forState: .Normal)
button.frame = CGRectMake(200, 65, 46, 30) // X, Y, width, height
button.addTarget(self, action: "buttonPressed:", forControlEvents: .TouchUpInside)
self.view.addSubview(button)
}
func buttonPressed(sender: UIButton!) {
var alertView = UIAlertView();
alertView.addButtonWithTitle("Done");
alertView.title = "Alert!";
alertView.message = "Button Pressed!!!";
alertView.show();
}
class ViewControllerB: ViewControllerA {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func createButton () {
let button = UIButton();
button.setTitle("Add", forState: .Normal)
button.setTitleColor(UIColor.blueColor(), forState: .Normal)
button.frame = CGRectMake(200, 65, 46, 30) // X, Y, width, height
button.addTarget(self, action: "buttonPressed:", forControlEvents: .TouchUpInside)
self.view.addSubview(button)
}
override func buttonPressed(sender: UIButton!) {
var alertView = UIAlertView();
alertView.addButtonWithTitle("Done");
alertView.title = "Alert!";
alertView.message = "Button Pressed!!!";
alertView.show();
}
Hopefully can help those who are also trying to understand the best way of view controller subclassing.

How to make a function in swift that addresses all UIButtons that will ever be on the ViewController

I have a longer code that spawns UIButtons onto the UIViewController every certain amount of time. But now what I am trying to do is turn their backgroundColor from a random color to UIColor.whiteColor(). Hw could I make a function that will do this to the UIButton that is clicked on but will work for every single UIButton I "spawn" in. Or is it easier to use UIViews opposed to UIButtons and take a different path? Thank you.
import UIKit
func randomPoint() -> CGPoint {
let randomPoint: CGPoint = CGPoint(x:CGFloat(arc4random()%320),y: CGFloat(arc4random()%528))
return randomPoint
}
class ViewController: UIViewController {
#IBOutlet weak var startGameButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
class ViewTwo : UIViewController {
var timer = NSTimer()
func randomColor() -> UIColor {
let red = CGFloat(drand48())
let green = CGFloat(drand48())
let blue = CGFloat(drand48())
return UIColor(red: red, green: green, blue: blue, alpha: 1.0)
}
func spawnEnemy() {
let enemy: UIButton = UIButton(frame: CGRect(x: 320, y: 320, width: 25, height: 25))
enemy.backgroundColor = randomColor()
enemy.center = randomPoint()
enemy.addTarget(self, action: Selector("buttonPressed"), forControlEvents: UIControlEvents.TouchUpInside)
self.view.addSubview(enemy)
}
// or my buttonPressed func goes but either way the app crashes when i click on one of the UIButtons
override func viewDidLoad() {
super.viewDidLoad()
func buttonPressed(sender: UIButton) {
sender.backgroundColor = UIColor.whiteColor()
}
timer = NSTimer.scheduledTimerWithTimeInterval(0.1, target: self, selector: Selector("spawnEnemy"), userInfo: nil, repeats: true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
When you're creating your buttons to spawn them you can add a target like this:
button.addTarget(self, action: "buttonClicked:", forControlEvents: UIControlEvents.TouchUpInside)
And then add the corresponding function to change the background color to white like this:
func buttonClicked(sender: UIButton) {
sender.backgroundColor = UIColor.whiteColor()
}
This will work for all buttons you spawn.

Button in view not clicking ios swift

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.

clicking a on runtime created button crashes my ios app (swift)

I am a newby in ios development and I am facing the following problem.
I create a button at runtime in the viewDidLoad method:
class ViewController: UIViewController {
#IBOutlet weak var TestButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
println("start");
// Create Button at runtime
var button = UIButton.buttonWithType(UIButtonType.System) as UIButton
button.frame = CGRectMake(100, 100, 100, 50)
button.backgroundColor = UIColor.redColor()
button.setTitle("Test Button", forState: UIControlState.Normal)
button.addTarget(self, action: "buttonAction:", forControlEvents: UIControlEvents.TouchUpInside)
self.view.addSubview(button)
func buttonAction(sender:UIButton!)
{
println("Button tapped.")
}
}
When I press the button in the simulator the app stops at line :
class AppDelegate: UIResponder, UIApplicationDelegate {
of AppDelegate.swift
Does anyone have any idea why it doesn't output "Button tapped." ?
If I get a problem like this, how can I report the errormessage to someone else ? I mean I do not see any errorcode or stacktrace in XCode. Where to find this ?
Function not in viewDidLoad.
example:
class ViewController: UIViewController {
#IBOutlet weak var TestButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
println("start");
// Create Button at runtime
var button = UIButton.buttonWithType(UIButtonType.System) as UIButton
button.frame = CGRectMake(100, 100, 100, 50)
button.backgroundColor = UIColor.redColor()
button.setTitle("Test Button", forState: UIControlState.Normal)
button.addTarget(self, action: "buttonAction:", forControlEvents: UIControlEvents.TouchUpInside)
self.view.addSubview(button)
} // CLOSE
func buttonAction(sender:UIButton!)
{
println("Button tapped.")
}
}// CLOSE
You should extract your method for button outside your function like following:
class ViewController: UIViewController {
#IBOutlet weak var TestButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
println("start");
// Create Button at runtime
var button = UIButton.buttonWithType(UIButtonType.System) as UIButton
button.frame = CGRectMake(100, 100, 100, 50)
button.backgroundColor = UIColor.redColor()
button.setTitle("Test Button", forState: UIControlState.Normal)
button.addTarget(self, action: "buttonAction:", forControlEvents: UIControlEvents.TouchUpInside)
self.view.addSubview(button)
}
func buttonAction(sender:UIButton!)
{
println("Button tapped.")
}
}
Why is this you might ask? It is because you will register your event which is in scope of the function. When function ends your function for event is not there any more. Since, the chrash
Try this:
override func viewDidLoad() {
super.viewDidLoad()
let button = UIButton.buttonWithType(UIButtonType.System) as UIButton
button.frame = CGRectMake(100, 100, 100, 50)
button.backgroundColor = UIColor.greenColor()
button.setTitle("Test Button", forState: UIControlState.Normal)
button.addTarget(self, action: "buttonAction:", forControlEvents: UIControlEvents.TouchUpInside)
self.view.addSubview(button)
}
func buttonAction(sender:UIButton!)
{
println("Button tapped")
}
Also import UIKit

Resources