i am using two protocol something like this:
#protocol ModalClosedProtocol <NSObject>
-(void) modalClosedGlobalProtocolMethod;
#end
and syncmlClient Protocol.
There are three classes ContactsViewController, EventViewController, ImageSettingViewController.These classes use ModalclosedProtocol and syncmlClient protocol.all these three classes expects from SettingViewController class to implement ModalClosedProtocol and syncmlClient protocol.It is just a short overview of current implementation.
In my SettingViewController i am handling table delegate method didSelectRow.for code factoring i have created a separate method which is called from didSelectRow.. like this:
-(void) checkDeviceAndHandleModelSizeForFlip:(FlipsideViewController *)flipVc orContact:(ContactsViewController *)contactVc orEvent:(EventViewController *)eventVc orImage:(ImageSettingViewController *)imageSettingVc
{
UIViewController *genericVC;
if (flipVc!=nil)
{
genericVC = flipVc;
flipVc.modalClosedProtocolDelegate = self;
flipVc.syncmlClient = self.syncmlClient;
}
else if (contactVc!=nil)
{
genericVC = contactVc;
contactVc.modalClosedProtocolDelegate = self;
contactVc.syncmlClient = self.syncmlClient;
contactVc.mainViewController = self.mainViewController;
}
else if (eventVc!=nil)
{
genericVC = eventVc;
eventVc.modalClosedProtocolDelegate = self;
eventVc.syncmlClient = self.syncmlClient;
eventVc.mainViewController = self.mainViewController;
}
else if (imageSettingVc!=nil)
{
genericVC = imageSettingVc;
imageSettingVc.modalClosedProtocolDelegate = self;
imageSettingVc.syncmlClient = self.syncmlClient;
imageSettingVc.mainViewController = self.mainViewController;
}
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
genericVC.modalPresentationStyle = UIModalPresentationFormSheet;
[self presentViewController:genericVC animated:YES completion:nil];
genericVC.view.superview.frame = CGRectInset(genericVC.view.superview.frame, 100, 50);
}
else
{
genericVC.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentViewController:genericVC animated:YES completion:nil]; //alok
}
}
basically if you see there is lot of redudancy in code in if/else clause.It would be better from code management point of view if i had a single UIViewController object.
genericVC = contactVc;//vc object passed from didSelectrow it may be contact/event/image
genericVC.modalClosedProtocolDelegate = self;
genericVC.syncmlClient = self.syncmlClient;
genericVC.mainViewController = self.mainViewController;
can some one help me regarding this problem.
Thanks
Just declare your genericVC like this:
UIViewController<ModalClosedProtocol, syncmlClient> *genericVC;
This way you let the compiler know that your genericVC View Controller conforms to the protocols ModalClosedProtocol and syncmlClient, so the methods you want to call are there.
[self checkDeviceAndHandleModelSizeForFlip:self.flipVc];//Pass contactVc, eventVc, imageSettingVc like that,
-(void) checkDeviceAndHandleModelSizeForFlip:(ViewController *)viewController
{
UIViewController *genericVC;
if (viewController!=nil)
{
genericVC = viewController;
viewController.modalClosedProtocolDelegate = self;
viewController.syncmlClient = self.syncmlClient;
viewController.mainViewController = self.mainViewController;
}
}
Related
I have a UIViewController subclass (say MyViewController).
MyViewController.h
#protocol TargetChangedDelegate
-(void) targetChanged;
#end
#interface MyViewController
#property (weak) id<TargetChangedDelegate> targetChangedDelegate;
-(void) doSomethingOnYourOwn;
#end
MyViewController.m
#implementation MyViewController <TargetChangedDelegate>
-(void) doSomethingOnYourOwn
{
// DO some stuff here
// IS THIS BAD ??
self.targetChangedDelegate = self;
}
-(IBAction) targetSelectionChanged
{
[self.targetChangedDelegate targetChanged];
}
-(void) targetChanged
{
// Do some stuff here
}
#end
Based on certain conditions a class that instantiates an instance of MyViewController may decide to set itself as the delegate or not.
Foo.m
#property(strong) MyViewController *myVC;
-(void) configureViews
{
self.myVC = [[MyViewController alloc] init];
[self.view addSubview:self.myVC];
if (someCondition)
{
self.myVC.targetChangedDelegate = self;
}
else
{
[self.myVC doSomethingOnYourOwn]
//MyViewController sets itself as the targetChangedDelegate
}
}
With reference to the code snippet above, I have the following question:
Is it a violation of MVC/delegation design pattern (or just a bad design) to say:
self.delegate = self;
There's absolutely no problem with setting the delegate to self. In fact it is a good way to provide default delegate functionality if a delegate is not set by somebody else.
Obviously, the delegate property has to be declared weak otherwise you get a reference cycle.
To expand a bit, having read the wrong answer and wrong comments above, if you allow an object to be its own delegate, your code is cleaner because you do not have to surround absolutely every single delegate call with
if ([self delegate] != nil)
{
[[self delegate] someMethod];
}
else
{
[self someMethod];
}
Its not proper way to assign self.delegate = self.
for your functionality, you can do this:
-(void) doSomethingOnYourOwn
{
// DO some stuff here
self.targetChangedDelegate = nil;
}
and when using delegate:
if(self.targetChangedDelegate != nil && [self.targetChangedDelegate respondsToSelector:#selector(targetChanged)]
{
[self.targetChangedDelegate targetChanged];
}
else
{
[self targetChanged];
}
It is bad design to set self.delegate = self; it should be another object. Delegation via protocols are an alternative design to subclassing and you can read more about delegation here:
https://developer.apple.com/library/archive/documentation/General/Conceptual/DevPedia-CocoaCore/Delegation.html
And here is more on protocols:
https://developer.apple.com/library/archive/documentation/General/Conceptual/DevPedia-CocoaCore/Protocol.html
I am a newbee in iOS development and recently run into this problem with customized transition in iOS 9.
I have an object conforms to UIViewControllerTransitioningDelegate protocol and implements animationControllerForDismissedController, something like:
#implementation MyCustomizedTransitioningDelegate
#pragma mark - UIViewControllerTransitioningDelegate
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
{
MyCustomizedTransitionAnimator *animator = [[MyCustomizedTransitionAnimator alloc] init];
animator.presenting = NO;
return animator;
}
#end
And the process that triggers the modal transition is something like:
#implementation MyViewController
#pragma mark - Initializers
+ (MyCustomizedTransitioningDelegate *)modalTransitioningDelegateSingletonInstance;
{
static MyCustomizedTransitioningDelegate *delegateInst = nil;
static dispatch_once_t onceToken = 0;
dispatch_once(&onceToken, ^{
delegateInst = [[MyCustomizedTransitioningDelegate alloc] init];
});
return delegateInst;
}
#pragma mark - UIViewController
- (void)dismissViewControllerAnimated:(BOOL)animated completion:(void (^)(void))completion;
{
[self prepareForDismissViewControllerAnimated:animated completion:&completion];
[super dismissViewControllerAnimated:animated completion:completion];
}
- (void)prepareForDismissViewControllerAnimated:(BOOL)animated completion:(dispatch_block_t *)completion;
{
self.presentedViewController.modalPresentationStyle = UIModalPresentationCustom;
self.presentedViewController.transitioningDelegate = [[self class] modalTransitioningDelegateSingletonInstance];
}
#end
Since animationControllerForDismissedController method is not called, the MyCustomizedTransitionAnimator is not created, which leads to its animateTransition not called either, which causes unexpected problem in my app. (Sorry for my poor English...)
I am also attaching the screenshot of stack trace for both iOS8 & iOS9.
In iOS 8, animationControllerForDismissedController is called after the stack trace below.
But in iOS9, transitionDidFinish is called somehow in advance, which I guess probably prevent animationControllerForDismissedController being called?
I was wondering if this is an iOS 9 bug or not. Any explanation or work around solution will be greatly appreciated!
I faced the same issue.
I hope this will help someone.
What fixed it for me is to make the object which applies UIViewControllerTransitioningDelegate protocol as variable instance to keep strong relationship with it.
I think because it gets dismissed after the view is presented first time.
I had the same issue.
Turned out I needed to set the delegate on the navigationController of the UIViewController that contains the trigger button.
Having this old code that didn't work:
UIViewController *dvc = [self sourceViewController];
TransitionDelegate *transitionDelegate = [TransitionDelegate new];
dvc.modalPresentationStyle = UIModalPresentationCustom;
dvc.transitioningDelegate = transitionDelegate;
[dvc dismissViewControllerAnimated:YES completion:nil];
I changed the first line to:
UIViewController *dvc = [self sourceViewController].navigationController;
and it worked.
Hope this helps.
You need to say something like:
MyDestinationViewController *viewController = [[MyDestinationViewController alloc] init];
MyCustomizedTransitioningDelegate *transitioningDelegate = [[MyCustomizedTransitioningDelegate alloc]init];
viewController.transitioningDelegate = transitioningDelegate;
viewController.modalPresentationStyle = UIModalPresentationCustom;
[self presentViewController: viewController animated:YES completion:nil];
Or if you're using segues, in prepareForSegue say something like:
MyDestinationViewController *toVC = segue.destinationViewController;
MyCustomizedTransitioningDelegate *transitioningDelegate = [[MyCustomizedTransitioningDelegate alloc]init];
toVC.transitioningDelegate = transitioningDelegate;
So I have my AppDelegate method trying to set an object in a ViewController method. My AppDelegate looks like this:
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
//Grab a reference to the UISplitViewController
UISplitViewController *splitViewController = (UISplitViewController *)self.window.rootViewController;
PatientDetailViewController* patientDetailViewController = [splitViewController.viewControllers lastObject];
splitViewController.delegate = patientDetailViewController;
//Grab a reference to the masterviewcontroller and get the first patient in the list.
UINavigationController *patientNavController = [splitViewController.viewControllers objectAtIndex:0];
PatientMasterTableViewController *patientMasterViewController = (PatientMasterTableViewController *)[patientNavController topViewController];
Patient* firstPatient = [[patientMasterViewController patientArray] objectAtIndex:0];
//Set it as the detailviews patient.
[patientDetailViewController setPatient:firstPatient];
//Set the detail's as the left's delegate.
patientMasterViewController.delegate = patientDetailViewController;
}
return YES;
}
and the method to set the object looks like this:
-(void)setPatient:(Patient *)patient
{
if (![self.patient isEqual:patient])
{
self.patient = patient;
//Update the UI to reflect the new patient selected from the list.
[self refreshUI];
}
}
The issue that I'm having is that the setPatient method will be called non stop until the program crashes and I have no idea why. Can anyone shed some light on this?
This:
-(void)setPatient:(Patient *)patient
{
if (![self.patient isEqual:patient])
{
self.patient = patient;
//Update the UI to reflect the new patient selected from the list.
[self refreshUI];
}
}
Should be:
-(void)setPatient:(Patient *)patient
{
_patient = patient;
[self refreshUI];
}
I have a workflow setup as follows:
Navigation Controller
-VC1
-VC2
-VC3
-PageVC
--VC4
--VC5
I pass an object for example a UIImage reference through the first controllers up to the PageVC. Once here the PageVC loads the controllers as follows:
- (void) viewDidLoad {
[super viewDidLoad];
self.dataSource = self;
_side = [self.storyboard instantiateViewControllerWithIdentifier: #"GuessGameTurnWordVC"];
_center = [self.storyboard instantiateViewControllerWithIdentifier: #"GuessGameTurnMainVC"];
[self setViewControllers: #[_center]
direction: UIPageViewControllerNavigationDirectionForward
animated: NO
completion: nil];
}
- (UIViewController*) pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController {
if ( viewController == _center )
{
_side.title = #"right";
return _side;
}
if ( viewController == _side && [_side.title isEqualToString: #"left"] )
{
return _center;
}
return nil;
}
- (UIViewController*) pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController {
if ( viewController == _center )
{
_side.title = #"left";
return _side;
}
if ( viewController == _side && [_side.title isEqualToString: #"right"] )
{
return _center;
}
return nil;
}
Question
Within the VC4/VC5 I need to access a property from the PageVC. How can I do this?
Do I need to pass the property down to these controllers too, or can I access it directly from its parent in someway?
Use the Singleton design pattern, write in your PageVC.m:
static NSObject yourObject;
#implementation PageVC
+(NSObject*)getThisObject{
if(!yourObject){
yourObject = [[NSObject alloc] init];
// init your object how you need it
}
return yourObject;
}
#end
and write in the PageVC.h:
+(NSObject*) getThisObject;
Now you can get this property in any class with this line:
NSObject *obj = [PageVC getThisObject];
But remember to include the PageVC.h in the other VCs.
I hope this was useful
You can access directly from it's parents. for parent's you meant they are subclasses of PageVC? Then just access it using VC4.propertyName if by parent's you meant viewController hierarchy within the NavigationController then you have different options.
The easiest one might be to create a property within your VC4 and VC5 then when you instantiate them just pass the value you want to use.
_side = [self.storyboard instantiateViewControllerWithIdentifier: #"GuessGameTurnWordVC"];
_side.someValue = something;
If you are going to use a variable or value over all your VC then I would suggest a singleton. Hope this helps
Here's the property declaration in my SlidingVC header, a subclass of UITableViewController:
#property (strong, nonatomic) NSString *user;
Here's my synthesize line:
#synthesize user = _user,sortedWordlist = _sortedWordlist, wordlist = _wordlist;
Here's my custom init:
- (id)initWithUser:(NSString*)theUser
{
self = [super init];
if (self) {
if ([theUser isEqualToString:#"user"]) {
_user = #"user";
}
else if ([theUser isEqualToString:#"opponent"]){
_user = #"opponent";
}
}
return self;
}
So what's happening is that as I step pver initWithUser:, I see that _user takes on the memory address of theUser in the variable debugger window. I step over return self and then to the closing } of the method and it's still set. However, Xcode returns to return self one more time and then if I step over that _user doesn't have a memory address next to it anymore, and it remains null for the methods that follow.
Why is it returning twice and then setting to null the second time?
Here's the method in my MainVC that instantiates the SlidingVC:
- (WSWordlistTableViewController *)setupWordlistTableViewController:(NSString*)user
{
WSWordlistTableViewController *wordlistTableViewController;
UIView *wordlistContainerView;
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"MainStoryboard"
bundle: nil];
if ([user isEqualToString:#"user"]){
if(!self.userWordlistTableViewController){
self.userWordlistTableViewController = [[WSWordlistTableViewController alloc] initWithUser:#"user"];
wordlistTableViewController = self.userWordlistTableViewController;
wordlistContainerView = self.userWordlistContainerView;
}
}
else if ([user isEqualToString:#"opponent"]) {
if(!self.opponentWordlistTableViewController){
self.opponentWordlistTableViewController = [[WSWordlistTableViewController alloc] initWithUser:#"opponent"];
wordlistTableViewController = self.opponentWordlistTableViewController;
wordlistContainerView = self.opponentWordlistContainerView;
}
}
wordlistTableViewController = [mainStoryboard instantiateViewControllerWithIdentifier:#"wordlistTableViewController"];
wordlistTableViewController.view.frame = wordlistContainerView.bounds;
wordlistTableViewController.view.autoresizingMask = wordlistContainerView.autoresizingMask;
[self addChildViewController:wordlistTableViewController];
[wordlistContainerView addSubview:wordlistTableViewController.view];
[wordlistContainerView bringSubviewToFront:wordlistTableViewController.wordlistTableView];
[wordlistTableViewController didMoveToParentViewController:self];
return wordlistTableViewController;
}
And the method that calls that, depending on which button is pressed:
- (IBAction)slideUserWordlistView:(id)sender {
if(!self.userWordlistTableViewController){
[self setupWordlistTableViewController:#"user"];
}
// (sliding drawer code here)
}
Or:
- (IBAction)slideOpponentWordlistView:(id)sender {
if(!self.opponentWordlistTableViewController){
[self setupWordlistTableViewController:#"opponent"];
}
// (sliding drawer code here)
}
What I'm doing is sliding out a view that contains my SlidingVC. I have two of them, one for each of two users. When each respective button is pressed I check if each respective SlidingVC exists, if not, instantiate then add to the the slidingViewContainer.