//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
}
}
}
Related
I am trying to make a playground and I have a button that says "Let's play!" and moves into a new view controller.
I looked at the code from this website and put it into my code:
http://lab.dejaworks.com/ios-swift-3-playground-uibutton-action/
This is all of my code (like, all of it):
import UIKit
import PlaygroundSupport
class MyViewController : UIViewController {
override func loadView() {
//Introduction
let view = UIView()
view.backgroundColor = .red
//title
func labelCool() {
let label = UILabel()
label.frame = CGRect(x: 50, y: 300, width: 400, height: 100)
label.text = "Add-Add - A Wonderful Game!"
//label.frame = CGRect(x: 150, y: 200, width: 200, height: 20)
label.numberOfLines = 3
label.font = UIFont(name: "HelveticaNeue-Bold", size: 30)
UILabel.animate (withDuration: 10.0, animations:{
label.textColor = .black
})
UILabel.animate(withDuration: 5.0, animations:{
label.textColor = .blue
})
view.addSubview(label)
}
labelCool()
//subtitle
let subtitle = UILabel()
subtitle.frame = CGRect(x: 50, y: 400, width: 200, height: 50)
subtitle.text = "Curated and Created by Yours Truly, Adit Dayal!"
subtitle.numberOfLines = 4
self.view = view
view.addSubview(subtitle)
}
override func viewDidLoad() {
class Responder : NSObject {
#objc func action() {
print("Yay!")
}
}
let responder = Responder()
//next page
let button = UIButton(frame : CGRect(x: 0, y: 500, width: 200, height: 50))
button.setTitle("Let's Play!", for: .normal)
button.backgroundColor = .blue
button.addTarget(responder, action: #selector(Responder.action), for: .touchUpInside)
view.addSubview(button)
}
}
class gameViewController: UIViewController {
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()
For now, I just want the button to display "Yay!" when clicked, but it is doing nothing!
Does anyone know why? (I'm on a bit of a time constraint)
Thank you so much,
Adit Dayal
Your Responder class is inside the viewDidLoad() function put the class outside like so
class Responder : NSObject {
#objc func action() {
print("Yay!")
}
}
override func viewDidLoad() {
let responder = Responder()
//next page
let button = UIButton(frame : CGRect(x: 0, y: 500, width: 200, height: 50))
button.setTitle("Let's Play!", for: .normal)
button.backgroundColor = .blue
button.addTarget(responder, action: #selector(responder.action), for: .touchUpInside)
view.addSubview(button)
}
The problem is that you are creating the responder object inside the viewDidLoad, as a local variable; this cause the object to be destroyed when the function ends (but we want that object alive even after). You have to retain that object, so instead of creating a local variable, create an instance variable by simply saving it as a class scope:
class Responder : NSObject {
#objc func action() {
print("Yay!")
}
}
let responder = Responder() // this is now outside the viewDidLoad, so it's an instance variable
override func viewDidLoad() {
//next page
let button = UIButton(frame : CGRect(x: 0, y: 500, width: 200, height: 50))
button.setTitle("Let's Play!", for: .normal)
button.backgroundColor = .blue
button.addTarget(responder, action: #selector(Responder.action), for: .touchUpInside)
view.addSubview(button)
}
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
}
I'm new to Swift and have a hard time understand the event flow. The code below can be run directly in an xcode playground. I have a white UIView in the background. This view has a brown button and a red view as sub-views. Click on them and the events are logged in the controller, just as expected.
But the controller of this white view also adds another view, that has it's own controller class (SubviewController). SubviewController is green and has a blue subview with a black button. Question is... why don't I get any logs from the green, blue and black views/buttons?
import Foundation
import UIKit
import PlaygroundSupport
class TestViewController : UIViewController {
let playButton: UIButton = {
let playButton = UIButton(frame: CGRect(x: 155, y: 135, width: 160, height: 40))
playButton.setTitle("BROWN BUTTON", for: .normal)
playButton.backgroundColor = UIColor.brown
return playButton
}()
override func loadView() {
let viewWhite = UIView()
viewWhite.backgroundColor = UIColor.white
let viewRed = UIView()
viewRed.backgroundColor = UIColor.red
viewRed.frame = CGRect(x: 20, y: 20, width: 40, height: 10)
viewRed.clipsToBounds = true
let recognizer2 = UITapGestureRecognizer(target: self, action: #selector (self.handleTapRed(_:)))
viewRed.addGestureRecognizer(recognizer2)
let recognizer = UITapGestureRecognizer(target: self, action: #selector (self.handleTap(_:)))
viewWhite.addGestureRecognizer(recognizer)
playButton.addTarget(self, action: #selector (self.action) , for: .touchUpInside)
let catList = SubviewController()
viewWhite.addSubview(catList.view)
viewWhite.addSubview(playButton)
viewWhite.addSubview(viewRed)
self.view = viewWhite
}
func action() {
print("Brown button tapped")
}
func handleTap(_ sender:UITapGestureRecognizer){
print("WHITE VIEW (background view) TAPPED")
}
func handleTapRed(_ sender:UITapGestureRecognizer){
print("RED VIEW TAPPED")
}
}
class SubviewController: UIViewController {
let buttonBlack: UIButton = {
let button = UIButton(frame: CGRect(x: 40, y: 10, width: 170, height: 20))
button.backgroundColor = UIColor.black
button.setTitle("BLACK BUTTON", for: .normal)
return button
}()
let viewBlue: UIView = {
let v = UIView()
v.backgroundColor = UIColor.blue
v.frame = CGRect(x: 20, y: 40, width: 240, height: 60)
v.clipsToBounds = true
return v
}()
override func loadView() {
super.loadView()
self.view.backgroundColor = UIColor.green
buttonBlack.addTarget(self, action: #selector (self.blackKlick) , for: .touchUpInside)
self.view.addSubview(viewBlue)
self.view.frame = CGRect(x: 0, y: 40, width: 240, height: 60)
self.view.clipsToBounds = true
self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector (self.handleTapGreen(_:))))
viewBlue.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector (self.handleTapBlue(_:))))
viewBlue.addSubview(buttonBlack)
}
func blackKlick() {
print("Black button tapped")
}
func handleTapBlue(_ sender:UITapGestureRecognizer){
print("BLUE VIEW TAPPED")
}
func handleTapGreen(_ sender:UITapGestureRecognizer){
print("GREEN VIEW TAPPED")
}
}
PlaygroundPage.current.liveView = TestViewController()
Thanks for any help!
This line in your current code:
let catList = SubviewController()
creates a local instance of SubvieController. As soon as you exit the loadView() func, that instance is gone.
So, you need a class-level variable to keep that instance around. Add this line:
class TestViewController : UIViewController {
var catList: SubviewController!
and then remove the let from the instantiation line in loadView():
catList = SubviewController()
I have my ViewController.class and a Menu.class
In the Menu.class I create and setup all the buttons and in the ViewController.class I add the Menu to the view. When I run the code everything is shown on the screen but I am not able to press the button.
This is how my ViewController looks like:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let menu = Menu()
menu.setupView(controller: self, width: 600, height: 120)
self.view.addSubview(menu)
// Do any additional setup after loading the view, typically from a nib.
}
}
And this is my Menu:
import Foundation
import UIKit
class Menu: UIView{
func setupView(controller: ViewController, width: CGFloat, height: CGFloat){
let newGame = UIButton(frame: CGRect(x: controller.view.center.x, y: 300, width: width, height: height))
newGame.center = CGPoint(x: controller.view.center.x, y: 300)
newGame.setTitle("New Game", for: .normal)
newGame.backgroundColor = UIColor.gray
newGame.titleLabel?.font = UIFont(name: "Helvetica", size: 42)
newGame.setTitleColor(UIColor.black, for: .normal)
newGame.addTarget(self, action: #selector(Menu.newGame), for: .touchUpInside)
self.addSubview(newGame)
}
func newGame(){
print("New Game")
}
}
What is my mistake. Do I need to do more initializing so it is able to detect a press?
This is how you should do it. Repeat the operation for buttonTwo and label. Setup the view in viewDidLoad() and setup the frames in viewDidLayoutSubviews.
If you subclass any UIView you should setup the frames in layoutSubviews()
If you want to display a tableView when you click on newGame then create a new UIViewController. Add a UITableView in it, the same way you did add Button and Label in ViewController
import UIKit
class ViewController: UIViewController {
let buttonOne = UIButton()
let buttonTwo = UIButton()
let label = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
buttonOne.backgroundColor = UIColor.gray
buttonOne.setTitle("New Game", for: .normal)
buttonOne.titleLabel?.font = UIFont(name: "Helvetica", size: 42)
buttonOne.setTitleColor(UIColor.black, for: .normal)
buttonOne.addTarget(self, action: #selector(newGame), for: .touchUpInside)
view.addSubview(buttonOne)
view.addSubview(buttonTwo)
view.addSubview(label)
}
override func viewDidLayoutSubviews() {
buttonOne.frame = CGRect(x: 0, y: 300, width: 600, height: 120)
buttonOne.center.x = self.view.center.x
}
func newGame(sender: UIButton?) {
print("New Game")
}
}
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.