I have a viewController with another containerView insider set up to appear temporarily (added programmatically). The containerView is a sort of operation bar, which allows you to change values of the viewController. The protocol called from an IBAction of a button however, does not call the protocol set up inside the viewController class.
Here is the code from both classes:
class viewController: UIViewController, updateListDelegate {
let dataSource = containerView()
override func viewDidLoad() {
super.viewDidLoad()
dataSource.delegate = self
}
func updateList(sender: containerView) {
print("is called") //is not printed
}
}
The code from the containerView:
protocol updateListDelegate {
func updateList(containerView)
}
class containerView: UIViewController {
var delegate: updateListDelegate?
#IBAction func AddSong(_ sender: UIButton) {
self.delegate?.updateList(sender: self)
}
}
If this method is only to be called from one object, then, in my opinion, I would not define a protocol. If multiple objects are to call this method, then I would define a protocol. This is typically how you would call a method backwards, using a basic delegate.
class ViewController: UIViewController {
let container = ContainerView()
override func viewDidLoad() {
super.viewDidLoad()
container.viewControllerDelegate = self
// push to this instance of container at some point
}
func doSomething() {
print("great success")
}
}
class ContainerView: UIViewController {
weak var viewControllerDelegate: ViewController?
#objc func someAction() {
if let viewControllerDelegate = viewControllerDelegate {
viewControllerDelegate.doSomething()
}
}
}
// prints "great success" when someAction() called
One of the most common mistakes people make is not keeping track of instances. For delegates to work, you must be sure you are using the specific instances that you've instantiated and assigned those delegates to.
Related
I need to post a notification on viewDidLoad of every one of my ViewControllers. I have a BaseViewController with a postNotification method, and it gets an enum as a parameter to identify the screen. It looks like this
class BaseViewController {
func postNotification(for screen: ScreenName) {
NotificationCenter.default.post(name: notification,
object: nil,
userInfo: ["ScreenName": screen])
}
}
class AViewController: BaseViewController {
override func viewDidLoad() {
super.viewDidLoad()
postNotification(for: screenA)
}
}
class BViewController: BaseViewController {
override func viewDidLoad() {
super.viewDidLoad()
postNotification(for: screenB)
}
}
Suppose we need to add another view controller later in the future, such as CViewController. I want to force the developer of CViewController to call this postNotification method with screen enum.
What is the best practice to achieve this on Swift?
Edit
Thanks to Loren's suggestion, I added a protocol on my base class
typealias BaseController = BaseViewController & BaseProtocol
protocol BaseProtocol {
var screenName: ScreenName { get }
}
This forces all my viewcontrollers to conform protocol and initialize screenName, but now I can't get it from my BaseViewController. If I can get child view controller's screenName property from BaseViewController, I would eliminate calling postNotification method on each child, and call it only on BaseViewController
class BaseViewController: UIViewController {
var screenName: ScreenName!
override func viewDidLoad() {
super.viewDidLoad()
if let screenName = self.screenName {
self.postNotification(for: screenName)
} else {
fatalError("screenName must be instantiated")
}
}
func postNotification(for screen: ScreenName) {
NotificationCenter.default.post(name: notification,
object: nil,
userInfo: ["ScreenName": screen])
}
}
class AViewController: BaseViewController {
override func viewDidLoad() {
self.screenName = screenA
super.viewDidLoad()
}
}
class BViewController: BaseViewController {
override func viewDidLoad() {
self.screenName = screenB
super.viewDidLoad()
}
}
Try this since you are using inheritance, have BaseViewController inherit UIViewController superclass, then create a screenName variable. In your childViewControllers instantiate the screenName before calling super.viewDidLoad(). You can use protocols like you mentioned to force the implementation of the variable, but it just seems like overkill for just one variable.
I want to trigger Navigation controller to some other screen when i press the button in UIView class. How can i do this?
//Code for UIView Class in Which Button Iboutlet is created
import UIKit
protocol ButtonDelegate: class {
func buttonTapped()
}
class SlidesVC: UIView {
var delegate: ButtonDelegate?
#IBAction func onClickFinish(_ sender: UIButton) {
delegate?.buttonTapped()
}
#IBOutlet weak var imgProfile: UIImageView!
}
//ViewController Class code in Which Button Protocol will be entertained
class SwipingMenuVC: BaseVC, UIScrollViewDelegate {
var slidesVC = SlidesVC()
override func viewDidLoad() {
super.viewDidLoad()
slidesVC = SlidesVC()
// add as subview, setup constraints etc
slidesVC.delegate = self
}
extension BaseVC: ButtonDelegate {
func buttonTapped() {
self.navigationController?.pushViewController(SettingsVC.settingsVC(),
animated: true)
}
}
A more easy way is to use typealias. You have to write code in 2 places. 1. your viewClass and 2. in your View Controller.
in your SlidesView class add a typealias and define param type if you need otherwise leave it empty.
class SlidesView: UIView {
typealias OnTapInviteContact = () -> Void
var onTapinviteContact: OnTapInviteContact?
#IBAction func buttonWasTapped(_ sender: UIButton) {
if self.onTapinviteContact != nil {
self.onTapinviteContact()
}
}
}
class SwipingMenuVC: BaseVC, UIScrollViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let slidesView = SlidesView()
slidesView.onTapinviteContact = { () in
// do whatever you want to do on button tap
}
}
You can use the delegate pattern to tell the containing ViewController that the button was pressed and let it handle whatever is needed to do next, The view doesn't really need to know what happens.
A basic example:
protocol ButtonDelegate: class {
func buttonTapped()
}
class SomeView: UIView {
var delegate: ButtonDelegate?
#IBAction func buttonWasTapped(_ sender: UIButton) {
delegate?.buttonTapped()
}
}
class ViewController: UIViewController {
var someView: SomeView
override func viewDidLoad() {
someView = SomeView()
// add as subview, setup constraints etc
someView.delegate = self
}
}
extension ViewController: ButtonDelegate {
func buttonTapped() {
self.showSomeOtherViewController()
// or
let vc = NewViewController()
present(vc, animated: true)
}
}
I have ButtonClass which has buttonTapped method implemented.
The button itself is on the view. I need to add an extra layer when the button is pressed but I don't have access to self in the ButtonClass because self is a button not the view... Basically I need to access self.view found in the viewController but that's another class. If I create an new instance like so viewController() it won't help because it wont' be the same instance.
If you're adding the buttonTapped method from the custom button class then you need to create a delegate, and call the delegate method when the user the buttonTapped method is called.
In your custom button class
protocol YourCustomButtonDelegate {
func changeSomethingInTheView()
}
class YourCustomButtonClass {
var delegate: YourCustomButtonDelegate?
func buttonTapped(_ sender: AnyObject) {
if let delegate = delegate {
delegate.changeSomethingInTheView()
}
}
}
And in the ViewController
class ViewController: UIViewController, CustomCellDelegate {
#IBOutlet weak var button: YourCustomButton!
override func viewDidLoad() {
super.viewDidLoad()
button.delegate = self
}
func changeSomethingInTheView() {
// write your code here...
}
}
I have two classes. One class is named ViewController and the other class is named TabView.
My goal is to call a function changeTab() which is inside the TabView class from the ViewController.
Somehow I am having trouble with it because everytime my delegate is nil.
Here is my code for ViewController:
protocol TabViewProtocol: class {
func changeTab()
}
class ViewController: NSViewController {
// delegate
weak var delegateCustom : TabViewProtocol?
override func viewDidLoad() {
print(delegateCustom) // outputs "nil"
}
buttonClickFunction() {
print(delegateCustom) // outputs "nil"
delegateCustom?.changeTab() // doesn't work
}
}
Here is my code for TabView:
class TabView: NSTabViewController, TabViewProtocol {
let myVC = ViewController()
override func viewDidLoad() {
super.viewDidLoad()
myVC.delegateCustom = self
}
func changeTab() {
print("test succeed")
}
}
Can someone explain me what I am doing wrong? - I am new to delegates and protocols...
You are using the delegate pattern wrongly. It is hard to tell which controller you want to define the protocol for and which one you want to adopt it - but here is one possible way.
// 1. Define your protocol in the same class file as delegate property.
protocol TabViewProtocol: class {
func changeTab()
}
// 2. Define your delegate property
class ViewController: NSViewController {
// delegate
weak var delegateCustom : TabViewProtocol?
override func viewDidLoad() {
// It should be nil as you have not set the delegate yet.
print(delegateCustom) // outputs "nil"
}
func buttonClickFunction() {
print(delegateCustom) // outputs "nil"
delegateCustom?.changeTab() // doesn't work
}
}
// 3. In the class that will use the protocol add it to the class definition statement
class TabView: NSTabViewController, TabViewProtocol {
let myVC = ViewController()
override func viewDidLoad() {
super.viewDidLoad()
myVC.delegateCustom = self
// Should output a value now
print(myVC.delegateCustom) // outputs "self"
}
func changeTab() {
print("test succeed")
}
}
you are creating a new instance in this line:
let myVC = ViewController()
you should get existing instance of your ViewController.then set
myVC.delegateCustom = self
I have a UIViewController -let's call it parentViewController - and it contains a container. This container has embeddedViewController embedded in it.
Now, my parentViewController contains a method that prints something into the console:
func printSomeData(){
print("some data")
}
embeddedViewController has a button with action asigned to it:
#IBAction func printSomething(sender: AnyObject) {
is there a way that I could call method printSomeData from printSomething in Swift?
There are couple of ways such as by implementing delegate or by posting NSNotification. Here I show the sample delegation pattern. This is exact scenario as your own controller but from this you can get some concept and if you implement this hope this will accomplished your goal.
class ParentController:UIViewController,printing {
override func viewDidLoad() {
//
}
func presentEmbadedController(){
let embadedVC = EmbadedController()
embadedVC.delegate = self
}
func printSomeData() {
print("some date")
}
}
Here is the protocol something like this
protocol printing{
func printSomeData()
}
And then the EmbadedController like this
class EmbadedController:UIViewController {
var delegate: printing?
override func viewDidLoad() {
//
}
#IBAction func printSomething(sender: AnyObject) {
if let _ = delegate{
delegate?.printSomeData()
}
}
}
You can use NSNotificationCenter.defaultCenter().addObserver(...)