I have a method wiche is bool and returns the value of YES. This method is declared in BViewController, is there a way when I access this method in CViewController and set the value of to NO ?
-(BOOL)prefersNavigationBarHidden {
return YES;
}
Something like that maybe ?
[self prefersNavigationBarHidden:NO ];
I know that I could do something like that :
-(BOOL)prefersNavigationBarHidden{
return NO;
}
But this method has to be triggered on a specific event
thanks for help and fast answer
At the moment, your function is hard-coded and always returns YES. You will need to store the value somewhere, if you want to be able to change it, either from inside view controller B or from other places.
You can store it as a property instead:
#property (assign, nonatomic) BOOL prefersNavigationBarHidden;
And expose it on the .h file of view controller B. This way view controller C will have access to it, and will be able to change it.
It depends on the context in which you want to use that method. If it's a method that you use to tell a view controller if it can display or not the navigation bar, one option is create a superclass, from which view controller C inherits, and define that method there. Then, override that method in your view controller C which should inherit from the superclass. Like this:
#interface MySuperclass : UIViewController
- (BOOL)prefersNavigationBarHidden;
#end
#implementation MySuperclass
-(BOOL)prefersNavigationBarHidden {
return YES;
}
/*
Use the value returned by the method to achieve what you want
with that boolean value
*/
#end
//--------------------
#interface ViewControllerC : MySuperclass
#end
#implementation ViewControllerC
- (BOOL)prefersNavigationBarHidden {
return NO; // return no for example
}
#end
It may not be your case, but it's for you to get an idea. As I told you, it depends on what you are trying to achieve.
Related
I have the following protocol:
#protocol CardTransferFundsDelegate <NSObject>
#property (strong, nonatomic) id<CardTransferFundsView> view;
#end
I have the following in the .h file:
#interface CardTopViewModel : NSObject<CardTransferFundsDelegate>
- (instancetype)initWithVirtualCard:(Card *)card;
#end
In my .m I have the following:
#interface CardTopUpViewModel()
#property (strong, nonatomic) Card *card;
#end
#implementation CardTopUpViewModel
-(instancetype)initWithCard:(Card *)card {
self = [super init];
if(self){
self.card = card;
}
return self;
}
- (id <CardTransferFundsView>)view {
return nil;
}
- (void)setView:(id <CardTransferFundsView>)view {
self.view = view;
}
#end
The issue I have is whenever I call setView it then calls self.view which then calls the getter method and I have a program that just keeps running infinitely.
So my question is, How do I fix this issue, such that the protocol still keeps the property but I am able to get and set the view without having the program run infinitely?
You have implemented the setter, setView, in terms of itself, set.view, so it will recurse until the call stack overflows.
To implement the property specified in your protocol you need three things:
Storage - an instance variable in which to hold the properties value
A setter
A getter
First add an instance variable, say myView:
#implementation CardTopUpViewModel
{
id <CardTransferFundsView>) myView; // storage for view property
}
Now your setter becomes:
- (void)setView:(id <CardTransferFundsView>)view
{
myView = view;
}
And the getter is similarly trivial.
Or of course you can just skip all this and use a property declaration in your #interface which matches that in the protocol and the compiler will provide the storage, setter and getter automagically.
HTH
You should not call self.view = view will call the setter method again and again resulting infinite call. That moto is do not call setter method in with in setter method (setting property will call it again). So Issue in below lines. so view is already a property of viewcontroller. - (void) setView:(id)view is setter method for the view Property. try use another name.
Basically you should be very careful while creating method start with set. Usually try to use method name which starts with any verb as it does some action.
- (void)setView:(id <CardTransferFundsView>)view {
self.view = view;
}
create any property of id <CardTransferFundsView>) with other name and use with in setter method in place of self.view = view.
EDIT: edited for clarity
Disclaimer: I'm new and pretty bad. But I have tried very hard and read lots of stuff to figure this out, but I have not...
I think my whole delegate pattern would work, except I can't figure out how to set the delegate property of ViewController to self in the MatchLetter class. The reason is because I can't figure out how to call code there. It's not a view controller, so viewDidLoad or prepareForSegue won't work.
This is what I've got:
ViewController.h
#import <UIKit/UIKit.h>
#class ViewController;
#protocol letterMatchProtocol <NSObject>
- (BOOL) isLetterMatch:(char) firstLetter;
#end
#interface ViewController : UIViewController
#property (nonatomic, weak) id <letterMatchProtocol> delegate;
#end
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
char c = 'a';
// This is the method I want to delegate to MatchLetter, to have a BOOL returned
BOOL returnValue = [self.delegate isLetterMatch:c];
}
#end
MatchLetter.h
#import <Foundation/Foundation.h>
#import "ViewController.h"
#interface Delegate : NSObject <letterMatchProtocol>
#end
MatchLetter.m
#import "MatchLetter.h"
#implementation Delegate
// this is the code I think I need to run here, to set the delegate property...
// ViewController *viewController = [ViewController new];
// viewController.delegate = self;
// ... so that isLetterMatch can be run here from ViewController.m
// But I don't know where to put this code, or how to get it to run before the ViewController
// especially since there are no segues or views to load.
- (BOOL) isLetterMatch:(char)firstLetter {
if (firstLetter == 'a') {
return YES;
}
else {
return NO;
}
}
#end
Can somebody please tell me the best way to proceed? Thanks for reading
You asked "Where to set delegate = self? Or should I just use a different design pattern?".
Answer: Don't. An object should never be it's own delegate.
Your code is quite a mess.
Don't name a class "Delegate". A delegate is a design pattern. The whole point of a delegate is that any object that conforms to a particular protocol ("speaks the language") can serve as the delegate. You don't need to know what class of object is serving as the delegate, but only that it speaks the language you need.
An analogy: When you call the operator, you don't care who is working the operator desk. You don't care about his/her gender, religion, ethnic background, how tall they are, etc. You just care that they speak your language.
Likewise, when you set up a delegate, it doesn't matter what type of object gets set as the delegate. All that matters is that the object that is the delegate conforms to the protocol for that delegate.
A table view can have ANY object serve as it's delegate, as long as that object conforms to the UITableViewDelegate protocol. You usually make you view controller be the table view's delegate, but you don't have to. You could create a custom class that manages your table views, and have it be the delegate. There is no "TableViewDelegate" object class. There is instead a UITableViewDelegate protocol, and any object that conforms to the protocol can act as a table view's delegate.
Edit: Your question is confusing. I think what you're proposing is that your Delegate class would create a view controller and make itself the delegate for the view controller.
If that's what you are talking about, your thinking is backwards. The view controller is using the Delegate class as a helper class. Any given instance of a view controller class can create an instance of the Delegate class and set it as it's delegate if it desires. You might have 3 instances of ViewController at one time, each with it's own instance of your Delegate class.
Thus, the ViewController object is the one that should create and set up an instance of Delegate if it needs one:
- (void) viewDidLoad;
{
self.delegate = [[Delegate alloc] init];
//other setup here
}
I have a class that subclasses UITableViewController. Based on user actions that are recognized in this class, I need to call a method on a table in the UIViewController were the table is instantiated. I can't figure out how to do this.
I tried to make the function static, but that won't work since there is an instance variable that I need to reach. I could probably use NSNotificationCenter but my intuition is that there is a better way. Can someone help? Thanks!
MonthsTableViewController.h
#interface MonthsTableViewController : UITableViewController <UITableViewDataSource, UITableViewDelegate>
{
NSArray *monthsArray;
}
#end
MonthsTableViewController.m
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
NSLog(#"calling the UIViewController");
//this is where I am stuck!!!
}
SubscribeViewController.h
#interface SubscribeViewController : UIViewController <SIMChargeCardViewControllerDelegate>
{
MonthsTableViewController *monthsController;
IBOutlet UITableView *monthsTable;
}
- (void) snapMonthsToCenter;
#end
SubscribeViewController.m
- (void) snapMonthsToCenter {
// snap the table selections to the center of the row
NSLog(#"method called!");
NSIndexPath *pathForMonthCenterCell = [monthsTable indexPathForRowAtPoint:CGPointMake(CGRectGetMidX(monthsTable.bounds), CGRectGetMidY(monthsTable.bounds))];
[monthsTable scrollToRowAtIndexPath:pathForMonthCenterCell atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
}
Basically in order to do this, you need a reference to your UIViewController from your UITableViewController. This will allow you to call the methods of this object. Typically you would call this property a delegate, because you're assigning the "parent" UIViewController as the delegate of the "child" UITableViewController.
Modify your UITableViewController (MonthsTableViewController.h) to add a delegate property like so:
#interface MonthsTableViewController : UITableViewController <UITableViewDataSource, UITableViewDelegate>
{
NSArray *monthsArray;
id delegate;
}
#property (nonatomic, retain) id delegate;
#end
You will need to #synthesize the property in your .m file. You'll also want to import SubscribeViewController.h in your header here, if you haven't already.
Then, when you instantiate your MonthsTableViewController, set the delegate to your current object MonthsTableViewController like so:
MonthsTableViewController *example = [[MonthsTableViewController alloc] init.... // This is the line you should already have
[example setDelegate:self]; // Set this object's delegate property to the current object
Now you have access to the parent SubscribeViewController from your MonthsTableViewController. So how do you call functions? Easy! You can either hardcode the method call, or, to be super safe, use respondsToSelector::
[(MonthsTableViewController*)[self delegate] snapMonthsToCenter];
In your case, the above code is absolutely fine, because you know that this method will always exist on this object. Typically, however, delegates are declared as protocols that may have optional methods. This means that although methods are declared in the #interface, they may not actually exist (be implemented) in the object. In this case, the following code would be used to make sure that the method can actually be called on the object:
if([[self delegate] respondsToSelector:#selector(snapMonthsToCenter)]) {
[[self delegate] snapMonthsToCenter];
}
I've done some reading on here about passing data back from view controllers and I know that there are some definitely no-no's about this, but I wanted to inquire about a best practice for passing data in this manner and ask if what i'm doing below is ok?
I have 3 view controllers (classes ViewControllerA, B, and C) and when the user clicks a button (from either A, or B) it takes them to C using this:
let vc = new ViewControllerC()
self.presentViewController(vc, animated: true, completion: nil)
Passing data forward
So 'self' above is either A, or B. My thought was if I wanted to send C some info, I could just make a property of C (such as a string or int, etc, etc) and assign it above just before I do a presentViewController. Is that right?
i.e.
vc.propertyHere = "my value"
Passing Data Back
if something happened in C that I'd like to send back to A or B, what is the best practice?
To return back to A or B I know I can do this:
self.dismissModalViewControllerAnimated(true)
Should I first instantiate A or B (maybe by their base type) and access a similar property for the information I'd like to pass back? For example if A and B both inherit from some class, I could make a property on that class and just access / set it from C. Is that the right move? Or is there a better approach?
i.e.
callingViewController.myProperty = "my value i'm passing back"
Thanks!
Passing Data forward - Your implementation is good.
Passing Data Back -
Dont initiate A and B again, its a common mistake I see with new developers. A or B already exists, you came to this new controller from A/B and you just want to pass a value back to that class.
The best practice to use here is delegate design pattern.
Steps:
Create a protocol declaring the a method to call in A/B.
Implement a property in C named delegate.
Before presenting the view C, assign the delegate.
Whenever you want to pass any info to A or B call the delegate method.
Implementing delegate is explained in another SO answer here : How do I create delegates in Objective-C?
For downhill theres technically nothing wrong with your proposal of setting a property on the view controller
vc.propertyHere = "my value"
but theres a pattern called Dependancy Injection which provides a much cleaner conversation between the parent and the child.
So if your new view controller requires something to function you give it to the instance at init.
import Foundation
import UIKit
class ObjectTypeA {}
class ObjectTypeB {}
class FooVC : UIViewController {
var importantThing:ObjectTypeA!
var otherImportantThing:ObjectTypeB!
init(somethingINeed:ObjectTypeA, somethingElseINeed:ObjectTypeB) {
importantThing = somethingINeed
otherImportantThing = somethingElseINeed
super.init(nibName:nil,bundle:nil)
}
}
class BarVC : UIViewController {
func yo() {
let object1 = ObjectTypeA()
let object2 = ObjectTypeB()
let newVC = FooVC(somethingINeed: object1, somethingElseINeed: object2)
self.presentViewController(newVC, animated: true, completion: nil)
}
}
So keep doing what you are doing but look at other patterns in the future.
For passing data or events uphill a delegate pattern as described by others is best. That way there is no tight coupling between the parent and child. Any object which adopts the protocol can be the delegate.
e.g
class BlueSuedeShoes{}
protocol FunkyProtocol {
func danceLikeElvis(shoes:BlueSuedeShoes)
}
class LumpyVC:UIViewController {
var delegate:FunkyProtocol?
func somethingHappened() {
let myShoes = BlueSuedeShoes()
self.delegate?.danceLikeElvis(myShoes)
}
}
and a class which implements that protocol
class SmoothVC:UIViewController,FunkyProtocol {
func danceLikeElvis(shoes: BlueSuedeShoes) {
dontStepOn(shoes)
}
func dontStepOn(shoes:BlueSuedeShoes) {
}
}
You can use delegation pattern to achieve this. Here is an little example ,please see.
#protocol SecondViewControllerDelegate;
#interface SecondViewController;
SecondViewController : UIViewController
#property (nonatomic, assign) id<SecondViewControllerDelegate> delegate;
#property (nonatomic, retain) NSArray* someArray;
#end
#protocol SecondViewControllerDelegate
- (void)secondViewControllerDidFinish:(SecondViewController*)secondViewController;
#end
SecondViewController.m:
#implementation SecondViewController
#synthesize delegate;
#synthesize someArray;
- (void)dealloc
{
[someArray release];
[super dealloc];
}
- (void)someMethodCalledWhenUserIsDone
{
[delegate secondViewControllerDidFinish:self];
}
FirstViewController.h:
#import SecondViewController
#interface FirstViewController : UIViewController <SecondViewControllerDelegate>
{
...
}
#end
FirstViewController.m:
#implementation FirstViewController
- (void)secondViewControllerDidFinish:(SecondViewController*)secondViewController
{
NSArray* someArray = secondViewController.someArray
// Do something with the array
}
#end
I am looking for some additional explanation/insight as to how protocol's and delegation work in Objective-C. I have an app that I am working on, which is using a UINavigationController. There is a main page and a settings page, which will allow a user to input some text that will be used as the main pages' Title. I have everything implemented and working, but I just need some clarifications on how it is working.
Here is an example of how things are set up:
#interface MainPageViewController : UIViewController
#end
#interface MainPageViewController() <SettingsControllerDelegate>
// properties
#end
#implementation MainPageViewController
- (void)methodThatSetsTitle(NSString *)title
{
self.title = title;
}
#end
.....
#protocol SettingsControllerDelegate <NSObject>
{
- (void)methodThatSetsTitle(NSString *)title
}
#interface SettingsViewController
#property (weak, nonatomic) id <SettingsControllerDelegate> delegate;
#end
#interface SettingsViewController ()
// properties that will be used for a text field and holding an NSString
#end
#implementation SettingsViewController
- (void)methodThatPassesStringToDelegateProtocolMethod
{
// Code that will set the property for the NSString title
[self.delegate methodThatSetsTitle:self.titleNameProperty];
}
#end
My question is: How is the NSString title from the SettingsViewController actually getting passed to the MainViewController? My thinking is that the 'delegate' property is declared as a SettingsControllerDelegate so it inherently can hold information from the method's that the protocol has. Then obviously in the MainViewController I call that same protocol method, which will just take the parameter and set the current Navigation title to it. It is just a little confusing as to where that parameter and method information is stored for that other method call to take it. Does every time I call the SettingsViewController method, '- (void)methodThatPassesStringToDelegateProtocolMethod', just call the method in the MainViewController?
(Also in my code I have a prepareForSegue method that set's the SettingViewController.delegate to self.)
Any clarification as to how this information is passed and the details as to how it works would be great! I can understand the complexities, but if you can explain it in a way that is holistic and easy to grasp that'd be great. I can understand memory models and such as well so an explanation as to how this would work in memory would be very useful.
Thank you very much!
I think the main thing that you may be looking for is - what exactly is the delegate property? The declaration
id<SettingsViewControllerDelegate> delegate;
says that you are declaring an object (id) that conforms to the SettingsViewControllerDelegate protocol - meaning that it implements the methodThatSetsTitle: method. This can be any object, as long as it conforms to that protocol. So when you do this:
[self.delegate methodThatSetsTitle:self.titleNameProperty];
you are sending a message to that object, whatever it is, to do something with the given NSString.
In your particular case, you are using the Main Page View Controller as the delegate, so the above line of code sends a message from the Settings View Controller to the Main Page View Controller to set its title to the string you are sending as an argument.
In terms of memory, think of this as you would with any other "normal" instance method. The delegate in this case is the Main Page View Controller, so that is presumably on the navigation stack.
Hope this helps!