I am developing a very simple Swift application programmatically (without storyboards). I have a HomeViewController
import UIKit
class HomeViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func createButton(){
let button = UIButton()
button.setTitle("Start", forState: .Normal)
button.setTitleColor(UIColor.blueColor(), forState: .Normal)
button.frame = CGRectMake(100,200,50,50)
button.addTarget(self, action: "timerPressed:", forControlEvents: .TouchUpInside)
self.view.addSubview(button)
}
}
And I have the following
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let home = HomeViewController()
home.createButton()
}
}
When I call home.createButton() i expect a button to display, but currently it does not. I know the method works because when I moved that createButton() method into the ViewController class it displays the button. Am I missing something with how to properly call the createButton method from within the HomeViewController. Is there a better convention to do what I am trying to do?
class func createButton(controller: UIViewcontroller){
let button = UIButton()
button.setTitle("Start", forState: .Normal)
button.setTitleColor(UIColor.blueColor(), forState: .Normal)
button.frame = CGRectMake(100,200,50,50)
button.addTarget(self, action: "timerPressed:", forControlEvents: .TouchUpInside)
controller.view.addSubview(button)
}
Now call this method to any ViewController
Like This :
HomeViewController.createButton(self).
You can write createButton code under loadview() method of your HomeViewController
class HomeViewController: UIViewController {
override func loadView() {
super.loadView()
let button = UIButton()
button.setTitle("Start", forState: .Normal)
button.setTitleColor(UIColor.blueColor(), forState: .Normal)
button.frame = CGRectMake(100,200,50,50)
button.addTarget(self, action: "timerPressed:", forControlEvents: .TouchUpInside)
self.view.addSubview(button)
}
}
so there is no need to call extra method of HomeViewController to add button as a subview of it.
Related
When I create a button like below in the custom view the action does not work:
private let usernameButton: UIButton = {
let button = UIButton(type: .system)
button.setTitleColor(.black, for: .normal)
button.setTitle("someTitle", for: .normal)
button.titleLabel?.font = .boldSystemFont(ofSize: 13)
button.addTarget(self, action: #selector(didTapUsername), for: .touchUpInside)
return button
}()
However, it works when I do the same in UIViewController somehow.
It always works if I make it lazy var but couldn't understand why it doesn't work if I make it let in custom view while It is working in UIViewController
Thanks for any comment.
self inside that block actually is the block, not the view(you can see it using print(self)), probably because it's not yet initialised(same reason why you can't use self before super.init), that's why I usually add targets/delegates outside of the initialisation block. With lazy var self is already initialised, so no problem should be there.
I'm not sure why it works at all, seems kind of magic for me, and I wouldn't depend on it.
Anyway, it works fine with both custom ViewController and custom View in my case: I've added CustomView as subview to ViewController in the storyboard.
class ViewController: UIViewController {
private let usernameButton: UIButton = {
let button = UIButton(type: .system)
button.setTitleColor(.black, for: .normal)
button.setTitle("someTitle", for: .normal)
button.titleLabel?.font = .boldSystemFont(ofSize: 13)
button.addTarget(self, action: #selector(didTapUsername), for: .touchUpInside)
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
view.insertSubview(usernameButton, at: 0)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
usernameButton.frame = view.bounds
}
#objc
func didTapUsername() {
print("ViewController", #function)
}
}
class CustomView: UIView {
private let usernameButton: UIButton = {
let button = UIButton(type: .system)
button.setTitleColor(.black, for: .normal)
button.setTitle("someTitle", for: .normal)
button.titleLabel?.font = .boldSystemFont(ofSize: 13)
button.addTarget(self, action: #selector(didTapUsername), for: .touchUpInside)
return button
}()
override func awakeFromNib() {
super.awakeFromNib()
addSubview(usernameButton)
}
override func layoutSubviews() {
super.layoutSubviews()
usernameButton.frame = bounds
}
#objc
func didTapUsername() {
print("CustomView", #function)
}
}
This shouldn't do differently for UIViewController/UIView, check out your logic
The following code is located inside a subclass of UIView
I am setting up a cancelButton inside a closure:
private var cancelButtonClosure: UIButton = {
...
button.addTarget(self, action: #selector(cancel(_:)), for: .touchUpInside)
...
}()
And at first I instantiated the button inside a function like so:
func showConfirmationView(...) {
...
let cancelButton = self.cancelButtonClosure
...
addSubview(cancelButton)
...
}
However this resulted in the cancel function not being called at all (even though the layout was right and the button was highlighting)
So I made these change:
Removed the addTarget part from the cancelButtonClosure
Added the addTarget part inside the showConfirmationView function
So it looked like that:
func showConfirmationView(...) {
...
let cancelButton = self.cancelButtonClosure
cancelButton.addTarget(self, action: #selector(cancel(_:)), for: .touchUpInside)
...
addSubview(cancelButton)
...
}
It worked: the cancel function was called; but I don't know why. I'm really curious to know why what I did before did not work. Thanks for your insights!
Check your implementation because a setup like this works as expected:
private var cancelButton: UIButton = {
let btn = UIButton(type: .system)
btn.setTitle("Cancel", for: .normal)
btn.addTarget(self, action: #selector(cancelSomething(_:)), for: .touchUpInside)
return btn
}()
#objc func cancelSomething(_ sender: UIButton) {
print("Something has to be cancelled")
}
override func viewDidLoad() {
super.viewDidLoad()
showConfirmationView()
}
func showConfirmationView() {
cancelButton.sizeToFit()
cancelButton.center = view.center
view.addSubview(cancelButton)
}
SquareBox.swift
class SquareBox {
func createBoxes() {
for _ in 0..<xy {
let button = UIButton()
button.backgroundColor = .white
button.setTitleColor(UIColor.black, for: .normal)
button.layer.borderWidth = 0.5
button.layer.borderColor = UIColor.black.cgColor
stack.addArrangedSubview(button)
button.addTarget(self, action: #selector(click(sender:)) , for: .touchUpInside)
}
}
#objc func click(sender : UIButton) {
print("Click")
}
}
ViewController.swift
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let boxRow = SquareBox()
boxRow.createBoxes()
}
}
Also I've tried #IBAction instead of #objc, it doesn't work, but if I use "click" function in ViewController.swift that I created this object, it's working but I need this function inside of this class.
Now that you have posted relevant information in your question, the problem is quite clear. You have a memory management issue.
In your GameViewController's viewDidLoad you create a local instance of SquareBox. This local instance goes out of scope at the end of viewDidLoad. Since there is no other reference to this instance, it gets deallocated at the end of viewDidLoad.
Since the instance of SquareBox has been deallocated, it is not around to act as the button's target. And your click method is never called.
The solution is to keep a reference in your view controller:
class GameViewController: UIViewController {
let boxRow = SquareBox()
override func viewDidLoad() {
super.viewDidLoad()
boxRow.createBoxes()
}
}
var btnfirst:UIButton!
override func viewDidLoad()
{
super.viewDidLoad()
btnfirst = UIButton(type: .system)
btnfirst.setTitle("Press", for: .normal)
btnfirst.setTitleColor(.red, for: .normal)
btnfirst.frame = CGRect(x: 100, y: 200, width: 100, height: 30)
btnfirst.addTarget(self, action: #selector(benpress( sender:)),for: .touchUpInside)
self.view.addSubview(btnfirst)
}
func benpress( sender :UIButton)
{
//Your Code Here
}
For those who did not find a solution, here is mine.
If you constructed your UIButton as
let button: UIButton = {
return UIButton()
}()
Just convert those into
lazy var button: UIButton = {
return UIButton()
}()
I think this is because of somewhat deallocation as mentioned above.
button.addTarget(self, action:#selector(self.click), for: .touchUpInside)
func click(sender : UIButton) {
// code here
}
I guess the issue is how you are setting up layout of your buttons.
Try this:
func createBoxes() {
stack.backgroundColor = UIColor.red
for _ in 0..<xy {
// Create the button
let button = UIButton()
button.backgroundColor = UIColor.red
// Add constraints
button.translatesAutoresizingMaskIntoConstraints = false
button.heightAnchor.constraint(equalToConstant: 44.0).isActive = true
button.widthAnchor.constraint(equalToConstant: 44.0).isActive = true
// Setup the button action
button.addTarget(self, action: #selector(SquareBox.click(sender:)), for: .touchUpInside)
// Add the button to the stack
stack.addArrangedSubview(button)
}
}
#objc func click(sender : UIButton) {
print("Click")
}
button.addTarget(self, action: #selector(self.buttonTapped), for: .touchUpInside)
func buttonTapped(sender : UIButton) {
// code here
}
Replace with this :
btn.addTarget(self, action: #selector(self.click(sender:)), for: .touchUpInside)
I think something else effect to your selector method try to find in your code because your code also working in my project.
I am trying to pop a view off programmatically but am having trouble.
import UIKit
class CreatePostViewController: UIViewController {
var isAnimating: Bool = false
var dropDownViewIsDisplayed: Bool = false
override func viewDidLoad() {
super.viewDidLoad()
createDescriptionField()
createLabels()
createInputFields()
createBackButton()
}
//More code omitted
func createBackButton(){
let button = UIButton()
button.setTitle("back", forState: .Normal)
button.setTitleColor(UIColor.blueColor(), forState: .Normal)
button.frame = CGRectMake(200,200,100,50)
button.backgroundColor = UIColor.redColor()
button.addTarget(self, action: "goBack:", forControlEvents: .TouchUpInside)
self.view.addSubview(button)
}
func goBack(sender: UIButton){
navigationController?.popToRootViewControllerAnimated(true)
}
}
I add this class from a another class like this:
import UIKit
class HomeViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
needCashButton()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func needCashButton(){
let image = UIImage(named: "GlossyRoundedButton.png") as UIImage?
let button = UIButton()
button.setTitle("Do Job", forState: .Normal)
button.setTitleColor(UIColor.blueColor(), forState: .Normal)
button.setBackgroundImage(image, forState: .Normal)
button.frame = CGRectMake(225,200,100,50)
button.addTarget(self, action: "cashButtonPressed:", forControlEvents: .TouchUpInside)
self.view.addSubview(button)
}
func cashButtonPressed(sender: UIButton){
let findJob = CreatePostViewController()
self.presentViewController(findJob, animated: false, completion: nil)
}
}
I read many other posts that suggested I just pop the CreatePostViewController with the line: navigationController?.popToRootViewControllerAnimated(true) but that doesn't do anything when I click the button. Is there something I am overlooking here?
If your UIViewController is NOT in a UINavigationViewController (or it was presented modally), use this to dismiss it:
self.dismissViewControllerAnimated(true, completion: nil)
If you have it inside a UINavigationViewController, dismiss it using this:
self.navigationController.popViewControllerAnimated(true)
navigationController works only if you actually use Navigation controller :). So I would suggest to use something like this
let vc = UINavigationController(rootViewController: CreatePostViewController())
self.presentViewController(vc, animated: false, completion: nil)
I have a class like this,
import UIkit
class One {
let btn = UIButton()
override func viewDidLoad(){
super.viewDidLoad()
btn.frame = CGRectMake(10, 20, 30, 30)
btn.setTitle("Go", forState: UIControlState.Normal)
btn.addTarget(self, action: "goToClassTwo", forControlEvents: UIControlEvents.TouchUpInside)
self.view.addSubview(btn)
}
func goToClassTwo(){
if(AppGlobals().getIsFromDiffView()){
let difView = UINavigationController(rootViewController: DiffView())
difView.pushViewController(Two(), animated: true)
}else{
self.navigationController?.pushViewController(Two(), animated: true)
}
}
}
A setter/getter class like this,
class AppGlobals: NSObject {
var isFromDiffView = false
func setIsFromDiffView(val: Bool){
isFromDiffView = val
}
func getIsFromDiffView() -> Bool {
return isFromDiffView
}
}
And I have another class like this,
class DiffView {
let btn = UIButton()
override func viewDidLoad(){
super.viewDidLoad()
btn.frame = CGRectMake(10, 20, 30, 30)
btn.setTitle("Push", forState: UIControlState.Normal)
btn.addTarget(self, action: "btnAction", forControlEvents: UIControlEvents.TouchUpInside)
self.view.addSubview(btn)
}
func btnAction(){
AppGlobals().setIsFromDiffView(true)
One().goToClassTwo()
}
}
I am facing a problem here. When the 'Go' button in the class 'One' is tapped, then the 'Two' view controller is shown. But when I tap on the 'Push' button in the class 'DiffView' is tapped, the 'Two' view controller is not being shown.
I have checked setting breakpoints. The control does come to the goToClassTwo function in the class 'One' and the if path is being executed. But the 'Two' view controller is not shown. difView.pushViewController is called. But it is not pushing to the next view.
NOTE: I am not using storyboard
Any help would be appreciated!
This is the updated code.
Code for class 'One':
import UIKit
class One {
let btn = UIButton()
override func viewDidLoad(){
super.viewDidLoad()
btn.frame = CGRectMake(10, 20, 30, 30)
btn.setTitle("Go", forState: UIControlState.Normal)
btn.addTarget(self, action: "goToClassTwo", forControlEvents: UIControlEvents.TouchUpInside)
self.view.addSubview(btn)
}
func goToClassTwo(){
if(AppGlobals().getIsFromDiffView()){
//Using the navigation controller of DiffView
AppGlobals().getController().pushViewController(Two(), animated: true)
}else{
self.navigationController?.pushViewController(Two(), animated: true)
}
}
}
setter/getter class:
class AppGlobals: NSObject {
var isFromDiffView = false
var cntrlr: UINavigationController!
func setIsFromDiffView(val: Bool){
isFromDiffView = val
}
func getIsFromDiffView() -> Bool {
return isFromDiffView
}
//Setting and getting DiffView Navigation controller
func setController(cntrl: UINavigationController){
cntrlr = cntrl
}
func getController() -> UINavigationController {
return cntrlr
}
}
DiffView class:
class DiffView {
let btn = UIButton()
override func viewDidLoad(){
super.viewDidLoad()
btn.frame = CGRectMake(10, 20, 30, 30)
btn.setTitle("Push", forState: UIControlState.Normal)
btn.addTarget(self, action: "btnAction", forControlEvents: UIControlEvents.TouchUpInside)
self.view.addSubview(btn)
}
func btnAction(){
AppGlobals().setIsFromDiffView(true)
//Setting the navigation controller
AppGlobals().setController(self.navigationController!)
One().goToClassTwo()
}
}
With this updated code, class 'Two' view controller is being displayed.
Thank you #zp_x for your help.