Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
i was wondering how to write a protocol to pass data backwards to UITableViewController? If i write a normal protocol as one would write for UIViewController i get an error that "Type "TableViewController" does not conform to type protocol "PresentedViewControllerDelegate". "
Thanks for any help!
Only a protocol is not sufficient, you need also a delegate
Declare your protocol:
protocol PresentedViewControllerDelegate {
func method1(data:[String:AnyObject])
func method2(controller:PresentedViewController)
}
In the first method you pass a custom object (Dictionary),
in the second method the destination controller itself.
In the destination view controller PresentedViewController (the sender) create a delegate property:
weak var delegate : PresentedViewControllerDelegate?
and add code to call the methods
delegate?.method1(someDictionary)
delegate?.method2(self)
The optional chaining is very convenient, the methods aren't called if the delegate is nil.
In the source view controller (the receiver) add PresentedViewControllerDelegate to the declaration line, implement the required methods of the protocol and add a line in prepareForSegue to set the delegate.
let destinationController = segue.destinationController as! PresentedViewController
destinationController.delegate = self
Create your protocol, and have your custom UITableViewController subclass implement the protocol you created. Implement the required methods and properties in your controller and you're good to go.
Ex:
protocol YourProtocol
{
func method1(par1: String, par2: Int)
}
class YourTableViewController: UITableViewController, YourProtocol
{
//MARK:- YourProtocol Methods
func method1(par1: String, par2: Int)
{
//Receive your data through this method
//And Do your thing here
}
}
Your TableViewController have not implemented functions declared in the PresentedViewControllerDelegate protocol. Implement those functions and you'll be fine.
protocol PresentedViewControllerDelegate {
func somefunction(parameter: String)
func anotherfunction()
}
class Table: UITableViewController, PresentedViewControllerDelegate {
func somefunction(parameter: String) {
}
func anotherfunction() {
}
}
Related
This question already has answers here:
Swift protocol with "where Self" clause
(1 answer)
Forced to cast, even if protocol requires given type
(5 answers)
Closed 4 years ago.
I want to use Protocol to hide type of classes which is subclass of UIViewController. So I create a Protocol looks like this:
protocol Displayable where Self: UIViewController {
func display()
}
and the concrete class:
class DisplayableViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
}
extension DisplayableViewController: Displayable {
func display() {
_ = view
}
}
Everything goes well, until I perfom display() at runtime:
class ViewController: UIViewController {
private var displayable: Displayable!
override func viewDidLoad() {
super.viewDidLoad()
displayable = DisplayableViewController()
displayable.display()
}
}
Crashes occurs at _ = view.
Thread 1: EXC_BAD_ACCESS (code=1, address=0x0)
Remove where Self: UIViewController or make (displayable as? DisplayableViewController).display() solve this problem, but why?
And I just want subclass of UIViewController to conform it.
Here is the demo to reproduce it.
Swift version: 4.2
Make your type a composite of UIViewController and Displayable.
For example:
private var displayable: (UIViewController & Displayable)!
Here is a link to some docs that contain relevant info on composite types:
https://docs.swift.org/swift-book/ReferenceManual/Types.html
I am trying to perform a segue from another class via a class function.
I have a class called MyTableViewController. In that class I have constructed a view controller of the type AnswerViewController. A segue to this view controller is supposed to occur when a condition in the Extension : MyCell is met. The problem that I am having is that the function showNextView is not being called.
I have read posts on both Perform Segue From Another Swift File via a class function and Perform Segue from another class with helper function, but both of these create a segue before constructing the view controller (which I cannot do because I am not using storyboards and do not actually have segues, only pushViewController).
class MyTableViewController: UITableViewController {
//Construct View Controller
let answerViewController = AnswerViewController()
//Create goToNextView function which will be called in extension MyCell
func goToNextView(){
navigationController?.pushViewController(answerViewController, animated: true)
}
}
extension MyCell: YSSegmentedControlDelegate{
func segmentedControl(_ segmentedControl: YSSegmentedControl, willPressItemAt index: Int) {
tagToIndex[actionButton.tag] = index
print(tagToIndex)
//Condition To Be Met
if tagToIndex == [0:1,1:0,2:1]{
//Access function goToNextView from MyTableViewController
func showNextView(fromViewController : MyTableViewController){
fromViewController.goToNextView()
}
}
}
}
How do I call the showNextView function so that the segue occurs?
Thanks,
Nick
You can't do this that way. Your showNextView function is nested inside segmentedControl(_, willPressItemAt) - this means it is not accessible outside of it. You generally shouldn't use nested functions.
To solve your issue you should create a delegate for your cell and inform your view controller that an action has occured.
A simple example :
protocol MyCellDelegate: class {
func myCellRequestedToOpenAnswerVC(cell: MyCell)
}
class MyCell {
weak var delegate: MyCellDelegate?
// rest of your inplementation
}
Then, change segmentedControl(_, willPressItemAt) to :
func segmentedControl(_ segmentedControl: YSSegmentedControl, willPressItemAt index: Int) {
tagToIndex[actionButton.tag] = index
print(tagToIndex)
//Condition To Be Met
if tagToIndex == [0:1,1:0,2:1]{
self.delegate?.myCellRequestedToOpenAnswerVC(cell: self)
}
}
The last part happens in MyTableViewController - first, in your cellForRow method assign the view controller as delegate, something like this - cell.delegate = self, and make the view controller conform to MyCellDelegate:
extension MyTableViewController: MyCellDelegate {
func myCellRequestedToOpenAnswerVC(cell: MyCell) {
self.goToNextView()
}
}
Now, whenever the condition is met, your view controller will get informed about it and be able to act accordingly.
If you are not familiar with protocols and delegation pattern, I highly recommend reading through the docs, as it is something used extensively in CocoaTouch.
class SuperClass
{
var delegate : SuperClassDelegate?
}
protocol SuperClassDelegate
{
func doFirstAction ()
func doSecondAction ()
}
class SubClass : SuperClass , SuperClassDelegate
{
override init ()
{
super.init()
self.delegate = self
}
func doFirstAction () {}
}
class MyViewController : UIViewController
{
override func viewDidLoad ()
{
let c : SubClass = SubClass()
}
func doSecondAction ()
{
// I want to handle this action in the ViewController
}
}
So I've made a subclass that, for convenience, can act as the superclass delegate. However some of the methods in the superclass delegate are still most appropriately implemented in a view controller, meaning I don't want my subclass to implement these.
Is there a better way I can be dealing with these delegates so I can 'share' the responsibility?
You could create a multicast delegate. That way multiple objects (the subclass, the ViewController) could be delegates from the caller object. The protocol methods would be optional and then you could choose which class would implement what methods from the protocol.
Alternatively you could just create 2 protocols with 2 delegate references in the caller object. Unless there would be some specific reason you want to use a single protocol.
I read a lot about the delegates but in practice I cannot use it properly.
Description: I have A: UIViewController, B: UIView, C: UIViewController. I want to run segue from A: UIViewController to the C: UIViewController from the inside of B: UIView.
I've tried:
protocol SegueDelegate {
func runSegue(identifier: String)
}
class B: UIView { ... }
where in my A: UIViewController:
override func viewDidLoad() {
B().delegate = self
}
func runSegue(identifier: String) {
self.performSegueWithIdentifier(identifier, sender: self)
}
and trying to call it via:
#IBAction func send(sender: AnyObject) {
let a: SegueDelegate? = nil
a!.runSegue("goToMainPage")
}
but I'm sure that I do not use it properly. Can anyone help me with it? I do not want just an answer. Please describe me it concept shortly
Delegates are just a Design Pattern that you can use in a number of ways. You can look at the Apple Frameworks to see how and where to use delegates as examples. A table view delegate is probably the best known delegate in UIKit.
Delegates serve as a callback mechanism for code to communicate with an instance of an unknown class without knowing more than that that instance will respond to the methods of the delegate protocol.
An alternative to a delegate is to use a closure (what we used to call a block in Objective-C). When to use one vs. the other is a matter of taste. There are a couple of rules of thumb, like for instance outlined here.
What you are doing is, IMO, the proper way to use delegates. You separate the view functionality from the View Controller's functionalities via a delegate, and so the contract for your view is clear: the user needs to respond to the delegate method.
Your code works and is correct. I made a quick implementation here: https://github.com/kristofvanlandschoot/DelegateUsage/tree/master
The main difference from your example, and maybe that's the place where you made a mistake is the third part of your code where you should write something like:
#IBAction func send(sender: AnyObject) {
delegate?.runSegue("segueAB")
}
There are multiple errors in your code, for example:
Here you are creating a new B, and setting A as a delegate of that new instance, no the one you actually want
override func viewDidLoad() {
«B()».delegate = self
}
And here you are creating force unwrapping a nil value
#IBAction func send(sender: AnyObject) {
let a: SegueDelegate? = «nil»
«a!».runSegue("goToMainPage")
}
If what you want to do is tell A to perform a segue to C, from inside B, all you need to do is to call performSegueWithIdentifier on A
For example:
class B: UIView {
weak var referenceToA: UIViewController? = nil // set this somewhere
#IBAction func send(sender: AnyObject) {
guard let a = referenceToA else {
fatalError("you didn't set the reference to a view controller of class A")
}
a.performSegueWithIdentifier("goToMainPage", sender: self)
}
}
I have an UIViewController
class WelcomeViewController: UIViewController
and an UIView
class SignUpView: UIView
Now I want to set in my WelcomeViewController delegate of SignUpView:
protocol SegueDelegate {
func runSegue(identifier: String)
}
class SignUpView: UIView { ... }
and connect it in
class WelcomeViewController: UIViewController, SegueDelegate {
how can I set in my WelcomeViiewController those delegate? When I'm trying to set:
override func viewDidLoad() {
SignUpView.delegate = self
}
it returns me
Instance member 'delegate' cannot be used on type 'SignUpView'
how can I find a solution?
You are trying to set delegate to a class. It should be an instance of the class i.e
let signUpView = SignUpView()
signUpView.delegate = self
What would be the point in doing that? If you want to navigate from one View to another, just add that Segue in Storyboard with an Identifier, so you can call self.performSegueWithIdentifier("IdentifierOfSegue", sender: self)
Create a weak property in SignUpView of that delegate(protocol) and name it other than delegate
then you can set and use it.
I agree with the developers saying "you can just do that via segue" but
the problem is you didn't declare a delegate var in the SignUpView class
so you can implement it in the signIn , if you declared it please write the line of code for me in a comment to check it
for now ...
I can suggest that you make a subview to be a parent class then override
which method you want to call
and you need to declare the delegate var as an optional (so you won't have
a memory cycle) like the following line ...
var delegate: SegueDelegate?
Let's solve this for people in need whom could need a solution when reading this issue:
In your UIView:
class SignUpView: UIView
you need to add:
var delegate : SegueDelegate?
Now, still in your class SignUpView, you need to add the function you want to delegate, just like this:
func runSegue(identifier: String) {
delegate?.runSegue(identifier)
}
This will call your delegate:
protocol SegueDelegate {
func runSegue(identifier: String)
}
Now, in your ViewController, you should have your SignUpView somewhere (created programmatically or linked through Storyboard / XIB).
In your viewDidLoadfunction, add: signUpView.delegate = self.
Don't forget to add SegueDelegatein your class heritage.