Pushing a controller from a subview - objective-c

In essence what is the correct way of pushing a new view controller from a sub view of a navigation controller.
The issue being subviews of the navigation view don't inherit the self.navigationController (its nil)
The reason is I need separate controllers for the navigation bar view & the main view but both need to push new controllers.
I am willing to change this model if someone can tell me the correct way of doing this.
Also:
AppDelegate *del = (AppDelegate *)[UIApplication sharedApplication].delegate
[del.navigationController pushViewController:vc animated:YES];
Does not work as the delegates controller is nil.

Create a following category on UIView.
#interface UIView (GetUIViewController)
- (UIViewController *)viewController;
#end
#implementation UIView (GetUIViewController)
- (UIViewController *)viewController;
{
id nextResponder = [self nextResponder];
if ([nextResponder isKindOfClass:[UIViewController class]]) {
return nextResponder;
} else {
return nil;
}
}
#end
Now get the SuperView from the subView.
mySuperView = [mySubView superview];
Then call the method from category created.
mySuperViewController = [mySuperView viewController];
Now, using this viewController, you can access the navigationController.
I have not tried the above code and approach, but I hope it should work.

You may try two ways
1.Use the superViewController's navigationController to push your viewController
2.Embed your current viewController in a NavigationController so that the navigationController won't be nil

Related

Setting a delegate property of a controller to self is causing a crash in my app

I have 3 controllers:
SearchViewController
CollectionViewController
MainViewController
In short
In CollectionViewController I can access SearchViewController by tapping the magnify glass in the nav bar. I can then search for a specific item. Then select a result from the rows returned. Once I tap a row the SearchViewController is dismissed and a query is performed on the CollectionViewController.
However on MainViewController things don't work the same. Because when the SearchViewController is accessed from the MainViewController and dismissed it's the MainViewController that appears because that is where the SearchViewController was initialized from so I have to do some extra work to get me to the CollectionViewController to perform the query. I performed in first paragraph.
There is a protocol/delegate method I set in the MainViewController that notifies the CollectionViewController when the SearchViewController is dismissed that is causing the app to crash. I can't figure out why. Please read on to for a better explanation of what is going on.
In long
Customer types what they want to search for in the search box of the SearchViewController and when they tap the row in the associated tableView of results the SearchViewController is dismissed and it's parent CollectionViewController is now on screen. When this controller is dismissed I use a delegate to notify CollectionViewController that the SearchViewController was dismissed.
Notify CollectionViewController about dismissal of SVC then perform a query:
- (void)searchViewControllerDismissed:(VAGSearchViewController*)searchViewController withTitleForObject:(NSString *)titleString
{
_searchTitleString = titleString;
[self setObjects:nil];
[self performQuery];
}
This all works fine. However I have another controller MainViewController. SearchViewController can be accessed from MainViewController but I can't dismiss and perform a query the way I did when I accessed SearchViewController from CollectionViewController because when the SearchViewController is dismissed MainViewController is present on screen because it was the controller I accessed the SearchViewController from.
What I decided to do was detect which controller initialised SearchViewController then use the same delegate method from above to notify that controller when the SearchViewController was dismissed.
Inside SearchViewController:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
// If count of child view controllers is greater than 1 then we are not on main table view controller
if ([[[self presentingViewController] childViewControllers] count] > 1) {
[self dismissViewControllerAnimated:NO completion:^{
[[self delegate] searchViewControllerDismissed:self withTitleForObject:[[cell textLabel] text]];
}];
} else {
[self dismissViewControllerAnimated:NO completion:^{
[[self delegate] searchViewControllerDismissed:self withTitleForObject:[[cell textLabel] text]];
}];
}
}
This works fine. So now I created a protocol inside MainViewController.
Inside MainViewController.h:
#class VAGMainTableViewViewController;
#protocol VAGMainTableViewControllerDisappearedDelegate <NSObject>
- (void)mainTableViewControllerDisappearedwithTitleForObject:(NSString *)titleString;
#end
#interface VAGMainTableViewController : UITableViewController
#property (nonatomic, weak) id<VAGMainTableViewControllerDisappearedDelegate> disappearDelegate;
What I wanted was this delegate method to then notify CollectionViewController that SearchViewController had been dismissed from MainViewController then perform a query.
Here is the code inside MainViewController.m:
- (void)searchViewControllerDismissed:(VAGSearchViewController *)searchViewController withTitleForObject:(NSString *)titleString
{
self.disappearDelegate = self;
[[self disappearDelegate] mainTableViewControllerDisappearedwithTitleForObject:titleString];
[self performSegueWithIdentifier:#"garmentsCollectionSegue" sender:nil];
}
This is crashing with:
[VAGMainTableViewController mainTableViewControllerDisappearedwithTitleForObject:]: unrecognized selector sent to instance 0xf0140f0
When I comment out the line where I set the disappearDelegate line the crash goes away and I am pushed to the CollectionViewController (garmentsCollectionSegue). But the data passed in the delegate method is received by CollectionViewController obviously because a delegate is set.
I've done enough messing around and I think the problem I'm having is caused by not properly setting a delegate or not setting it in the correct place.
Would appreciate some help.
Thanks for your time.
It seems that you are setting the MainViewController as delegate of itself. I don't think that is what you want.
You want to use that delegate protocol to inform the CollectionViewController of what happens in the MainViewController, right?
Then CollectionViewController should be the disappearDelegate.
So try instead:
CollectionViewController *collectionVC = //this depends on your app structure, get the controller
self.disappearDelegate = collectionVC;

Switch Active View Controller Programmatically

I have 2 views as part of my Navigation Controller.
I created a class for my Navigation Controller with a public method that certain actions on the first view will trigger to load the Detail View Controller. I am importing the Detail View Controller, but nothing in my code is working. I know the method is being called correctly because I'm logging it.
What am I doing wrong here?
#import <UIKit/UIKit.h>
#interface NearbyController : UINavigationController
- (void) nextpage;
#end
#import "NearbyController.h"
#import "DetailView.h"
#implementation NearbyController
-(void) nextpage {
NSLog(#"working");
DetailView *nextView = [[DetailView alloc] init];
[self pushViewController:nextView animated:YES];
}
#end
If you initialize a view controller programmatically you should use this init.
If you have set a storyboard ID you can do this
DetailView *nextView = (DetailView *)[self.storyboard instantiateViewControllerWithIdentifier:#"DetailViewControllerID"];
[self pushViewController:nextView animated:YES];
If you want to load from a XIB you can call
DetailView *nextView = [[DetailView alloc] initWithNibName:#"MyDetailViewControllerID" bundle:nil];
[self pushViewController:nextView animated:YES];
Hope it helps!
If you're using storyboards what you wanna do is setup the storyboard id in the identity inspector on your DetailViewViewController
Then you would wanna instantiate your view controller like so:
UIStoryboard *sb = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
DetailView *nextView =[sb instantiateViewControllerWithIdentifier:#"DetailViewViewController"];
/* your identifier is your storyboard id*/
sb = nextView.storyboard;
Then you should be able to push your view controller.
Im still new to this but i hope this helps!

set navigationController's rootViewController as delegate

My problem may be one of technique instead of a misunderstanding of how controllers and delegates are set up. That is, maybe I should be doing all a different way...
In any event, I have a storyboard setup with a mainViewController. In it there's a UIButton which, when clicked, segues to a popover. The popover's content view controller is a UINavigationController who's rootViewController is, say, MyViewController.
I'm trying to make the mainViewController a delegate of MyViewController and am doing so in prepareForSegue:
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:(#"popSleepSegue")] || [segue.identifier isEqualToString:(#"popAlarmSegue")])
{
UIStoryboardPopoverSegue *popSegue = (UIStoryboardPopoverSegue *)segue;
popSegue.popoverController.delegate = self;
popSegue.popoverController.passthroughViews = [NSArray arrayWithObject:self.view];
if ([segue.identifier isEqualToString:#"popAlarmSegue"])
{
if ([[segue destinationViewController] isKindOfClass:[UINavigationController class]])
{
UINavigationController *uNC = (UINavigationController *)[segue destinationViewController];
MyViewController *aVC = (MyViewController *)uNC.topViewController;
aVC.popController = popSegue.popoverController;
aVC.delegate = self;
}
}
}
}
The [self.delegate class] is coming up as null in an NSLog when MyViewController loads. And, naturally, the delegate callback isn't received in the mainViewController.
Essentially, I'm trying to mimic the behavior of Apple's Calendar app on the iPad.
I'm trying to use delegation to pass data upstream as per the idiom. The trick is that I'm trying to set the delegate through a UINavigationController which is the content view of a popover. Sounds too complex. Maybe there's another idiom?
In the meantime, I'm going to give NSNotificationCenter a whirl.
You have to potential 'if' statements which can not be true:
if ([segue.identifier isEqualToString:#"popAlarmSegue"])
{
if ([[segue destinationViewController] isKindOfClass:[UINavigationController class]])
{
From the look of your code, you should get the controller drilling inside the content controller in the PopOver,not the destination viewController from the segue. As it seems the second 'if' is not true.
You would need to add:
if ([[popSegue.popoverController contentViewController] isKindOfClass:[UINavigationController class]])
{
UINavigationController *uNC = (UINavigationController *)popSegue.popoverController;
MyViewController *aVC = (MyViewController *)uNC.topViewController;
aVC.popController = popSegue.popoverController;
aVC.delegate = self;
}

How to pass an uiimage to another view controller?

How to pass the uiimage selected in uiimagepickercontroller in MainViewController to SecondViewController? It did push to the SecondViewController, however the uiimage is empty.
I've searched through the web, tried with the solutions but still cannot get it work.
photoImage is the uiimageview I've declared in SecondViewController.
- (void)imagePickerController:(UIImagePickerController *) picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
[picker dismissModalViewControllerAnimated:YES];
SecondViewController *secondVC = [[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil];
[secondVC.photoImage setImage:[info objectForKey:#"UIImagePickerControllerEditedImage"]];
[self.navigationController pushViewController:secondVC animated:YES];
[secondVC release];
}
A UIImageView in your second view controller doesn't exist until the viewDidLoad method of that controller is called. Put a property into SecondViewController that holds a UIImage, store the reference to the image you want to use in it when you push the view controller (instead of trying to set the image view), and then move your setImage: call into the second view controller's `viewDidLoad``.
Create a shared class that will store the image, and access it wherever you want.
In MainViewController assign that image in that shared class' ivar, and in SecondViewController access it.
Edit :
Below code shows how to implement shared class, this may not be syntactically correct, because currently I am not using mac, so I cant complile.
#interface SomeManager : NSObject
+(id)myImage;
#end
#implementation SomeManager
+(id)myImage{
static id myImage= nil;
#synchronized([self class]){
if (myImage== nil) {
myImage= //put your image here;
}
}
return myImage;
}
#end

Protocol is not calling methods

I have a modal view which gets the user to select some data to add to a table. When the user presses a save button, the modal view should disappear and send the required data back to the view controller that presented the modal view for further processing. To achieve this, I have set up a protocol. The protocol method in the original view controller does not get called. My code is below, what am I doing wrong?
The header file (modal view controller):
#protocol AddTAFDataSource;
#interface AddTAFViewController : UIViewController <UIPickerViewDelegate, UIPickerViewDataSource> {
id<AddTAFDataSource> dataSource;
NSString *newICAOCode;
}
#property (nonatomic, assign) id<AddTAFDataSource> dataSource;
- (IBAction)saveButtonPressed;
#end
#protocol AddTAFDataSource <NSObject>
- (void)addNewTAF:(AddTAFViewController *)addTAFViewController icao:(NSString *)icaoCode;
#end
The implementation file (modal view controller):
#import "AddTAFViewController.h"
#import "TAFandMETARViewController.h"
#implementation AddTAFViewController
#synthesize dataSource;
...
- (IBAction)saveButtonPressed {
[self.dataSource addNewTAF: self icao: newICAOCode];
}
#end
Presenting view controller header file:
#import "AddTAFViewController.h"
#interface TAFandMETARViewController : UITableViewController <AddTAFDataSource> {
}
#end
And finally, the presenting view controller:
#import "AddTAFViewController.h"
...
- (void)insertNewObject:(id)sender {
AddTAFViewController *addTAFViewController = [[AddTAFViewController alloc] initWithNibName: #"AddTAF" bundle: [NSBundle mainBundle]];
addTAFViewController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[(AddTAFViewController *)self.view setDataSource: self];
[self presentModalViewController: addTAFViewController animated: YES];
addTAFViewController = nil;
[addTAFViewController release];
}
- (void)addNewTAF:(AddTAFViewController *)addTAFViewController icao:(NSString *)icaoCode {
newICAO = icaoCode;
[self dismissModalViewControllerAnimated: YES];
}
Just to remind, it is the above -(void)addNewTAF: method that does not get messaged. Any help/pointers in the right direction are much appreciated.
Replace:
[(AddTAFViewController *)self.view setDataSource: self];
With:
[addTAFViewController setDataSource:self]
After all, the dataSource is a property of the controller, not a controller's view.
Rather than trying to use a separate object (your dataSource) to pass data between the two view controllers, you could simply use add properties to contain the data directly in the view controller you're going to present modally (here, the AddTAFViewController).
Then in the method you use to dismiss the modal view controller, before dismissing it you can send [self modalViewController] to get the modal view controller, and at that point the parent view controller can send it any messages it wants. That would allow you to grab whatever data you need from the modal view controller, so you wouldn't need the data source and the protocol at all.
You are wrong at this point:
[(AddTAFViewController *)self.view setDataSource: self];
you should write this instead:
addTAFViewController.dataSource = self;