Showing a view controller from a parallel window (with the same parent) - ios

I am trying to show a ViewController directly from its sibling window, the tree looks kinds like this: CalibrationVC<--- MainVC ---> Settings VC. The MainVC presents the other two modally, and over current context. Now what I want to do is click a button in SettingsVC, that would open the CalibrationVC for a specific device. I have managed to do so using unwind segues and a delegate from the SettingsVC, and it looks like this:
- (void)showViewForDeviceCalibration
{
[self performSegueWithIdentifier:#"showCalibrationViewFromSettings" sender:nil];
}
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"showCalibrationViewFromSettings"])
{
[_delegate calibrateDevice:deviceToConfigure];
}
}
The delegate implementation:
-(void)calibrateDevice:(Device *)device
{
dispatch_block_t autoinitService =
^{
deviceToCalibrateFromSettings = device;
[NSThread sleepForTimeInterval:0.2];
dispatch_async(dispatch_get_main_queue(), ^{
[self performSegueWithIdentifier:#"showCalibrationViewForDeviceFromSettings" sender:nil];
});
};
dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), autoinitService);
}
And the prepare for segue method:
- (void)prepareForSegue: (UIStoryboardSegue *)segue sender:(id) sender
{
if ([segue.identifier isEqualToString:#"showCalibrationViewForDeviceFromSettings"])
{
CalibrationViewController *destinationController = (CalibrationViewController*)segue.destinationViewController;
NSArray *devicesToCalibrate = [NSArray arrayWithObject:deviceToCalibrateFromSettings];
[destinationController setDevicesToCalibrate: devicesToCalibrate];
}
This works well, but is there a better way to do that? I am really concerned about the delegate implementation because it uses a separate thread only to wait for a moment and then use the main thread again. I had to do this because without it the CalibrationVC would not appear saying that the MainVC is already presenting. So to sum thing up, is there a better, more optimal/proper way to do this?

If you don't specifically need unwind segues, you could do something like this:
Settings VC:
- (void)showViewForDeviceCalibration
{
[_delegate calibrateDevice:deviceToConfigure];
}
Delegate:
-(void)calibrateDevice:(Device *)device
{
[self dismissViewControllerAnimated:YES completion:^{
[self performSegueWithIdentifier:#"showCalibrationViewForDeviceFromSettings" sender:nil];
}];
}

Related

Dimissing TableViewController unwinds to first storyboard

I have a UITableViewController embedded in a UINavigationController.
When I click a button in my Detail View (Detail view is in a UITabViewController) to showList the UITableViewController (List Table View) called List is presented.
If I click the Back button in the navigation controller, I am taken back the correct screen: Detail View Shown by the Blue Line
If I select an list item the delegate method didSelectFromList is called from the didSelectRowAtIndexPath and the app is taken back to main screen (Home View) shown by
the red line.
What I want is the purple line.
Any help?
Attempt 1:
I let the delegate dismiss the List TableVC.
#import "ListTableViewController.h"
#interface CallDetailViewController () <ListTableViewControllerDelegate>
#property (nonatomic, strong) LotListTableViewController * lltvc;
#end
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([[segue identifier] isEqualToString:#"segue_list"]) {
self.lltvc = (ListTableViewController*)segue.destinationViewController;
self.lltvc.delegate = self;
}
}
- (void) showList {
[self performSegueWithIdentifier:#"segue_list" sender:self];
}
#pragma mark LIST SELECTION DELEGATE METHOD
- (void) didSelectFromList:(NSString *)item {
[self.lltvc dismissViewControllerAnimated:YES completion:^{
NSLog(#"Did Select item: %#", item);
}];
}
In the LIST TABLE VIEW CONTROLLER
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
///.. stuff to find the correct string
[self.delegate didSelectFromList:item];
}
Attempt 2:
Here I let the List TableVC dimiss itself.
#import "ListTableViewController.h"
#interface CallDetailViewController () <ListTableViewControllerDelegate>
#end
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([[segue identifier] isEqualToString:#"segue_lots"]) {
NSLog(#"GOING TO THE LOT LIST");
UINavigationController * nvc = (UINavigationController*)segue.destinationViewController;
LotListTableViewController *lltvc = [nvc childViewControllers][0];
lltvc.delegate = self;
}
}
- (void) showLotList {
[self performSegueWithIdentifier:#"segue_lots" sender:self];
}
#pragma mark LIST SELECTION DELEGATE METHOD
- (void) didSelectFromList:(NSString *)item {
NSLog(#"Did Select item: %#", item);
}
In the LIST TABLE VIEW CONTROLLER
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
///.. stuff to find the correct string
[self dismissViewControllerAnimated:YES completion:^{
[self.delegate didSelectFromList:item];
}];
}
I have also tried from the didSelectRowAtIndexPath in the List TableVC
[self.navigationController dismissViewControllerAnimated:YES completion:^{
[self.delegate didSelectFromList:item];
}];
If you keep on pushing view controller, they get added to the navigation stack, so if you do [self dismiss or [self.navigationController dismiss, all the navigation stack is cleared and you will betaken to the rootViewController.
Either you can pop one view controller or
Instead of pushing the navigation controller from detailVC, present the navigation controller that has the list table VC. In that case [self.nav dismissviewCOntroller will work,

Warning: Attempt to present <ResetSigninDetailsViewController: 0x8ca9640> on <UINavigationController: 0x8ca44f0> while a presentation is in progress

I'm getting warning like this Warning: Attempt to present ResetSigninDetailsViewController: 0x8ca9640 on UINavigationController: 0x8ca44f0 while a presentation is in progress!
and also i'm sending an array to ResetSigninDetailsViewController in prepare for segue method,even the array in ResetSigninDetailsViewController is empty always...
please give a better soluton thanks in advance :)
- (IBAction)didClickCantAccessMyAccount:(id)sender
{
[self performSegueWithIdentifier:#"ResetSigninDetailsView" sender:self];
[self dismissViewControllerAnimated:YES completion:nil];
// when clicked the button the ResetSigninDetailsViewController have to be
load with getting array loaded in that(ResetSigninDetailsViewController)
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"ResetSigninDetailsView"])
{
ResetSigninDetailsViewController *resetSigninDetailsObject =( ResetSigninDetailsViewController *)segue.destinationViewController;
resetSigninDetailsObject.userDetailsArray = userInformationArray;
//userInformationArray is an array in current viewcontroller
}
}
I think that this:
[self performSegueWithIdentifier:#"ResetSigninDetailsView" sender:self];
[self dismissViewControllerAnimated:YES completion:nil]
Should be:
[self dismissViewControllerAnimated:YES completion:^{
[self performSegueWithIdentifier:#"ResetSigninDetailsView" sender:self];
}];
i think
[self performSegueWithIdentifier:#"ResetSigninDetailsView" sender:self];
should be enough as long as it was a PUSH segue

How can i add 2 segue on 1 action?

I have a tableview which is showing my array items. . I connected push style segue to detailviewcontroller screen with my storyboard but i dont want to all items go detailviewcontroller so i made a controller like that ;
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if (sender == self.btnEkle) return;
if (sender == self.btnKrediKartlarim) return;
NSString *bankaAdi = [[mainList objectAtIndex:indexPath.row] objectForKey:#"BankaAdi"];
if (bankaAdi.length > 1) {
KartDetay *vc = [self.storyboard instantiateViewControllerWithIdentifier:#"KartDetay"];
[self presentViewController:vc animated:YES completion:nil];
}
If bankaAdi.length > 1 my app should go to the KartDetay
if its not i mean else my app should go to the detailviewcontroller
these codes are working but there is an error in my compiler.
Unbalanced calls to begin/end appearance transitions for .
Sorry for my English i know i didn't describe myself clearly but please try to help me.
Thanks !
---UPDATED AREA----
First of all Thank you for answer.But it doesnt work or i couldnt do that.
1- I created 2 manuel different segues to my KartDetay("taksit" segue name) and my DetayEkran("detay" segue name)..
2-I used with these codes..
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSIndexPath *indexPath2 = [self.tableView indexPathForSelectedRow];
bankaAdi = [[mainList objectAtIndex:indexPath2.row] objectForKey:#"BankaAdi"];
if (bankaAdi.length > 1)
{
[self performSegueWithIdentifier:#"taksit" sender:nil];
}
else
{
[self performSegueWithIdentifier:#"detay" sender:nil];
}
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([segue.identifier isEqualToString:#"taksit"])
{
//if (sender == self.btnEkle) return;
//if (sender == self.btnKrediKartlarim) return;
KartDetay *vc = [self.storyboard instantiateViewControllerWithIdentifier:#"KartDetay"];
[self presentViewController:vc animated:YES completion:nil];
}
else
{
DetayEkran *detayEkran = [self.storyboard instantiateViewControllerWithIdentifier:#"DetayEkran"];
[self presentViewController:detayEkran animated:YES completion:nil];
}
When I run my app and tap my first cell which is bankaAdi>1.
App can go to KartDetay screen but my compiler says :
Unbalanced calls to begin/end appearance transitions for .
Thank you for answer again.
----ReUpdated Area --- problem is solved by Greg... Thank you for that Greg...
I made a huge mistake with these codes coz i call another DetayEkran *detayEkran = [self.storyboard instantiateViewControllerWithIdentifier:#"DetayEkran"];
[self presentViewController:detayEkran animated:YES completion:nil];
in my prepareforsegue method. When i delete all instantiate codes in my prepareforsegue methods my app working fine...
So
Greg codes are working like a charm.. Thank you Greg !
When the segue is called the prepareForSegue:segue method is called and it handles the transition, you shouldn't call presentViewController or push view controller manually.
The best way to do that is create two segue for KartDetay and detailviewcontroller with two different identifiers in storyboard.
And override didSelectRowAtIndexPath method, where you check your condition and run appropriate segue:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *bankaAdi = [[mainList objectAtIndex:indexPath.row] objectForKey:#"BankaAdi"];
if (bankaAdi.length > 1) {
[self performSegueWithIdentifier:#"YOUR1IDENTIFIER" sender:nil]
}
else
[self performSegueWithIdentifier:#"YOUR2IDENTIFIER" sender:nil]
}
If you need to pass any parameters use prepareForSegue method:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([segue.identifier isEqualToString:#"YOUR1IDENTIFIER"]) {
KartDetay *vc = [self.storyboard instantiateViewControllerWithIdentifier:#"KartDetay"];
[self presentViewController:vc animated:YES completion:nil];
}
else {
//handle other view controller
}
}
Just remember to replace segue identifiers with your one.
The prepareForSegue:sender: method is invoked before opening the detail view controller. It is the place to prepare for the transition, not to present/push view controllers. The actual transition is done after the method has been invoked. Whether push or present is used, depends on the segue type in the storyboard.
Try using the tableView:didSelectRowAtIndexPath: method instead of the prepareForSegue:sender: method. This should solve your problem.

IOS7 Change value of label in different view before segue (Same controller)

I have 2 simple views in my Storyboard and both use the same ViewController. I have the following code to switch the the second view...
self.labelStatus.text = #"CHECKING";
[self performSegueWithIdentifier:#"LoginSuccess" sender:sender];
*labelStatus is on the second view.
For some reason the labels text is not changing. Can anyone advise why this is?
[self performSegueWithIdentifier:#"LoginSuccess" sender:sender];
//change the label value in prepareForSegue: method
- (void)prepareForSegue:(UIStoryboardSegue *)segue {
if ([segue.identifier isEqualToString:#"LoginSuccess"]) {
UIViewController *loginVC = segue.destinationViewController;
//declare the string in .h file and assign it in viewDidLoad in loginVC
loginVC.labelString = #"CHECKING";
}
}
in loginViewController.h file
#property(nonatomic, strong) NSString *labelString;
in loginViewController.m file
- (void)viewDidLoad
{
[super viewDidLoad];
self.labelStatus.text = labelString
}
You can customise your destinationViewController (properties, views, etc) in prepareForSegue: method, which is called before segue is executed:
- (void)prepareForSegue:(UISegue *)segue {
if ([segue.identifier isEqualToString:#"mySegue"]) {
UIViewController *destVC = segue.destinationViewController;
// do your customisation
}
}
Edit-
If you need to change the label in the same view controller, then you should modify the label in the prepareForSegue method.

Is there any way to push a vc in a storyboard in init with coder?

So I have a problem when trying to push some Viewcontrollers basically when the app loads it checks if you are already registered and if you are it sends you to a diff vc and if your not registered it sends you to a RegisterVC. It's done in storyboard and the problem is that when I put the code in viewDidLoad the pushes works but I can't see the rootviewcontroller from the storyboard but if I move the code into the init with coder I can see the viewcontroller but the push return's nil in the method that's supposed to push to a new vc, so the transition never happens.
The code looks like follows:
if (emp == nil) {
[self gotoRegisterView];
}
else {
[self gotoSMWView];
}
and :
- (void) gotoRegisterView {
UIViewController* vc = [self.storyboard instantiateViewControllerWithIdentifier:#"RegisterVC"];
[self.navigationController pushViewController:vc animated:YES];
}
- (void) gotoSMWView {
UIViewController* vc = [self.storyboard instantiateViewControllerWithIdentifier:#"SMWVC"];
[self.navigationController pushViewController:vc animated:YES];
}
Any suggestions are much appreciated.
If you're using storyboards, just connect the main scene to any other scene you want to call from there. Then, Ctrl+Drag from the initial VIEW CONTROLLER (not elements on the view controller, but from the controller itself). Then click on the segues you just created and give them good descriptive names. For this example, we'll just say segue1 and segue2.
Now, if segue1 takes you to the RegisterView, and segue2 to SMWView then you can do this:
- (void) gotoRegisterView {
[self performSegueWithIdentifier:#"segue1" sender:self];
}
- (void) gotoSMWView {
[self performSegueWithIdentifier:#"segue2" sender:self];
}
If you need to do any other initialization or set up on these view controllers, you can override this method:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
Within this method, you can check which segue has been called using this template:
if([[segue identifier] isEqualToString:#"segue1"])
And you can get an instance of your destination view controller like this:
YourViewController *vc = [segue destinationViewController];

Resources