appearance().setBackgroundImage Not Working On Custom Class - swift

I have created a UIBarButtonItem custom class and assigned one bar button item with this class in the storyboard.
In the app delegate I'm trying to set the appearance for it using:
VIPButton.appearance().setBackgroundImage(UIImage(named: "vipButton"), for: .normal, barMetrics: .default)
However while this works for regular UIBarButtonItems it has no affect for my custom class bar button item.
Any help would be appreciated.

create custom class from UIButton:
public class SimpleButton: UIButton {
#objc dynamic var backColor: UIColor? {
didSet {
self.backgroundColor = self.backColor
}
}
#objc dynamic var image: UIImage? {
didSet {
self.setImage(self.image, for: .normal)
}
}
}
Next you create a class from UIBarButtomItem.
now set customView property to SimpleButon for custom Appearance and set action for this like below:
public class SimpleBarButton: UIBarButtonItem {
// create object from simpleButton
private let button = SimpleButton()
public required init?(coder: NSCoder) {
super.init(coder: coder)
// add target for button
self.button.addTarget(self, action: #selector(self.buttonTapped(_:)), for: .touchUpInside)
// set title
self.button.setTitle("title", for: .normal)
self.button.sizeToFit()
// assing simple button to customView property
self.customView = self.button
}
// get callback from touchUp button
#objc func buttonTapped(_ sender: UIButton) {
// here set callback for tapped on BarButtonItem
// check target and action is not Nil
guard let target = self.target, let action = self.action else {
return
}
// call selector (implement to your ViewController)
// pass self for parameters
target.performSelector(inBackground: action, with: self)
}
}
I created a class in this section and assigned a button to the customView variable.
Now I add style to SimpleButton like this:
SimpleButton.appearance().backColor = .red
SimpleButton.appearance().image = UIImage.init(named: "images")!
now for create BarButtonItem on storyboard and change custom class to SimpleBarButton:
now create ViewController and add action for event barButton:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func sampleClicked(_ sender: SimpleBarButton) {
print("callBack from action on BarButton Item")
}
}
and assign this function to BarButton:
UI output:

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.

How do I targeting selector with addTarget in another class

I try to understand how to control component form one class to another class but that's not working.
class VC:UIViewController {
override func viewDidLoad() {
let instance = Test()
self.view.addSubView(instance.button)
}
}
class Test:NSObject {
var button:UIButton!
override init() {
button = UIButton(frame:CGRect(x:0, y:0, width:100, height:40))
button.setTitle(("TEST", for:.normal)
button.addTarget(self, action: #selector(tapButton(_:)), for: .touchUpInside)
}
#objc func tapButton(_ sender:UIButton) {
print("TAP Button")
}
}
When I tapped the button, nothing happens !
I try to change
button.addTarget(self, action: #selector(tapButton(_:)), for: .touchUpInside)
with
button.addTarget(nil, action: #selector(tapButton(_:)), for: .touchUpInside)
That's not working !
How to resolve this problem ?
Thanks for your help.
You have 3 issues:
instance is a local variable, so it gets deallocated as soon as viewDidLoad() finishes. Make it a property of VC. The button only keeps a weak reference to the object, so when it is deallocated it becomes nil. In cases where the target is nil, UIKit will search up the responder chain for the action method. Since VC doesn't supply a tapButton method, nothing happens when the button is pressed.
You need to call super.init() so that self is available to be used with the button. self can't be created until all properties have been initialized. Because init is an override, you must call super.init() to initialize the properties that NSObject provides before you can use self in the button.
Your frame puts your button in an unaccessible region of the screen. I changed your frame to put the button inside the safe area of the iPhone 11. I also made the button .green so that I could see it.
class VC: UIViewController {
var instance = Test() // make instance a property
override func viewDidLoad() {
// let instance = Test() // this was a local variable that doesn't hang around
self.view.addSubview(instance.button)
}
}
class Test: NSObject {
var button: UIButton!
override init() {
super.init() // call this so that you can use self below
button = UIButton(frame:CGRect(x: 100, y: 100, width: 100, height: 40))
button.setTitle("TEST", for:.normal)
button.backgroundColor = .green
button.addTarget(self, action: #selector(tapButton(_:)), for: .touchUpInside)
}
#objc func tapButton(_ sender: UIButton) {
print("TAP Button")
}
// Add deinit to see when this object is deinitialized. When
// instance is local to viewDidLoad() this object gets freed
// when viewDidLoad() finishes.
deinit {
print("Oops, the Test object has been deinitialized")
}
}

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

How to pass a UIView on UIButton as a parameter on click event

I generated a multiple UIButton and UIView using loop, the problem is, I want that generated UIView to be hidden when the generated UIButton was clicked,
The question is, how can I pass the UIView on a UIButton click event so that the system knows what UIView will going to be hidden
This is my code that generate UIButtons and UIViews
for (key, value) in myStringsArray {
let myButton = UIButton()
let myView = UIView()
panelButton.tag = value
panelButton.addTarget(self, action: #selector(onMyButtonClick), for: .touchUpInside)
}
The only data that I can pass on .tag was Int
And this is my onMyButtonClick function that listen on click event of the UIButton
#objc func onMyButtonClick (sender: UIButton) {
print(sender.tag)
}
What I want to do is to have a click listener function that is working like this
func clickMe (view: UIView, isOpen: Bool) {
view.isHidden = isOpen
}
You can assign the button and the view the same tag.
then you can find the view by tag and hide it.
#objc func onMyButtonClick (sender: UIButton) {
print(sender.tag)
if let foundView = view.viewWithTag(sender.tag) {
foundView.isHidden = true
}
}

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.