swift IBAction on button with two classes - ios

I wonder if I can trigger an action with a button created in an another class.
I explain:
I have two classes, the view controller, and a class used to create a view.
In the view controller, I call a method located in the second class to create a custom view. Then I add the custom view to the main view (see code below).
The custom view displays a button and I don't know how I can use my button because the method created as a target is not found when I run my app.
Code viewController:
import UIKit
class FirstViewController: UIViewController {
var popupViewBeforeOrderCoupon:UIView?
override func viewDidLoad() {
super.viewDidLoad()
popupViewBeforeOrderCoupon = CustomView.createPopupViewWithList()
self.view.addSubview(popupViewBeforeOrderCoupon!)
}
func cancelView(sender: UIButton!) {
var alertView = UIAlertView();
alertView.addButtonWithTitle("OK");
alertView.title = "Alert";
alertView.message = "Button Pressed!!!";
alertView.show();
}
}
and the second class CustomView:
import Foundation
import UIKit
class CustomView {
init () {
}
static func createPopupViewWithList() -> UIView? {
var dynamicView = UIView(frame: CGRectMake(100, 200, 200, 100))
dynamicView.backgroundColor = UIColor.grayColor()
dynamicView.alpha = 1
dynamicView.layer.cornerRadius = 5
dynamicView.layer.borderWidth = 2
let button = UIButton();
button.setTitle("Add", forState: .Normal)
button.setTitleColor(UIColor.blueColor(), forState: .Normal)
button.frame = CGRectMake(10, 10, 100, 50)
dynamicView.addSubview(button)
button.addTarget(self, action: "cancelView:", forControlEvents: .TouchUpInside)
return dynamicView
}
func cancelView(sender: UIButton!) {
var alertView = UIAlertView();
alertView.addButtonWithTitle("OK");
alertView.title = "Alert";
alertView.message = "Button Pressed!!!";
alertView.show();
}
}
I want my button created in customView to call the method cancelView when I press it but I don't manage to do it.
Here's the error I get :
NSForwarding: warning: object 0x523c8 of class 'Myproject.CustomView' does not implement methodSignatureForSelector: -- trouble ahead
Unrecognized selector +[Myproject.CustomView cancelView:]
How can I do it?

You can modify your createPopupViewWithList to accept 1 argument of UIViewController and then set button's target to it.
Code:
static func createPopupViewWithList(controller: UIViewController) -> UIView? {
var dynamicView = UIView(frame: CGRectMake(100, 200, 200, 100))
dynamicView.backgroundColor = UIColor.grayColor()
dynamicView.alpha = 1
dynamicView.layer.cornerRadius = 5
dynamicView.layer.borderWidth = 2
let button = UIButton();
button.setTitle("Add", forState: .Normal)
button.setTitleColor(UIColor.blueColor(), forState: .Normal)
button.frame = CGRectMake(10, 10, 100, 50)
dynamicView.addSubview(button)
// set your controller here
button.addTarget(controller, action: "cancelView:", forControlEvents: .TouchUpInside)
return dynamicView
}
Then call your function from FirstViewController:
popupViewBeforeOrderCoupon = CustomView.createPopupViewWithList(self)

Related

Swift 4 UIButton action in UIImageView not working

I am trying to add a delete button as a subview in an image. This is my current structure:
-> class DesignViewController: UIViewController
|
-> class Sticker: UIImageView, UIGestureRecognizerDelegate
|
-> UI button inside the Sticker
Inside Sticker class I have :
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let button2 = UIButton(frame: CGRect(x: 100, y: 100, width: 100, height: 50))
button2.backgroundColor = .red
button2.setTitle("Delete", for: .normal)
button2.tag = 23
button2.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
self.addSubview(button2)
}
#objc func buttonAction(sender: UIButton!) {
print("Button tapped")
}
The buttonAction is not getting called.
When I change self.addSubview(button2) line to :
self.superview?.addSubview(button2)
I can see buttonAction getting called. However I would like to keep the button inside the Sticker view so that when user moves the sticker, the button moves as a subview with it.
Can anyone please help and let me know how I can keep the button inside Sticker view?
By default isUserInteractionEnabled property of UIImageView is set to false. Set it to true and your button will start to respond. You can set it in code as well as in the storyboards.
Also try setting the clipsToBounds property of your imageview to true. It will clip your button if it is going outside of the image bounds. That might be one of the reason that your button is not getting touches.
You should create a protocol delegate for button action. This is code example:
protocol ButtonDelegate: class {
func buttonTapped(button: UIButton)
}
class Sticker: UIImageView {
weak var delegate: ButtonDelegate?
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(button2)
}
lazy var button2: UIButton = {
let button = UIButton(frame: CGRect(x: 100, y: 100, width: 100, height: 50))
button2.backgroundColor = .red
button2.setTitle("Delete", for: .normal)
button2.tag = 23
button2.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
return button
}()
#objc func buttonAction(sender: UIButton) {
guard let delegate = delegate else { return }
delegate.buttonTapped(button: sender)
}
So, now go to your DesignViewControllerl, add your custom imageview class Sticker. Don't forget to do that "imageView.delegate = self". Then in extension add protocol delegate you've created before. Code example:
class DesignViewController: UIViewController {
private lazy var sticker: Sticker = {
let iv = Sticker(frame: view.bounds)
iv.delegate = self
return iv
}()
override viewDidLoad() {
super.viewDidLoad()
view.addSubiew(sticker)
}
}
extension DesignViewController: ButtonDelegate {
func buttonTapped(button: UIButton) {
// input your action here
}
}

UIButton not performing action from function in a different class

I have a class where written is a function creating my button:
LoginButton.swift
func createButton() {
let myButton: UIButton = {
let button = UIButton()
button.addTarget(self, action: #selector(Foo().buttonPressed(_:)), for: .touchUpInside)
}()
}
Now in my second class, Foo.swift, I have a function that just prints a statement
Foo.swift
#objc func buttonPressed(_ sender: UIButton) {
print("button was pressed")
}
When ran I get no errors except when I try to press the button, nothing happens. Nothing prints, the UIButton doesn't react in any way. Really not sure where the error occurs because Xcode isn't printing out any type of error or warning message.
The action method is called in the target object. Thus, you have either to move buttonPressed to the class which contains createButton or to pass an instance of Foo as a target object.
But note that a button is not the owner of its targets. So, if you just write:
button.addTarget(Foo(), action: #selector(buttonPressed(_:)), for: .touchUpInside)
This will not work, because the Foo object is immediately released after that line. You must have a strong reference (e.g. a property) to Foo() like
let foo = Foo()
func createButton() {
let myButton: UIButton = {
let button = UIButton()
button.addTarget(foo, action: #selector(buttonPressed(_:)), for: .touchUpInside)
}()
}
You are missing with target. So make instant of target globally and make use of it as target for button action handler.
class ViewController: UIViewController {
let foo = Foo()
override func viewDidLoad() {
super.viewDidLoad()
createButton()
}
func createButton() {
let myButton: UIButton = {
let button = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 50))
button.backgroundColor = UIColor.red
button.setTitle("Tap me", for: .normal)
button.addTarget(self.foo, action: #selector(self.foo.buttonPressed(_:)), for: .touchUpInside)
return button
}()
myButton.center = self.view.center
self.view.addSubview(myButton)
}
}
Class Foo:
class Foo {
#objc func buttonPressed(_ sender: UIButton) {
print("button was pressed")
}
}
Just pass Selector as function argument.
func createButtonWith(selector: Selector) {
let myButton: UIButton = {
let button = UIButton()
button.addTarget(self, action: selector), for: .touchUpInside)
}()
}
And call this function like below...
createButtonWith(selector: #selector(Foo().buttonPressed(_:)))

Create a button programatically in differrent class swift

I am new to swift and trying to create a UIButton programmatically. I have two classes. I am creating a button in one class and calling that method from another class.
class viewcontroller
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let renderobj = render()
renderobj.sign()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
class render
import UIKit
class render: UIViewController {
func sign() {
let button = UIButton(frame: CGRect(x: 100, y: 100, width: 100, height: 50))
button.backgroundColor = .green
button.setTitle("Test Button", for: .normal)
button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
self.view.addSubview(button)
}
func buttonAction(sender: UIButton!) {
print("Button tapped")
}
}
But no button is created. If I put the UIButton code inside viewDidLoad() , it works. I want the button to be created in a function of different class. Is it possible to do so ?
Thanks for any help
func sign() -> UIButton
{
let button = UIButton(frame: CGRect(x: 100, y: 100, width: 100, height: 50))
button.backgroundColor = .green
button.setTitle("Test Button", for: .normal)
button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
return button
}
After returning that button you can add it in your calling view controller view
override func viewDidLoad() {
super.viewDidLoad()
let renderobj = render()
let button = renderobj.sign()
self.view.addSubview(button)
}
After doing this
let renderobj = render()
And after calling this function, a new button is initialized and added to the view of render view controller.
renderobj.sign()
But renver view controller is only initialized and its view is not added to current view controller which is ViewController
If you want to show render controller, then present it like usual. If you want to add that button to your ViewController then either declare a button variable and access it or return it in sign() function
I think there is a lack of understanding around the concepts of a UIViewController here. A UIViewController is used to manage the views displayed to a user, think about it as a single screen of your app. You can embed view controllers in other view controllers, but typically you won't need to do this for basic stuff.
A UIViewController has a lifecycle which you can find more about here: https://developer.apple.com/reference/uikit/uiviewcontroller#1652793
Now what it appears like you want to do is to wrap up the functionality of creating a button, you can do this by either creating a factory method in you ViewController class (or other class somewhere else), or by subclassing UIButton and creating a convenience initialiser method to create the button with the properties you want (you can also use Swift extensions but I won't go into that here).
Lets look at the simplest method and create a factory method inside your ViewController class:
// The button method takes a title and a selector (the selector is the method that should be called when the button is tapped)
func makeButton(title: String, action: Selector) -> UIButton {
// Create a basic button with a default size
let button = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 50))
// Customise the button with your settings
button.backgroundColor = .green
button.setTitle(title, for: .normal)
button.addTarget(self, action: action, for: .touchUpInside)
// Return the newly created button
return button
}
Now we have a method that creates a button of a default size and position, we want to call that method to create a button and then add it as a subview of the ViewController's view. To do that we can do something in the viewDidLoad() method that we override:
override func viewDidLoad() {
// Always remember to call the super method to ensure all super classes have a chance to do any work thats required by them
super.viewDidLoad()
// Create the button by calling our new method
let button = self.makeButton(title: "Test Button", action: #selector(buttonTapped))
// Set the origin of the button to move it to the right position
button.frame.origin = CGPoint(x: 100, y: 100)
// Finally add the new button as a subclass of the view controllers view
self.view.addSubview(button)
}
Now we understand whats going on in the methods we can put it all together to have a class that looks like this:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let button = self.makeButton(title: "Test Button", action: #selector(buttonTapped))
button.frame.origin = CGPoint(x: 100, y: 100)
self.view.addSubview(button)
}
func makeButton(title: String, action: Selector) -> UIButton {
let button = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 50))
button.backgroundColor = .green
button.setTitle(title, for: .normal)
button.addTarget(self, action: action, for: .touchUpInside)
return button
}
func buttonTapped(sender: UIButton) {
print("Button tapped")
}
}
You create new UIViewController here:
let renderobj = render()
renderobj.sign()
but ViewController instance is showing in this moment.

PushViewController from another UIViewController - Swift

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.

Unrecognized selector sent to instance 0x13d65ccf0 - Swift

I have programmatically created a button in my main class and passing an instance of a game class (gameSCNScene - where most of the game logic lies) to the button. Inside this game class instance is where the action for the button resides however when ever I press the button I get the error - Unrecognized selector.
class GameViewController: UIViewController, SCNSceneRendererDelegate {
var gameSCNScene: GameSCNScene!
override func viewDidLoad() {
super.viewDidLoad()
let scnView = self.view as! SCNView
scnView.delegate = self
// Create my game scene instance
gameSCNScene = GameSCNScene(currentview: scnView)
// Make button
makeButtonsUI(gameSCNScene)
}
func makeButtonsUI(gameSCNScene: GameSCNScene) {
let image = UIImage(named: "art.scnassets/addBtn.png") as UIImage?
let button = UIButton(type: UIButtonType.System) as UIButton
button.frame = CGRectMake(100, 100, 100, 100)
button.setImage(image, forState: .Normal)
button.addTarget(self, action:("gameSCNScene.addCube:"), forControlEvents:.TouchUpInside)
self.view.addSubview(button)
}
Button function inside my gameSCNScene instance
func addCube(sender:UIButton) {
//Code here
}
The line of code where you add the target is incorrect. This:
button.addTarget(self, action:("gameSCNScene.addCube:"), forControlEvents:.TouchUpInside)
Should be:
button.addTarget(gameSCNScene, action:("addCube:"), forControlEvents:.TouchUpInside)

Resources