Change UIImage on UIButton with separate Swift file if loop - ios

Goal of the code:
To change a UIButton's UIImage that was already set on viewDidLoad() using an outside function that is not in viewDidLoad(), that is in an entirely separate swift file in the project.
I am however able to successfully change the UIImage of the UIButton by linking the button to a function that does the standard exampleButton.setImage(exampleButtonimage, for: .normal).
Code below:
class ViewController: UIViewController {
static let vcShared = ViewController()
var exampleButton = UIButton(frame: CGRect(x: 146, y: 140, width: 100, height: 50))
let exampleButtonimage = UIImage(named: "ExampleImage")
...
override func viewDidLoad() {
super.viewDidLoad()
exampleButton.setImage(exampleButtonimage , for: .normal)
}
}
Attempting to set UIButton's UIImage in the Second file:
let otherButtonimage = UIImage(named: "OtherImage")
if someVariable > 55000 {
ViewController.vcShared.exampleButton.setImage(otherButtonimage , for: .normal)
}
When the if criteria is met, the UIButton in viewDidLoad() does not change its image (I know the if criteria is met as I have a separate print("Hello World") in the if loop that consistently works.) However, nothing happens with the UIButton when the criteria is met.

Best practice is cosntruct delegate or observer pattern for that situation but the code below is going to work your code.
class ViewController: UIViewController {
static var exampleButton = UIButton(frame: CGRect(x: 146, y: 140, width: 100, height: 50))
let exampleButtonimage = UIImage(named: "ExampleImage")
...
override func viewDidLoad() {
super.viewDidLoad()
exampleButton.setImage(exampleButtonimage , for: .normal)}}
And where change code;
let otherButtonimage = UIImage(named: "OtherImage")
if someVariable > 55000 {
ViewController.exampleButton.setImage(otherButtonimage , for: .normal)}

Related

Swift: Set UIView as the background of UIBarButton/UIButton

How can I set a UIView as the background of a UIBarButton? UIBarButton/UIButton should contain its title and background image.
I tried with overriding UIButton class, but it doesn't work. If someone has a solution please advise me.
// For UIBarButtonItem (just set customView parameter)
let frame = CGRect(x: 0, y: 0, width: 100, height: 40)
let customView = UIView(frame: frame)
customView.backgroundColor = .red
let barButtonItem = UIBarButtonItem(customView: customView)
// for UIButton
// 1. Subview case (add subview on your target UIButton, send to back so that it does not cover title)
let button0 = UIButton(frame: frame)
button0.addSubview(customView)
button0.sendSubviewToBack(customView)
button0.setTitle("Button", for: UIControl.State())
// 2. Rendered image case (as an alternative render your view to an image and add as a background image)
// Extension for capturing a UIVIew as an image:
extension UIImage {
convenience init?(view: UIView) {
UIGraphicsBeginImageContext(view.frame.size)
guard let ctx = UIGraphicsGetCurrentContext() else {
return nil
}
view.layer.render(in: ctx)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
guard let cgImage = image?.cgImage else {
return nil
}
self.init(cgImage: cgImage)
}
}
let button1 = UIButton(frame: frame)
let customViewImage = UIImage.init(view: customView)
button1.setBackgroundImage(customViewImage, for: UIControl.State())
button1.setTitle("Button", for: UIControl.State())
You can add view as subView of button:
extension UIButton {
func setBackgroundView(view: UIView) {
view.frame = CGRect(x: 0, y: 0, width: self.frame.width, height: self.frame.height)
view.isUserInteractionEnabled = false
self.addSubview(view)
}
}
You can use CustomButton which inherits from UIControl. By using xib you canmanage your UI as per your requirements. You can add n number of UI elements in it. You will get default event handler with this like UIButton (like touchupinside, touchupoutside, valuechange). You can provide IBActions for same. For reference attaching some screen shots. Let me know if you face any while implementing this. Keep coding.
Maybe you should use UIImageView.image instead of the UIView itself to set the bottom background image.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var myButton: UIButton!
#IBOutlet weak var myView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
myButton.setBackgroundImage(myView.image, for: .normal)
}
}

Trying to understand why this crashes when switch changes

learning Swift 3 - not sure why the code to change the background color of my button crashes the app. myButton is a class variable and should be available to the function correct?
class ViewController: UIViewController {
var myButton : UIButton!
var mySwitch : UISwitch!
override func viewDidLoad() {
super.viewDidLoad()
let firstView = UIView()
firstView.backgroundColor = UIColor.white
view = firstView
let myNewView = UIView( frame: CGRect(x:65, y:100, width:300, height:200))
myNewView.backgroundColor = UIColor.blue
//myNewView.layer.cornerRadius = 25
//myNewView.layer.borderWidth = 2
//myNewView.layer.borderColor = UIColor.red.cgColor
self.view.addSubview(myNewView)
let switchDemo=UISwitch();
switchDemo.isOn = true
switchDemo.setOn(true, animated: false);
switchDemo.addTarget(self, action: #selector(ViewController.switchValueDidChange(sender:)), for: .valueChanged);
let myButton = UIButton(frame: CGRect(x: 0, y: 0, width: 144, height: 144))
myButton.backgroundColor = UIColor.red
myButton.addTarget(self, action: #selector(ViewController.ratingButtonTapped(sender:)), for: .touchUpInside)
self.view.addSubview(myButton)
myButton.addSubview(switchDemo);
}
func switchValueDidChange( sender:UISwitch){
print("Switch changed")
myButton.backgroundColor = UIColor.green
}
func ratingButtonTapped(sender:UIButton!)
{
print("Button It Works!!!")
}
}
First of all and strictly spoken myButton is an instance variable (actually a property) rather than a class variable.
let myButton = creates a local variable with the same name as the property but it is not the same object.
The solution is to omit let
myButton = UIButton(frame: CGRect(x: 0, y: 0, width: 144, height: 144))
It's simple. You declare a class property but you don't instantiate it. I your viewDidLoad you just instantiate a local variable myButton, instead of instantiating the class property.
Here is a fix:
myButton = UIButton(frame: CGRect(x: 0, y: 0, width: 144, height: 144))
Just remove the let keyword.
You did not instantiate myButton anywhere in your code, therefore it stays nil.
You are, however, declaring a local variable called myButton in viewDidLoad.
You should replace your instantiation with this:
myButton = UIButton(frame: CGRect(x: 0, y: 0, width: 144, height: 144))

Swift Custom Navigation Controller Class

Everyone who reads it, hello and thank you. I am writing an application like Viber or WhatsApp for iOS devices using Xcode 8.2.1 and Swift 3.0. I have a problem: I want my app has a navigation controller but not a simple one, I want it to be a custom one. There is a print screen from WhatsApp to be understanding, what I am trying to achieve:
As we can see, this navigation controller has 3 buttons, label and image with avatar. I also want to change my color to green one. Due to do this I have created class:
import UIKit
class CustomNavigationController: UINavigationController {
override func viewDidLoad() {
super.viewDidLoad()
self.navigationBar.barTintColor = UIColor.green
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 39, height: 39))
imageView.contentMode = .scaleAspectFit
let image = UIImage(named: "logo")
imageView.image = image
navigationItem.titleView = imageView
}
}
And it gives this result:
As you can see it is without picture. I know that if I add this code:
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 39, height: 39))
imageView.contentMode = .scaleAspectFit
let image = UIImage(named: "logo")
imageView.image = image
navigationItem.titleView = imageView
to ViewController, image with logo will appear but I want complete class which I will connect somewhere (for example, in Interface Builder) and it will make a beautiful Navigation Controller with buttons, images and etc. Moreover, I can create a different class with another Navigation Controller and connect it to different screens, on which, for example, I will not need images, only buttons. So question is: how to make custom Navigation Controller class?
I will be really glad and thankful for any help, Thank You!
Now, I know the answer! It will be written for Swift 4.1.2. Each class inherited from UIViewController has a property self.navigationController?.navigationBar or navigationItem. You can create you own class like this:
class CustomNavigationController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let color = UIColor(red: 81 / 255, green: 155 / 255, blue: 22 / 255, alpha: 1.0)
self.navigationController?.navigationBar.barTintColor = color
let image = UIImage(named: "logo")
let imageView = UIImageView(image: image)
imageView.contentMode = .scaleAspectFit
navigationItem.titleView = imageView
}
private func imageView(imageName: String) -> UIImageView {
let logo = UIImage(named: imageName)
let logoImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 35, height: 35))
logoImageView.contentMode = .scaleAspectFit
logoImageView.image = logo
logoImageView.widthAnchor.constraint(equalToConstant: 35).isActive = true
logoImageView.heightAnchor.constraint(equalToConstant: 35).isActive = true
return logoImageView
}
func barImageView(imageName: String) -> UIBarButtonItem {
return UIBarButtonItem(customView: imageView(imageName: imageName))
}
func barButton(imageName: String, selector: Selector) -> UIBarButtonItem {
let button = UIButton(type: .custom)
button.setImage(UIImage(named: imageName), for: .normal)
button.frame = CGRect(x: 0, y: 0, width: 35, height: 35)
button.widthAnchor.constraint(equalToConstant: 35).isActive = true
button.heightAnchor.constraint(equalToConstant: 35).isActive = true
button.addTarget(self, action: selector, for: .touchUpInside)
return UIBarButtonItem(customView: button)
}
}
Now you can inherit any of you UIViewControllers from this class CustomNavigationController and add ass much buttons, icons, change colors, logos and etc. as much as you want (of course, in normal range)! Like this:
class ViewController: CustomNavigationController {
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.leftBarButtonItem = barImageView(imageName: "picture1")
let firstImage = barImageView(imageName: "picture2")
let secondButton = barButton(imageName: "picture3", selector: #selector(secondButtonPressed))
navigationItem.rightBarButtonItems = [firstImage, secondButton]
}
#objc func secondButtonPressed() {
print("Pressed")
}
}
This is my solution, maybe not the best, but 100% works! Thank you!

Eliminate repeated code Swift 3

I have multiple navigation controllers and their root view controllers in my app. I want each navigation bar to have social media buttons closely placed on the right side of the bar. For the same I have used this code to show the buttons in 1 view controller:
let fbImage = UIImage(named: "Facebook.png")!
let twitterImage = UIImage(named: "Twitter.png")!
let youtbImage = UIImage(named:"YouTube.png")!
let fbBtn: UIButton = UIButton(type: .custom)
fbBtn.setImage(fbImage, for: UIControlState.normal)
fbBtn.addTarget(self, action: #selector(HomeViewController.fbBtnPressed), for: UIControlEvents.touchUpInside)
fbBtn.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
let fbBarBtn = UIBarButtonItem(customView: fbBtn)
let twitterBtn: UIButton = UIButton(type: .custom)
twitterBtn.setImage(twitterImage, for: UIControlState.normal)
twitterBtn.addTarget(self, action: #selector(HomeViewController.twitterBtnPressed), for: UIControlEvents.touchUpInside)
twitterBtn.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
let twitterBarBtn = UIBarButtonItem(customView: twitterBtn)
let youtbBtn: UIButton = UIButton(type: .custom)
youtbBtn.setImage(youtbImage, for: UIControlState.normal)
youtbBtn.addTarget(self, action: #selector(HomeViewController.youtubeBtnPressed), for: UIControlEvents.touchUpInside)
youtbBtn.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
let youtbBarBtn = UIBarButtonItem(customView: youtbBtn)
self.navigationItem.setRightBarButtonItems([youtbBarBtn, twitterBarBtn, fbBarBtn], animated: false)
Now I want the same buttons on all navigations bars. I can easily copy this code and respective target methods in viewDidLoad() of each view controller, but too much code is getting repeated. So how can avoid this situation?
I am using Swift 3. I am new to iOS. Any help will be appreciated!
Most duplications are solved by using functions. The first step is to extract that code into a function, the second step is to use the same function from multiple places.
You can add it to an extension, for example:
extension UIViewController {
func addShareButtons() {
...
self.navigationItem.setRightBarButtonItems([youtbBarBtn, twitterBarBtn, fbBarBtn], animated: false)
}
}
and only call
self.addShareButtons()
from every controller that needs the buttons.
You can add button handlers to the extension too.
Another method is to use a UIViewController subclass but that's always a problem if you want to use a UITableViewController subclass.
You can design a custom navigation controller and add these code to the navigation controller. inherit the navigation controller to your storyboard or programmatically where you want to use.
//sample code
class "YourNavigationCorollerName": UINavigationController, UINavigationControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
//declare here you code, what you want
}
}
Assuming that these buttons ALWAYS have the same behavior, you can create a custom class for them, and place the repeated code there. For instance:
class FacebookButton : UIButton {
override init() {
super.init()
self.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
self.setImage(UIImage(named: "Facebook.png")!, for: .normal)
}
}
...I think the compiler will yell at you for initializing a UIView without a decoder and frame, but hopefully you get the idea.
To improve on this, you could 1) create a custom UINavigationController with the UIBarButtonItems you want to use, and reuse that controller, and/or 2) create a protocol like the following:
protocol FacebookBtnHandler {
func fbBtnPressed()
}
...and have any relevant VCs conform to it. This would allow you to assign the target and selector for the button in the FacebookButton init method, where you assign the image and frame, and hence prevent repetition of that line, as well.
Try to implement bar button by creating subclass of UIBarButton
class Button: UIBarButtonItem {
convenience init(withImage image : UIImage,Target target: Any, andSelector selector: Any?){
let button = UIButton(type: .custom)
button.setImage(image, for: UIControlState.normal)
button.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
button.addTarget(target, action: selector, for: UIControlEvents.touchUpInside)
self.init(customView: button)
}
}
let fbImage = UIImage(named: "Facebook.png")!
let twitterImage = UIImage(named: "Twitter.png")!
let youtbImage = UIImage(named:"YouTube.png")!
let fbBtn = Button(withImage: fbImage, Target: self, andSelector: #selector(HomeViewController.fbBtnPressed))
let twitterBtn = Button(withImage: fbImage, Target: self, andSelector: #selector(HomeViewController.twitterBtnPressed))
let youtubeBtn = Button(withImage: fbImage, Target: self, andSelector: #selector(HomeViewController.youtubeBtnPressed))
self.navigationItem.setRightBarButtonItems([youtbBarBtn, twitterBarBtn, fbBarBtn], animated: false)
and make it for all your view controller
extension UIViewController {
func addButtons() {
// add above code
}
}

Swift: Calling UIButton press from another class

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

Resources