Creating UIButton programmatically in Swift 4 but getting error [duplicate] - swift

This question already has answers here:
Expected declaration error for making high score [duplicate]
(2 answers)
Closed 4 years ago.
I'm trying to create a UIButton in Swift 4 but I keep getting a "Expected declaration" error in the "MyView" class when I try to call the addTarget function. I've done this same code in other classes and have never gotten the error. Am I missing something? Thanks.
import Foundation
import UIKit
protocol MyDelegate: class {
func onButtonTapped()
}
class OtherViewController: UIViewController {
}
class MyViewController: UIViewController, MyDelegate {
func onButtonTapped() {
let nextViewController = OtherViewController()
navigationController?.pushViewController(nextViewController, animated: false)
}
var myView: MyView!
override func viewDidLoad() {
super.viewDidLoad()
myView.delegate = self
}
}
class MyView: UIView {
weak var delegate: MyDelegate?
let button = UIButton()
button.addTarget(self, action: #selector(buttonTapped),for: .touchUpInside)
func buttonTapped() {
self.delegate?.onButtonTapped()
}
}

you cannot addTarget or call any method in the class space , this space is for declartion as the error suggests .
to fix this you can do either this
let button:UIButton =
{
let btn = UIButton()
btn.addTarget(self, action: #selector(buttonTapped),for: .touchUpInside)
return btn
}()
Or
let button:UIButton = UIButton()
override init(frame: CGRect) {
super.init(frame: frame)
button.addTarget(self, action: #selector(buttonTapped),for: .touchUpInside)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
in both cases you must add #objc annotation to the buttonTapped function because selectors must refer to an #objc function .
so it will be like this
#objc func buttonTapped(){}
Also you need to add this button to the view on order for it to be drawn to the screen .
view.addSubView(button)

Related

Swift: How to reduce duplicated code when using ViewControllers with identifical buttons

I have the following code on Swift:
class ResenhaMarcasFocinhoController: UIViewController {
var rotateMaisButton:UIButton! = nil
var rotateMenosButton:UIButton! = nil
var lixeiraButton:UIButton! = nil
var confirmarButton:UIButton! = nil
var desfazerVermelhoButton:UIButton! = nil
var desfazerPretoButton:UIButton! = nil
override func viewDidLoad() {
super.viewDidLoad()
inflateView()
}
func inflateView() {
rotateMaisButton = SubViewHelper.getButtonByText(buttonText: "rotate_mais", currentView: view)
rotateMaisButton.addTarget(self, action: #selector(rotateMais_Clicked), for: .touchUpInside)
rotateMenosButton = SubViewHelper.getButtonByText(buttonText: "rotate_menos", currentView: view)
rotateMenosButton.addTarget(self, action: #selector(rotateMenos_Clicked), for: .touchUpInside)
lixeiraButton = SubViewHelper.getButtonByText(buttonText: "lixeira", currentView: view)
lixeiraButton.addTarget(self, action: #selector(lixeira_Clicked), for: .touchUpInside)
confirmarButton = SubViewHelper.getButtonByText(buttonText: "confirmar", currentView: view)
confirmarButton.addTarget(self, action: #selector(confirmar_Clicked), for: .touchUpInside)
desfazerVermelhoButton = SubViewHelper.getButtonByText(buttonText: "desfazer_vermelho", currentView: view)
desfazerVermelhoButton.addTarget(self, action: #selector(desfazerVermelho_Clicked), for: .touchUpInside)
desfazerPretoButton = SubViewHelper.getButtonByText(buttonText: "desfazer_preto", currentView: view)
desfazerPretoButton.addTarget(self, action: #selector(desfazerPreto_Clicked), for: .touchUpInside)
}
func rotateMais_Clicked(sender: UIButton) {
// some code
}
func rotateMenos_Clicked(sender: UIButton) {
// some code
}
func lixeira_Clicked(sender: UIButton) {
// some code
}
func confirmar_Clicked(sender: UIButton) {
// some code
}
func desfazerVermelho_Clicked(sender: UIButton) {
// some code
}
func desfazerPreto_Clicked(sender: UIButton) {
// some code
}
}
The function SubViewHelper.getButtonByText is only a way to assign the button based in the text.
But my problem here is that I have 4 UIViewControllers that contains the same code, because each UIView has the same buttons.
Is it possible to reduce the code on each view controller?
ViewControllers are just classes, and they can participate in inheritance just like any class. So define a base ViewController
class BaseViewController: UIViewController
{
// Common implementation
}
and then inherit from it:
class ViewController1: BaseViewController {
...
}
class ViewController2: BaseViewController {
...
}
...
You can call super for the common code.
An alternative approach would be to define a protocol for your view controllers and provide the common implementation in a protocol extension, however since extensions can't store properties, this might not be the best use for protocols.
If you prefer to use composition rather than inheritance, you can put your common code in a separate class that's not even a view controller that you instantiate in your view controllers, and forward whatever you like to it. This is essentially creating your own kind of delegate:
struct MyViewControllerDelegate // Doesn't have to be a class so I made this a struct
{
/* all those common buttons go here */
func viewDidLoad() { /* common code */ }
}
class ViewController: UIViewController
{
var myDelegate = MyViewControllerDelegate()
override func viewDidLoad()
{
super.viewDidLoad()
myDelegate.viewDidLoad()
// whatever special code you need here.
}
...
}
If you find this cropping up for other sets of view controllers, you might be able to define a protocol for your "ViewControllerDelegates" so they provide a common interface. That would make it easier to generalize the solution.

Type has no member during Apple tutorial

I just started learning Swift. I am following this Apple tutorial but I was met with this error. I copied the exact code from the tutorial. Not sure where did I go wrong.
Error message:
"Type 'RatingControl' has no member 'ratingButtonTapped(button:)'"
//Mark: Private Methods
private func setupButtons() {
for _ in 0..<5 {
//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(RatingControl.ratingButtonTapped(button:)), for: .touchUpInside)
//Add the button to the stack
addArrangedSubview(button)
//Add the new button to the rating button array
ratingButtons.append(button)
}
}
Initial to get the button action the RatingControl.swift class will be like
import UIKit
#IBDesignable class RatingControl: UIStackView {
//MARK: Initialization
override init(frame: CGRect) {
super.init(frame: frame)
}
required init(coder: NSCoder) {
super.init(coder: coder)
}
//MARK: Button Action
#objc func ratingButtonTapped(button: UIButton) {
print("Button pressed")
}
}
Create a file named RatingControl.swift with code and run your project. This issue will be resolved.
if you already have the full class the just add #objc before the method ratingButtonTapped.
More info: You can download the full project of the tutorial you currently following. The download link is present at the bottom.
We truly need to see the whole RatingControl class in order give you a complete answer. However your problem based on the error is that you there is no function with the name ratingButtonTapped inside your RatingControl class. There should be a class like the following in order to register it as a target to a button.
Example of .addTarget usage:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//Setup the button action
let button = UIButton()
button.addTarget(self, action: #selector(ratingButtonTapped(_:)), for: .touchUpInside)
view.addSubview(button)
}
#objc func ratingButtonTapped(sender: UIButton) {
// Triggered when the button is pressed
}
}

addTarget() does not work on custom extensions

I made a custom UIView() and i'm trying to add a okButton to it.
However. I have added the button but when showing my UIView in one of my ViewControllers, But the button won't work. This is my code:
class Luna: UIView {
let luna = UIView()
let okButton = UIButton()
typealias completionHandler = (_ success:Bool) -> Void
// Not yet implemented
open var touchOutsideToHide: Bool = true;
private var screenWidth: CGFloat {
return UIScreen.main.bounds.width
}
override init(frame: CGRect) {
super.init(frame: frame)
okButton.setTitleColor(.blue, for: .normal)
okButton.setTitle("Okay", for: .normal)
okButton.titleLabel?.font = UIFont(name: "avenirnext-demibold", size: 16)
let gesture:UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.pressButton(_:)))
okButton.addGestureRecognizer(gesture)
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
//The target function
#objc func pressButton(_ sender: UIButton){ //<- needs `#objc`
// .. //
print("btn hit")
}
I do not get btn hit in the console and the tap isn't registered
the button is visible, it's added as a subview:
Screenshot
Button have default touch gesture, you don't need to add it. just add target as follow to fix your issue. Create protocol of your customer class and declare delegate for it in same class as follow.
protocol LunaDelegate: class {
func okButtonTapped(sender: UIButton)
}
class Luna: UIView {
let luna = UIView()
let okButton = UIButton()
typealias completionHandler = (_ success:Bool) -> Void
weak var delegate: LunaDelegate?
// Not yet implemented
open var touchOutsideToHide: Bool = true;
private var screenWidth: CGFloat {
return UIScreen.main.bounds.width
}
override init(frame: CGRect) {
super.init(frame: frame)
okButton.setTitleColor(.blue, for: .normal)
okButton.setTitle("Okay", for: .normal)
okButton.titleLabel?.font = UIFont(name: "avenirnext-demibold", size: 16)
okButton.addTarget(self, action: #selector(pressButton(sender:)), for: .touchUpInside)
}
#IBAction func pressButton(sender: UIButton) {
if let selfDelegate = self.delegate {
selfDelegate.okButtonTapped(sender: sender)
}
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
Then after implement your code as follow to use your custom view. When your button will pressed then delegate method will be call in your view controller.
Usage:
class LunaViewController: UIViewController, LunaDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let luna = Luna(frame: CGRect.init(x: 100, y: 100, width: 100, height: 40))
luna.delegate = self
self.view.addSubview(luna)
}
// MARK:- Luna Delegate
func okButtonTapped(sender: UIButton) {
print("OK Button Pressed From Luna View")
}
}
This is the proper solution which I have already using in my current project. I hope this will help you.

Selector to Static Func with Parameters in Swift?

Is it possible to have a selector to a static function with parameters in Swift?
The syntax I tried is this:
button.addTarget(VCPresenter.self, action: #selector(VCPresenter.handleBackButton(_:parentNavigationController!)), for: .touchUpInside)
and the static method is this
#objc public static func handleBackButton(_ controller: UINavigationController) {
controller.popViewController(animated: true);
}
When I compile, I get
Error:(24, 56) argument of '#selector' does not refer to an '#objc' method, property, or initializer
Is this even possible to do?
I made new project with single view application and this work. Let me know if this work for you.
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let button = UIButton()
button.addTarget(self, action: #selector(ViewController.buttonAction(x:y:)), for: .touchUpInside)
}
#objc public static func buttonAction(x: Int, y: String){
print("Test")
}
}

I have custom uinavigation class. In that custom delegate method declared. How to access that method to view controller

#objc protocol MyDelegate {
func buttonAction()
}
class CustomNavigationBar: UINavigationController {
var delegte : MyDelegate?
override func viewDidLoad() {
super.viewDidLoad()
let button = UIButton.init(frame: CGRectMake(200, 10, 50, 30))
button.setBackgroundImage(UIImage(named: "a.png"), forState: .Normal)
button.addTarget(self, action: "testing", forControlEvents: .TouchUpInside)
self.navigationBar.addSubview(button)
}
func testing(){
self.delegte?.buttonAction()
print("Pavan")
}
If i press this button, testing is calling.
But in viewcontroller calling delegate method but giving error
class ViewController: UIViewController,MyDelegate{
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.title = "hi"
let vc = CustomNavigationBar()
vc.delegte = self
}
func buttonAction() {
print("Tupale")
}
would u mind to hint the error message?
updated:
In CustomNavigationBar class, you have to change var delegte : MyDelegate? to var delegte : UIViewController?.
then in ViewController class, you could set self which is an instance of UIViewController to the delegate of vc.
Have a try.