Swift - UIView Delegate Protocol not returning value - ios

I am trying to apply the colour picker from the question below.
Simple swift color picker popover (iOS)
The way I set this up was the colour picker class is attached to a UIView within the main view controller. The code by Michael Ros works but when I try to access it using my main view controller nothing happens. Below is the code I use in my view controller. Is this correct? I went over other questions and I am not sure what I am doing wrong.
class ViewController: UIViewController {
weak var colorPickerDelegate: ColorPickerDelegate?
override func viewDidLoad() {
super.viewDidLoad()
self.colorPickerDelegate = self
}
}
}
extension ViewController: colorPickerDelegate {
func colorChanged(color: UIColor) {
print(color)
}
}
The color picker code can be found on the attached question as I wasn't sure if it was allowed to copy the code over.
Thanks for the help

You should sublcass UIView and assign it to the view controllers view, then set the delegate of the ColorPickerView to the view controller:
ColorPickerView.swift
class ColorPickerView : UIView {
weak var delegate: ColorPickerDelegate?
// Other code from Michael Ros's adaptation.
}
ViewController.swift
import UIKit
class ViewController: UIViewController {
lazy var colorPickerView = ColorPickerView(frame: CGRect(origin: .zero, size: CGSize(width: self.view.frame.width, height: 300)))
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .white
self.colorPickerView.delegate = self
self.view.addSubview(colorPickerView)
}
}
extension ViewController: ColorPickerDelegate {
func colorDidChange(color: UIColor) {
print(color)
}
}

you should replace
colorChanged -> colorDidChange !
if you delegate is ColorPickerDelegate; I think so

Related

delegate method not getting called with UITabBarController

In FourthViewController, I have a slider, which has values ranging from 1 to 1000. The value that is set gets sent via the delegate to PatternViewController, where it should be used to do sth (I put the print for testing purposes).
I've worked with delegates before and it was all ok, checked the code multiple times and multiple answers here on stack, I can't seem to find the issue. Any help would be much appreciated
update: I have added a button so that it would be easier to track along. It turns out that by pressing first time the button, nothing happens. but if I first checkout the PatternViewController, then I go back to FourthViewController and press the button, the delegate gets triggered. anyone got any idea on why is this happening?
FourthViewController
import UIKit
class FourthViewController: UIViewController {
//MARK: Outlets
#IBOutlet var persistenceButton: UIButton!
#IBOutlet var persistenceSlider: UISlider!
#IBOutlet var persistenceLabel: UILabel!
weak var delegate: FourthViewControllerDelegate?
//MARK: Stored Properties - Constants
let userDefaults = UserDefaults.standard
let keyName = "sliderValue"
//MARK: Initializer
override func viewDidLoad() {
super.viewDidLoad()
loadSliderValue()
initialSetUp()
}
//MARK: Actions
#IBAction func handleValueChanged(_ sender: UISlider) {
updateLabel()
persistSliderValue(value: persistenceSlider.value, key: keyName)
}
//MARK: Methods
func updateLabel() {
persistenceLabel.text = String(format: "%.2f", persistenceSlider.value)
}
func persistSliderValue(value: Float, key: String) {
userDefaults.set(value, forKey: key)
}
func loadSliderValue() {
let persistedValue = userDefaults.float(forKey: keyName)
persistenceSlider.value = persistedValue
updateLabel()
}
}
func initialSetUp() {
persistenceButton.addTarget(self, action: #selector(handleButtonPressed), for: .touchUpInside)
}
#objc func handleButtonPressed() {
delegate?.valueChanged(value: persistenceSlider.value)
}
}
PatternViewController
import UIKit
class PatternViewController: UIViewController, FourthViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
setUp()
}
func setUp() {
if let tabBar = self.tabBarController, let viewController = tabBar.viewControllers, let fourthViewController = viewController[3] as? FourthViewController {
fourthViewController.delegate = self
}
}
func valueChanged(value: Float) {
print(value)
}
}
It depends upon how you instantiated the tab view controller. If you do it with storyboards, for example, the view controllers for the respective tabs are instantiated lazily, only instantiated as the user taps on them. (This helps reduce latency resulting from instantiating all four of the tabs’ view controllers.)
While you theoretically could go ahead and have the tab bar controller instantiate the four view controllers programmatically up front, rather than just-in-time via the storyboard, I might instead consider specifying a UITabBarControllerDelegate for the tab bar controller. Have the tab bar controller’s delegate method update the relevant tab’s view controller’s model.
Here is an example with two tabs, the first has a slider and the second has a label that displays the slider’s value. In this simplified example, I’ve moved the model object (the value associated with the slider) into the tab bar controller, and it passes it to the second view controller when you select the associated tab.
// TabViewController.swift
import UIKit
class TabBarController: UITabBarController {
var value: Float = 0.5
override func viewDidLoad() {
super.viewDidLoad()
delegate = self
}
}
// MARK: - UITabBarControllerDelegate
extension TabViewController: UITabBarControllerDelegate {
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
guard let viewController = viewController as? SecondViewController else { return }
viewController.value = value
}
}
And
// FirstViewController.swift
import UIKit
class FirstViewController: UIViewController {
#IBOutlet weak var slider: UISlider!
override func viewDidLoad() {
super.viewDidLoad()
guard let tabBarController = tabBarController as? TabViewController else { return }
slider.value = tabBarController.value
}
#IBAction func didAdjustSlider(_ sender: UISlider) {
guard let tabBarController = tabBarController as? TabViewController else { return }
tabBarController.value = sender.value
}
}
And
// SecondViewController.swift
import UIKit
class SecondViewController: UIViewController {
#IBOutlet weak var label: UILabel!
var value: Float = 0 { didSet { updateLabel() } }
let formatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .percent
return formatter
}()
override func viewDidLoad() {
super.viewDidLoad()
updateLabel()
}
func updateLabel() {
label?.text = formatter.string(for: value)
}
}
Probably needless to say, I not only set the base view controller class for the two tab’s view controllers, but also set the base class for the tab bar controller’s storyboard scene to the above TabBarController.

Implement functionality for all the UIViewControllers using Swift

Is there a way to implement functionality for all the UIViewControllers ?
I'm trying to extend a specific behavior for all the UIViewControllers, is very standard.
For example:
MyBaseClass
class MyBaseClass {
public func load(viewController:UIViewController){
print("The size of the view is \(viewController.view.size.width)")
}
}
I have to implement a similar behavior among 100 UIViewController
One way is to use protocols in combination with an extension to UIViewController.
Define your behavior as a protocol and extend UIViewController to implement it:
protocol WidthProtocol {
func showWidth()
}
extension UIViewController: WidthProtocol {
func showWidth() {
print("The width of the view is \(self.view.frame.size.width)")
}
}
Once you have that in place, you can call your function from any subclass of UIViewController, such as:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Show the width
showWidth()
}
}
Or if you don't want to call a the same function in all your view controller you can just subclass it:
class YourCustomViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
print("The size of the view is \(view.frame.width)")
}
}
then just replace UIViewController by YourCustomViewController:
class oneOfYour100Controler : UIViewController {
to something like that:
class oneOfYour100Controler : YourCustomViewController {
Then you don't need to do anything, the message will be print automatically.

Swift Call component from UIViewController in a UIView [duplicate]

This question already has answers here:
What does "Fatal error: Unexpectedly found nil while unwrapping an Optional value" mean?
(16 answers)
Closed 6 years ago.
I'm having a problem, I have a UIButton in a UIViewController class and I want to enable that button after an animation that happens in a UIView class that is in another file.
class MainViewController: UIViewController {
#IBOutlet weak var nextButton: UIButton!
#IBAction func nextButtonPressed(sender: UIButton) {
nextButton.enable = false
}
}
When I try to call the nextButton from the viewController class after the animation is done I get this error:
EXC_BAD_INSTRUCTION(code = EXC_I386_INVOP, subcode = 0x0)
I get the error on the line where I set the nextButton enable to true.
class CustomView: UIView {
var vc = MainViewController()
func animationEnded() {
vc.nextButton = true
}
}
I don't have a clue what I'm missing and I would appreciate some help. Thanks
You encounter an error because in your CustomView you create a new MainViewController, you're not using the one initialized from the storyboard. That new MainViewController doesn't have any of its properties initialized, so nextButton is nil, hence the crash when you try to access it.
What you want to do is notify your controller from the view that the animation has ended so that the controller can update the button (since the controller owns the button). The standard way to do this in Cocoa is to use the delegate pattern like so:
class MainViewController: UIViewController, CustomViewDelegate
{
#IBOutlet weak var nextButton: UIButton!
#IBOutlet weak var customView: CustomView!
#IBAction func nextButtonPressed(sender: UIButton) {
self.nextButton.enabled = false
}
override func awakeFromNib() {
super.awakeFromNib()
self.customView.delegate = self
}
func customViewAnimationDidEnd(customView: CustomView) {
self.nextButton.enabled = true
}
}
protocol CustomViewDelegate : class
{
func customViewAnimationDidEnd(customView: CustomView)
}
class CustomView: UIView
{
weak var delegate: CustomViewDelegate? = nil
func animationEnded() {
self.delegate?.customViewAnimationDidEnd(self)
}
}
In this implementation the controller is the view delegate and gets notified when interesting events happen in the view (like a particular animation ending).
Make a delegate in your UIView that tells when it should hapen
protocol CustomViewDelegate {
func pushThatButton()
}
in CustomView class put this:
weak var delegate: CustomViewDelegate?
then
func animationEnded() {
delegate.pushThatButton()
}
and in UIViewController
class MainViewController: UIViewController, CustomViewDelegate {
and implement delegate ofc
func pushThatButton()
nextButton.sendActionsForControlEvents(.TouchUpInside)
}
almost forget, do an outlet to your view in viewController and setup delegate! in viewDidLoad() or when you will find this best
customViewOutlet.delegate = self

update value of text when click back on navigation controller

I have 2 controllers
and have got 1 global variable, the problem is if I go to controller 2 and click on button northAmericaClick, it will navigate back to control 1, but the value of global variable won't change!
this is my code
controller 1
class OurViewController: UIViewController {
#IBOutlet weak var menuButton: UIBarButtonItem!
#IBOutlet weak var selectedServer: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
selectedServer.setTitle(selected server, forState: UIControlState.Normal) // selected server this is global variable
}
controller 2
class selectServerController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func northAmericaClick(sender: AnyObject) {
selectedserver = "North America"
self.navigationController?.popViewControllerAnimated(true)
}
From
You need to use a delegate. Here is an example how do use a delegate in Swift.
On your first ViewController, set your delegate when you load the second VC:
For example, if you are using the Storyboard Editor:
var secondViewController = (segue.destinationViewController.visibleViewController as MySecondViewControllerClass)
secondViewController.delegate = self
Write a Protocol and define a func to write you values back
For example, create a file called "Protocol.swift" and write something like that:
protocol writeValueBackDelegate {
func writeValueBack(value: String)
}
Add the function to your FirstViewController
func writeValueBack(value: String) {
// this is my value from my second View Controller
}
And to your ViewControllerClass
class ViewController: UIViewController, writeValueBackDelegate
Go to the Second View Controller, and add the delegate here:
class SecondViewController: ViewController {
// delegate for FirstViewController
var delegate: writeValueBackDelegate?
On your Second View Controller, you can now use this to call the func in the first View Controller an pass data.
delegate?.writeValueBack("That is a value")
You also need to indicate that your first view controller implements the protocol: class ViewController: UIViewController, writeValueBackDelegate {
A part of doing it with delegate you also can create singleton class ViewControllersDataModel class and share the variable using it:
import Foundation
class ViewControllersDataModel {
static let sharedInstance = ViewControllersDataModel()
var selectedserver: String = ""
private init() {
}
}
And call it like this:
ViewControllersDataModel.sharedInstance.selectedserver = "Selected Option";
Ok, I can do this with this code, only check when viewWillDisapear and call the parent of this view controller in the navicationController:
override func viewWillDisappear(animated: Bool) {
if ((self.navigationController!.viewControllers.last?.isKindOfClass(ActivityMyViewController)) == true){
let backView:MyViewController = self.navigationController!.viewControllers.last as! MyDetailViewController
backView // do whatever you want
}
}
I hope this code can help you, good luck
thanks guys for helping ;)
it was very simple
i just use then when it comeback ^^"
override func viewWillAppear(animated: Bool) {
selectedServer.setTitle(selectedserv, forState: UIControlState.Normal)
}

Accessing IBOutlet from another class

I am calling a class function from my ViewController class like this:
Buttons.set_cornerRadius(10)
I have another .swift file where I have the function declared:
class Buttons {
class func set_cornerRadius(radius: CGFloat) {
ViewController().someButton.layer.cornerRadius = radius
}
}
When I'm trying to run this it throws the error: "Unexpectedly found nil while unwrapping an optional Value".
I checked the Storyboard-IBOutlet connections already. Everything is connected right.
If I call the method in the same class like this, everything works:
class ViewController: UIViewController {
#IBOutlet weak var someButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
set_cornerRadius(10)
}
func set_cornerRadius(radius: CGFloat) {
someButton.layer.cornerRadius = radius
}
}
How can I fix this? What am I doing wrong/not understanding right?
Thanks in advance.
You access a generic ViewController, but need to use an existing UIView. Do something like this:
class Test: UIViewController {
class func set_cornerRadius(yourView: UIView, radius: CGFloat) {
yourView.layer.cornerRadius = radius
}
}
That way, you pass the UIView you want to set the corner-radius.
You extend your ViewController class like so:
extension ViewController {
func set_cornerRadius(radius: CGFloat) {
someButton.layer.cornerRadius = radius
}
}
Now you can call this method in your original ViewController file using: set_cornerRadius(someValue) in your viewDidLoad or wherever you want. You can put this extension in a different file.

Resources