presentViewController - view solid black, viewDid___ methods not called - objective-c

...
SecondViewController *svc = [SecondViewController new];
[self presentViewController:svc animated:YES completion:NULL];
}
This code is exactly the same as what I used in another app, but here I'm using presentViewController rather than presentModalViewController
(completion:NULL makes them effectively identical. Same result, at least.)
Both attempts at creating a modal view are structured the same way. Those lines in the main view, a view controller in the Storyboard, and matching .h and .m files. The only difference is that here I want a programmatic trigger, so it's impossible to drag a segue and be done with it.
I have an object set to recognize a gesture and call the transition method. That's probably what's causing the problem (part of it, at least), but it is necessary.
Using a UIButton would be cheating. No deadline, no shortcuts.
EDIT: NSLog output shows something odd.
2012-04-05 10:41:12.047 MyApp[5962:707] <SecondViewController: 0x1d8c130>
2012-04-05 10:41:12.479 MyApp[5962:707] <SecondViewController: 0x1d8e360>
So I'm doing something stupid again that happens to have a very simple fix, right?
Edit again: presentViewController… was being called more than once. Fixed it. Still black, though.
Back to performSegueWithIdentifier:sender: instead of the much easier presentViewController:animated:completion:
Terminating app due to uncaught exception 'NSInvalidArgumentException", reason: 'Receiver … has no segue with identifier …'
I told it to perform a segue, but there isn't one in the Storyboard (I can't add one, there is no Storyboard Segues section under 'Connections inspector' for the object I'm attempting to use), so it crashes. This is normal behavior.
What I want is to have a modal view without needing to create a segue. I've done it, so I know it's possible.
All I need is a bit of help getting it to work.
.
performSegueWithIdentifier:#"Identifier" sender:nil NSSInvalidArgumentException
presentViewController:viewController animated:YES completion:NULL Empty View

Got it.
Replace the lines in the question with this:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Storyboard"
bundle:nil];
SecondViewController *viewController =
[storyboard instantiateViewControllerWithIdentifier:#"SecondView"];
[self presentViewController:svc animated:YES completion:NULL];
Credit for this solution goes to IturPablo's self-answered question:
TabBarController, overwriting shouldSelectViewController to do a segue

Are you looking for performSegueWithIdentifier:sender:? The docs seem to match your description:
"you can call this method to trigger a segue programmatically, perhaps
in response to some action that cannot be specified in the storyboard
resource file"

Related

tvOS preferredfocusedview is not always called

After a viewcontroller has been presented modally, the initial preferredfocusedview is called. However, after we dismiss the viewcontroller and it has been dealloc. preferredfocusedview is not called after presenting the viewcontroller again. Running on tvOS 9.2.
Even adding the following did not help:
-(void) viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self setNeedsFocusUpdate];
[self updateFocusIfNeeded];
}
Anyone know what's going on? Or if there's anyways to debug this?
Edit:
the way I am adding the viewcontroller:
viewController = [[UIViewController alloc] init];
[viewController addChildViewController:self];
[viewController.view addSubview:self.view];
[self didMoveToParentViewController:viewController];
If you are using a container view, having multiple ViewControllers or adding only one View Controller, the preferredFocusEnvironments method must be called from the rootView Controller indicating which View Controller to focus.
For eg.
View Controller A has a container View having ViewControllers B and ViewController C inside the Container.
View Controller A should have preferredFocusEnvironments returning which ViewController to focus.
This way, preferredFocusEnvironments on ViewController B or ViewController C will be called whenever the view becomes visible.
If the ViewController A doesn't have preferredFocusEnvironments, then it won't be called on the containerView ViewControllers.
Implementing custom focus behavior in tvOS 9 is disaster. Apple already mentioned that there is a limitation on redirecting focus specially when presenting/ dismissing a viewcontroller in WWDC.
tvOS10 will handle munch better with preferredFocusEnvironments.
https://developer.apple.com/videos/play/wwdc2016/215/
When I needed to fix this focus redirection issues in viewDidAppear in tvOS 9, I had exactly same issues. Sometimes it works, sometimes not. No clue what so ever. But after I put split second delay on setNeedsFocusUpdate / updateFocusIfNeeded in viewDidAppear it was way better in terms of consistency. preferredFocusedView get called all the time.
-(void) viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self setNeedsFocusUpdate];
[self updateFocusIfNeeded];
});
}
Do this in both presented and presenting view controllers, if you are manually changing focus. This is all from my observation and I don't think there is a proper way to achieve some focus behavior because tvOS API is kind of new and premature. Sorry about not being able to give you good explanation why this might work. Good luck.

return to ViewController after presentViewController

Im trying to return to a specific ViewController in it's current state after going from that ViewController to another using presentViewController.
But when I try to close the other ViewController (with dismissViewController) I get a white screen.
RootViewController *rootViewController
= [[UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil]
instantiateViewControllerWithIdentifier:#"RootViewController"];
[self presentViewController:rootViewController animated:YES completion:nil];
This isn't an option either because that instantiates a new viewcontroller and I want the old ViewController in its current state.
Do I need to pass the RootViewController as an argument when presenting the other ViewController or is there another option to return to the RootViewController in its current state?
Yes, there is a way to return original screen.
I met just like problem but solved it with following code line
[self dismissViewControllerAnimated:YES completion:nil];
one way to address this is to avoid having one view controller responsible for presenting and dismissing the other one.. what you can do is create a controller of controllers (give it a singelton method).. and have that object basically keep a reference to any view controller you are interested in maintaining its state. That way you wouldn't have worry about what's going on behind the scenes when you dismiss or present a view controller.

Can't modal to next viewcontroller

I feel like I am just missing something simple here. I am trying to modal to my next view controller. I imported the next view controller in my first .m file first. After I did that I wrote this code
CRHViewController *nextViewController = [[CRHViewController alloc]init];
[self presentModalViewController:nextViewController animated:NO];
Also, I am working with storyboard and not nibs.
What happens when I run this is as soon as it goes to modal to the next viewcontroller it just goes black.
Am I missing something simple? Does anyone have an suggestions to fix this problem?
Probably you're not initialising it correctly. For testing purposes I would try to display CRHViewController as first root viewController from AppDelegate and find if it is initialising at all. Then check if it gets to its methods:
initWithNibName
loadView
awakeFromNib
viewDidLoad
viewWillAppear
viewDidAppear
In this order. Its 90% certain that one of them fails. Check if it gets to every method you have implemented correctly in this order.
If your CRHViewController is in your storyboard, then you should be instantiating it with:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"yourStoryboardName" bundle:nil];
[storyboard instantiateViewControllerWithIdentifier:#"myIdentifier"];
You should give your view controller an identifier in IB to pass in as the identifier parameter in the above method.

Replacing Storyboard Segue with pushViewController causes strange behaviour

I can't seem to figure this out for the life of me. I have a custom table view cell, in that cell I have a few buttons configured. Each button connects to other view controllers via a storyboard segue. I've recently removed these segues and put a pushViewController method in place. Transition back and forth across the various views works as expected however the destination view controller is not displaying anything! I have some code below as an example.
Buttons have this method set:
[cell.spotButton1 addTarget:self action:#selector(showSpotDetails:) forControlEvents:UIControlEventTouchUpInside];
// etc...
[cell.spotButton4 addTarget:self action:#selector(showSpotDetails:) forControlEvents:UIControlEventTouchUpInside];
// etc...
showSpotDetails Method contains this code:
- (void)showSpotDetails:(id)sender
{
// determine which button (spot) was selected, then use its tag parameter to determine the spot.
UIButton *selectedButton = (UIButton *)sender;
Spot *spot = (Spot *)[spotsArray_ objectAtIndex:selectedButton.tag];
SpotDetails *spotDetails = [[SpotDetails alloc] init];
[spotDetails setSpotDetailsObject:spot];
[self.navigationController pushViewController:spotDetails animated:YES];
}
The details VC does receive the object data.
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"spotDetailsObject %#", spotDetailsObject_.name);
}
The NSLog method below does output the passed object. Also, everything in the details view controller is as it was. Nothing has changed on the details VC. It just does not render anything ever since I removed the segue and added the pushViewController method. Perhaps I am missing something on the pushViewController method? I never really do things this way, I try to always use segues...
Any suggestions?
Welcome to the real world. Previously, the storyboard was a crutch; you were hiding from yourself the true facts about how view controllers work. Now you are trying to throw away that crutch. Good! But now you must learn to walk. :) The key here is this line:
SpotDetails *spotDetails = [[SpotDetails alloc] init];
SpotDetails is a UIViewController subclass. You are not doing anything here that would cause this UIViewController to have a view. Thus you are ending up a with blank generic view! If you want a UIViewController to have a view, you need to give it a view somehow. For example, you could draw the view in a nib called SpotDetails.xib where the File's Owner is an SpotDetails instance. Or you could construct the view's contents in code in your override of viewDidLoad. The details are in the UIViewController documentation, or, even better, read my book which tells you all about how a view controller gets its view:
http://www.apeth.com/iOSBook/ch19.html
The reason this problem didn't arise before is that you drew the view in the same nib as the view controller (i.e. the storyboard file). But when you alloc-init a SpotDetails, that is not the same instance as the one in the storyboard file, so you don't get that view. Thus, one solution could be to load the storyboard and fetch that SpotDetails instance, the one in the storyboard (by calling instantiateViewControllerWithIdentifier:). I explain how to do that here:
http://www.apeth.com/iOSBook/ch19.html#SECsivc

Create UINavigationController programmatically

Just because I am unable to find a secure way (in a sense that it can be rejected by Apple guys) to customize UITabbar, in particular UITabBarItem I am trying some workaround.
I have a main view on which I recreate a kind of UITabBar, a normal view with two buttons inside. This is (roughly) the current hierarchy:
-MainView
--placeholder(UIView)
--fakeTab (UIView)
What I want to do is, after tapping a button in fakeTab, build a UINavigationController and add it to "placeholder" view so that the fakeTab remain on top and the whole navigation happens on the placeholder level.
I already tried with this piece of code in the method that it's intercepting tap button, and it works, I can see the ipvc.view added to placeholder.
IPPlantsViewController *ipvc = [[IPPlantsViewController alloc] initWithNibName:#"IPPlantsView" bundle:[NSBundle mainBundle]];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:ipvc];
UIView *placeholder = [self.view viewWithTag:200];
[placeholder addSubview:ipvc.view];
But later when I call from inside ipvc, then nothing happens:
IPAttributes *ipas = [[IPFactory findPlantByIndex:indexPath.row] attrs];
[self.navigationController pushViewController:ipa animated:YES];
I find the solution myself. What I was doing wrong is to attach the ipvc controller view to placeholder. Instead of doing this:
[placeholder addSubview:nav.view];
and everything works as expected, with my fake tabbar fully customized :-)
But, as a side note, the viewWillAppear seems to be never called.
It would be interesting to know why. I partially solved by making IPPlantsViewController the delegate of the UINavigationController.