I'm using the template master detail application. I have added a modal segue from SplitViewController and given it the identifier "DisplayLoginView".
I call the following from my detailViewController:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:YES];
[self.splitViewController performSegueWithIdentifier:#"DisplayLoginView" sender:self.splitViewController];
}
I also have the prepareForSegue method defined in detailViewController:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSLog(#"Source Controller = %#", [segue sourceViewController]);
NSLog(#"Destination Controller = %#", [segue destinationViewController]);
NSLog(#"Segue Identifier = %#", [segue identifier]);
if ([segue.identifier isEqualToString:#"DisplayLoginView"])
{
PrometheusLoginViewController *loginViewController = (PrometheusLoginViewController *)segue.destinationViewController;
loginViewController.delegate = self;
}
}
Any idea on why it's not called?
You're asking the splitViewController to perform the segue, but you're defining prepareForSegue in the detailViewController. They need to be on the same object for prepareForSegue to be triggered.
Related
I have view and a lot of segues from different other views to it, is there any property to check which segue does call this view?
I am not sure tht you can check it by just segue name.
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqualToString:#"view1"])
{
[segue.destinationViewController setVariable:variable];
}
}
Create this variable in destinationViewController.
Then check value of "variable".
You make use of the the following function in your viewcontroller,In which making use of the segue identifier you are able to identify which view has called the upcoming view.
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqualToString:#"view1"])
{
NSLog(#" called by view1");
[segue.destinationViewController setVariable:variable];
}
}
Create a #property in your destination ViewController
Assign segue identificator to it in -(void)prepareForSegue method:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.destinationViewController respondsToSelector:#selector(setMyIdentifier)]) {
[segue.destinationViewController setMyIdentifier:segue.identifier];
}
}
Handle self.myIdentifier property in -viewDidLoad of your destination ViewController
I have a problem I am calling WSLogin class from my Loginviewcontroller. and it successfully return my sessionId of User. then I am storing this sessionId into string and sending this value to Searchviewcontroller but not successfully submit value to Searchviewcontroller.
when I succesfully login then I move to Searchviewcontroller through tab-bar controller. I generate NSLog in searchviewcontroller to check the value of session when I click on searchtabbar after successfully login then on NSLog session id = null return to me kindly tell me how I can fix this problem?
This is the code
// LoginViewController.m file
-(IBAction)Login:(id)sender
{
wsobject = [[WSLogin alloc] init];
sessId = wsobject.sessionId;
NSLog(#"sessionidd = %#",sessId);
// it successfully return session id
}
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
SearchViewController *afc = (SearchViewController *) segue.destinationViewController;
afc.sessionId = sessId;
// i have create sessionId property in SearchViewController
}
// SearhViewController.m file
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"sessionId = %#",sessionId);
//printing sessionId value but getting Null here when click on Searhtabbar
}
I'm asuming that you've already perpare segues for the same and in identity inspector gave it a name, say "goToNextView". Now put the following code where you're jumping to next view controller
-(IBAction)methodName:(id)sender
{
[self performSegueWithIdentifier:#"goToNextView" sender:self];
}
Then in your code modify prepare for segue delegate method like this
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"goToNextView"]) {
DestinatonViewController *controller = segue.destinationViewController
// set the properties value you want for destination View Controller
controller.propertyName = yourValue;
Hope this will help..
I have an Facebook enabled, iOS 5 app that uses a storyboard and segue based navigation and am confused on how to implement "iOS native deep linking." The example code at Improving App Distribution on iOS just displays a UIAlertView but I am trying to initiate two consecutive seque operations.
For purposes of this question, I've simplified the application to three view controllers: MYCategoryTableViewController, MYItemsTableViewController and MYItemViewController. In the normal flow, the application opens to MYCategoryTableViewController, which displays a table of categories. When a category is selected by the user, there is a segue to MYItemsTableViewController which displays a table of items for that selected category. Lastly, when an item is selected, there is a segue to MYItemViewController which displays an item detail view.
The prepareForSegue from MYCategoryTableViewController sets a property on the destination view controller that represents that category:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"ITEMS_SEGUE"]) {
MYItemsTableViewController *vc = [segue destinationViewController];
MYCategory *mycategory = [self.fetchedResultsController objectAtIndexPath:[self.tableView indexPathForSelectedRow]];
vc.mycategory = mycategory;
}
}
The prepareForSegue from MYItemsTableViewController sets a property on the destination view controller that represents that category:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"ITEM_SEGUE"]) {
MYItemViewController *vc = [segue destinationViewController];
MYItem *myitem = [self.fetchedResultsController objectAtIndexPath:[self.tableView indexPathForSelectedRow]];
vc.myitem = myitem;
}
}
Question: I know that I need to implement something in application:openURL, but not sure what to do next. Assume the incoming URL gives identifiers to lookup the MYCategory and MYItem objects. I found performSegueWithIdentifier but not sure how that interacts with prepareForSegue and how I set my model objects on the destination view controllers.
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
// get "target_url" from incoming url
// and parse out MYCategory and MYItem identifiers
// something like this???
[self.window makeKeyAndVisible];
[self.window.rootViewController performSegueWithIdentifier:#"ITEM_SEGUE" sender:self];
return [facebook handleOpenURL:url];
}
Update: Selecting programmatically a cell on a tableview doesn't perform associated segue has given me an idea. Maybe I just save off the url from application:openURL: and let MYCategoryTableViewController load naturally. Then during viewWillAppear, call tableView selectRowAtIndexPath and then performSegueWithIdentifier to transition to MYItemsTableViewController. Repeat the same pattern in MYItemsTableViewController, but clear out the url before the performSegueWithIdentifier call.
Here is what I got working. In MYAppDelegate, I captured a string represented the id of the deep link.
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
NSString *deepLinkId;
// more code that parses url
// only deep link if MYCategoryTableViewController is active controller
UIViewController *rootContoller = self.window.rootViewController;
if ([rootContoller isKindOfClass:[UINavigationController class]]) {
UINavigationController *navController = (UINavigationController *)rootContoller;
if ([navController.topViewController isKindOfClass:[MYCategoryTableViewController class]]) {
self.deepLinkId = deepLinkId;
}
}
}
Then, when MYCategoryTableViewController loads, call selectRowAtIndexPath and then performSegueWithIdentifier.
- (void)processDeepLink {
if (_appDelegate.deepLinkId) {
MYItem *myitem = [MYItem lookupById:_appDelegate.deepLinkId inManagedObjectContext:_appDelegate.dataDocument.managedObjectContext];
if (myitem) {
NSIndexPath *indexPath = [self.fetchedResultsController indexPathForObject:myitem.mycategory];
[self.tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionMiddle];
[self performSegueWithIdentifier:#"ITEMS_SEGUE" sender:self];
}
}
}
And in when MYItemViewController loads, a similar flow.
if (_appDelegate.deepLinkId) {
MYItem *plate = [MYItem lookupById:_appDelegate.deepLinkId inManagedObjectContext:_appDelegate.dataDocument.managedObjectContext];
NSIndexPath *indexPath = [self.fetchedResultsController indexPathForObject:plate];
[self.tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionMiddle];
_appDelegate.deepLinkId = nil;
[self performSegueWithIdentifier:#"ITEM_SEGUE" sender:self];
}
I also had to observe UIApplicationDidBecomeActiveNotification for the use case when the application was already open.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(processDeepLink)
name:UIApplicationDidBecomeActiveNotification
object:nil];
Trying to launch a viewcontroller in connectiondidfinish delegate method of NSUrlConection
//Sprequest.m inherited from nsobject
- (void)connectionDidFinishLoading:(NSURLConnection *)conn {
NSLog(#"connectionDidFinishLoading ");
if(nStatus == 401)
{
NSLog(#"called maincontroller to launch dvrview");
MainController *mainview =[[MainController alloc] init];
[mainview reponseFromServer];
}
}
//maincontroller.m from viewcontroller
-(void)reponseFromServer
{
NSLog(#"response from server - main controller ");
dvrView *dvrObj = [[dvrView alloc]initWithNibName:#"dvrView" bundle:nil];
[self.navigationController pushViewController:dvrObj animated:YES];
}
this dvr view doesnt get loaded
Sprequest.m is inherited from NSObject , its not a viewController subclass so you cant use
[self.navigationController pushViewController:dvrObj animated:YES];
inside Sprequest.m
You can get the navigationController object from the appdelegate like this
((AppDelegate *)[UIApplication sharedApplication].delegate).navigationController
then use
[((AppDelegate *)[UIApplication sharedApplication].delegate).navigationController pushViewController:dvrObj animated:YES];
I'm using a UIStoryboardPopoverSegue to present a popover for an iOS 5 iPad app. The Segue works great, but it seems like the toolbar that contains the button is a passthrough view for the popover controller so if you keep pressing the button, more popovers appear. As I'm not creating and keeping track of the UIPopoverController myself (as the Storyboard is doing it) I can't dismiss it when the button is touched again. Has anyone else run into this? I have a bug open with Apple but they haven't responded.
EDIT: I've solved this using the answer below. Here is the code I ended up using. currentPopover is a __weak ivar in my view controller class, so when the controller is done it will drop to nil automatically.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([segue isKindOfClass:[UIStoryboardPopoverSegue class]]){
// Dismiss current popover, set new popover
[currentPopover dismissPopoverAnimated:YES];
currentPopover = [(UIStoryboardPopoverSegue *)segue popoverController];
}
}
There are some visual issues with the your solution Cory.
Two options that can be considered - simply remove or change the action of the button that presents the popover.
Option 1, hold a pointer to the button's action, and after the popover is presented, set the action to nil. Upon dismissal of the popover reset to the original action.
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
action = [sender action];
[sender setAction:nil];
self.currentPopover = [(UIStoryboardPopoverSegue *)segue popoverController];
self.currentPopover.delegate = self;
}
-(BOOL)popoverControllerShouldDismissPopover:(UIPopoverController *)popoverController
{
[self.navigationItem.rightBarButtonItem setAction:action];
return YES;
}
This way the popover can only appear once, and will be dismissed as expected.
A second option would be to change the function of the button so that when the popover is visible, tapping the button will cause the popover to be dismissed.
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
action = [sender action];
target = [sender target];
[sender setTarget:self];
[sender setAction:#selector(dismiss:)];
self.currentPopover = [(UIStoryboardPopoverSegue *)segue popoverController];
self.currentPopover.delegate = self;
}
-(void)dismiss:(id)sender
{
[self.navigationItem.rightBarButtonItem setAction:action];
[self.navigationItem.rightBarButtonItem setTarget:target];
////or
// [sender setAction:action];
// [sender setTarget:target];
[self.currentPopover dismissPopoverAnimated:YES];
}
-(BOOL)popoverControllerShouldDismissPopover:(UIPopoverController *)popoverController
{
[self.navigationItem.rightBarButtonItem setAction:action];
[self.navigationItem.rightBarButtonItem setTarget:target];
return YES;
}
Simply connect a UIBarButtonItem via IBAction. Use the itendifier set in interface builder:
-(IBAction)barButtonItemPressed:(id)sender {
if (currentPopoverController && currentPopoverController.popoverVisible) {
[currentPopoverController dismissPopoverAnimated:YES];
currentPopoverController = nil;
} else {
[self performSegueWithIdentifier:#"aSegueIdentifier" sender:sender];
}
}
Get a reference of the new UIPopoverCOntroller from the seque:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"aSegueIdentifier"])
currentPopoverController = [(UIStoryboardPopoverSegue *)segue popoverController];
}
currentPopoverController is an instance variable, defined in header file:
UIPopoverController *currentPopoverController;
Important: The anchor property of the seque must be set to the corresponding UIBarButtonItem!
You have to store a reference to the popoverController property passed as part of the UIStoryboardPopoverSegue class in the prepareForSegue class method.
To access it, over-ride the method in the calling view controller like this:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// The Storyboard Segue is named popover in this case:
if ([segue.identifier compare:#"popover"] == NSOrderedSame) {
// segue.popoverController is only present in popover segue's
// self.seguePopoverController is a UIPopoverController * property.
self.seguePopoverController = segue.popoverController;
}
}
Then you can dismiss it in the usual way.
This solution could also have visual issues, but it doesn't for my simple case. In my case, the popover was just displaying some help. I put together the following (with ARC) that will dismiss the popover viewcontrollers when the button bar button is pressed a second time (both the original and the newly created one).
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if( [segue isKindOfClass:[UIStoryboardPopoverSegue class]] )
{
UIStoryboardPopoverSegue *popoverSegue = (id)segue;
UIPopoverController *popoverController = popoverSegue.popoverController;
if( m_popoverController.popoverVisible )
{
[m_popoverController dismissPopoverAnimated:NO];
dispatch_async( dispatch_get_main_queue(), ^{
[popoverController dismissPopoverAnimated:YES];
});
m_popoverController = nil;
}
else
m_popoverController = popoverController;
}
}
I also added some cleanup in dealloc
- (void)dealloc
{
if( m_popoverController.popoverVisible )
[m_popoverController dismissPopoverAnimated:YES];
}
It does require a member variable in your class
UIPopoverController *m_popoverController;
I prefer to use a static weak variable, which keeps everything together in one place:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"showSomething"]) {
static __weak UIPopoverController* currentPopover = nil;
[currentPopover dismissPopoverAnimated:NO];
currentPopover = [(UIStoryboardPopoverSegue *)segue popoverController];
// ...
}
}
There's no reason to add a separate extra variable (when are you going to have multiple instances of the view controller?), and this way you can add an extra variable for each if() block.
June 14 2013
Thanks for edit in question. Rather than dismissing and recreating the view controller - to avoid performance and battery concerns and prevent Flash when dismissing and recreating view controller - how about preventing the second instance of popover from popping?
//place in view controller (tested iOS6+, iPad, iPhone)
__weak UIPopoverController *popover;
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if([segue isKindOfClass:[UIStoryboardPopoverSegue class]]
&& [segue.identifier isEqualToString:#"mySegue"]) //remember to change "mySegue"
popover = [(UIStoryboardPopoverSegue *)segue popoverController];
}
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender {
if ([identifier isEqualToString:#"mySegue"]) //remember to change "mySegue"
return !popover;
else
return YES;
}
added checks to: http://stackoverflow.com/a/10238581/1705353
This is also good.
#interface ViewController : UIViewController <UIPopoverControllerDelegate> {
UIPopoverController * seguePopoverController;
}
#property (strong) UIPopoverController * seguePopoverController;
#end
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if (self.seguePopoverController) {
[self.seguePopoverController dismissPopoverAnimated:NO];
self.seguePopoverController = nil;
}
// The Storyboard Segue is named popover in this case:
if ([[segue identifier] isEqualToString:#"popover"]) {
UIStoryboardPopoverSegue* popSegue = (UIStoryboardPopoverSegue*)segue;
UIPopoverController *thePopoverController = [popSegue popoverController];
thePopoverController.delegate = self;
self.seguePopoverController = thePopoverController;
}
}
- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController
{
self.seguePopoverController = nil;
}