UIViewController in a UIScrollView in a UINavigationController wants to push a new Controller - ios

I've got a stupid UI Problem right now with one of our apps.
I start with a UINavigationController which is the Initial View Controller in my Storyboard. The Root view controller is a UIViewController witch a UIScrollView as subview. The UIScrollView itself has paging enabled and contains several UIViewControllers.
Now here is my problem: The UIViewControllers (lets call them View #1) in the UIScrollView should react to a button event and make a segue to another UIViewController (View #2) which needs to get data from the previous UIViewController and needs to send data back by delegate methods.
When I try to get the UINavigationController and push View #2 on it, nothing happens. Showing it via presentViewController of course works.
Could you just help me a little bit, tell me that this doesn't work and when not why? I'm a little bit stuck at this point and i don't know how to proceed.

DO NOT put UIViewControllers inside another UIViewController (atleast pre iOs5) that goes against apples development guidelines ..
The UIViewControllers life cycle is managed internally and if you directly add the UIViewController as a subview a lot of methods such as
viewDidLoad
viewWillAppear
viewDidAppear
.
.
.
will not be called and from my experience will lead to really erratic behavior
Try and use custom UIView objects to get your internal logic for each view
agreed this somewhat breaks the MVC pattern but this would be the best way to go about your issue
But iOS 5.0 added containment UIViewControllers that correctly handles those lifecycle events by adding child view controllers. so if you are targeting only post iOs 5 devices this maybe a good way to about it
Have a look at this and this

Related

SWIFT 3, Eureka: When using a navigation controller, the keyboardWillShow callback is not called

Scenario
NavigationController is a root controller of a TabBarController
TabBarController has 2 UIViewControllers
One UIViewController has a function that presents another UIViewController
UIVIewController that is presented has a StackView
Instantiates a subclass of FormController (eg: FormVC) and adds to Stack as subview
Problem
The keyboardWillShow function of FormVC does not get called. The keyboard actually adds the input to the row, but the auto scrolling does not happen
Is this only a problem in Eureka or perhaps a problem using the Navigation controller's pushViewController?
Answer
The correct way to use the FormVC is with the Container, as per the comment in the question.
Here is a great explanation credit to Miguel Revetria - source, github:
Hi chefren the problem with your approach is that the
FormViewController lifecycle is not being handled because you are not
adding it to the view controllers hierarchy. Actually you are only
adding its view to one of your views.
The function keyboardWillShow is not being called due to the
FormViewController adds this observer to the notification
Notification.Name.UIKeyboardWillShow from the function viewWillAppear,
which in your case is not being called. As this, other stuff may not
work properly, because the view controller lifecycle is not being
handled.
That is not the way the FormViewController should be used. Using it in
that way may end up with an undesired behavior like you experienced.
The correct way to embed a FormViewController in other view
controller's view is by adding it as a Child View Controller. See this
Apple guide for further information Implementing a Container View
Controller. Additionally, in storyboards you can use a Container
View.
Regards

App structure: UIView vs. UIViewController in a paging interface

There are some great answers relating to when to use a UIViewContoller vs. a UIView. For example here and here.
The general gist is that a UIVIewController should be used to control a full screen of data, because
It is designed to handle rotation. Only one UIViewController should be on the screen at once because only the newest will be notified that the rotation occurred.
To stay true to the MVC paradigm, your business logic should live in a UIViewController, while only display and interaction logic should live in a UIView. Presumably business logic relates to what's on the entire screen.
My question is, given this, how do I structure an app with left-right paging between several top-level views?
I want to create an interface with several UITableViews holding a list of recipes. The user swipes left and right to navigate between lists. There is a common menu on the bottom that stays fixed no matter what.
My initial thought is to use one UIVIewController for the UIScrollView, then add subviews from there.
However I'd really like each list to have its own UIViewController, so it can handle its own rotation, and hold business logic like its own REST methods. It would seem to be a pain to have the top-level UIViewController handle the rotation of a child subview, and it would seem to be a violation of MVC to put the logic anywhere else.
Is there a way to structure an app so that multiple UIViewControllers live inside a UIScrollView, or would it appropriate to use a series of top-level UIViewControllers and UISwipeGestureRecognizer to simulate the paging effect of the UIScrollView?
Thanks.
A couple of thoughts:
If targeting iOS 5 and higher, I'd suggest using a UIPageViewController which is designed for precisely this UI (swiping back and forth between UIViewController instances). You would then have a separate UIViewController for each recipe. In iOS 5, you only have UIPageViewControllerTransitionStylePageCurl transition style, but in iOS 6, you also have UIPageViewControllerTransitionStyleScroll.
For more information, see the Page View Controller section of the View Controller Catalog for iOS.
This is much simpler than writing your own scroll view based solution. If you "roll your own" with a UIScrollView, you'll want to remove instances that have scrolled off screen (by registering as the scroll view's delegate and responding to scrollViewDidScroll) so you don't use up memory unnecessarily.
If you do add child view controllers to your scroll view, don't forget to call the appropriate custom container calls. Specifically, as you add view controllers to your scroll view, make sure you call the following (assuming controller is the child controller and self is the main view controller):
[self addChildViewController:controller];
[self.scrollView addSubview:controller.view];
[controller didMoveToParentViewController:self];
And as you respond to scrollViewDidScroll to remove view controllers that are no longer visible, do the appropriate removal calls, e.g.:
[controller willMoveToParentViewController:nil];
[controller.view removeFromSuperview];
[self removeChildViewController:controller];
For information about why it's important to call these custom container calls, see WWDC 2011 video Implementing UIViewController Containment.
I definitely wouldn't recommend using a UINavigationController, though, because it will keep all of the previous pages in memory.
I believe for the requirements you're talking about you could use a UINavigationController. It will give you the "left-right" paging that you want and you can use a UIViewController for each of your recipes.
Also, I think you want to use a UIScrollView because it lets you perform a "swipe" gesture. If that's the case you could also add a UISwipeGestureRecognizer to your view controllers and every time the gesture is recognized call pushViewController:animated: and popViewControllerAnimated: to perform navigation between your recipes.
This is just and idea.
Hope this helps!
As far as I see, there are 2 good options:
Using a root UINavigationController and push/pop child
ViewControllers depending on the direction of the swipe gesture
(recognized by UISwipeGestureRecognizer, just as you said).
Using a root UIViewController with a UIScrollView and adding the child
viewcontroller views as subviews of the scrollview. To handle
orientation changes, you could pass the orientation-change
UIViewController methods (willRotateToInterfaceOrientation,
didRotateFromInterfaceOrientation) to the child controllers, so they
can handle them.
I hope I helped
Firstly, you should be aware that since iOS 5 it's been possible to include child view controllers inside your main view controller.
https://developer.apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/CreatingCustomContainerViewControllers/CreatingCustomContainerViewControllers.html
So it seems to me that a good options would be to have a paged UIScrollView as your main controller, and then to put instances of your child controller onto each page.
Since that could all be a little memory intensive, you should really only have three instances at any one time (one being displayed, and one one either side so that they're ready if the user starts to scroll). The Apple demo project shows you how to configure a scroll view like that:
https://developer.apple.com/library/ios/samplecode/PageControl/Introduction/Intro.html#//apple_ref/doc/uid/DTS40007795
Hope that helps.

Please clear some confusions regarding UIViewController

Please clear some confusions regarding UIViewController
I found this article Abusing UIViewController and here are the links link1 & link2
and summarised points
This is author's (and Apple’s) advice In a nutshell:
One (and only one) view controller should be responsible for a whole hierarchy (or screenful) of UIViews.
Mostly, you should only use one view controller per screen. Essentially the rootViewController of the current UIWindow should be the only UIViewController with a visible view.
Each different screen should have a different view controller i.e. one controller should not control more than one screen.
You should NOT nest custom UIViewControllers within a view hierarchy.
If more than one UIViewController hangs off the application’s UIWindow, only one of these will get the messages for changes in orientation. The other(s) will NOT get these messages.
Nested UIViewControllers are not guaranteed, or likely, to receive messages for changes in orientation or lifecycle messages such as viewDidAppear:, viewWillAppear:, viewDidDisappear: and viewWillDisappear: even though they inherit from UIViewController. Only the topmost UIViewController is certain to get these messages.
Please clear point number 2 and 3
because when we use UINavigationController or UITabBarController we use multiple subclasses of UIViewController. And ios device has only one screen.....
This article Abusing UIViewController highlight apple suggestion
Note: If you want to divide a view hierarchy into multiple subareas
and manage each one separately, use generic controller objects (custom
objects descending from NSObject) instead of view controller objects
to manage each subarea. Then use a single view controller object to
manage the generic controller objects.
and in apple docs under heading of Coordinating Efforts Between View Controllers apple saying
Few iOS apps show only a single screenful of content. Instead, they
show some content when first launched and then show and hide other
content in response to user actions. These transitions provide a
single unified user interface that display a lot of content, just not
all at once.....
My requirement is NOT to use any container or modal or popover, I want to do manual management, I have two view controllers VC1 & VC2. VC1 is the root view controller now I want to switch/transit/move to VC2 what should I do?
VC1 should be the only subclass of UIViewController and VC2 should be the subclass of NSObject to manage a particular view in VC1 hierarchy?(the show hide thing by apple doc).
VC2 can also be the subclass of UIViewController, I just remove VC1 from root view and add VC2 as root view?
or what is the correct way?
Container view controllers (like UINavigationController) allow working around the one-VC-per-screen rule. Since iOS 5, developers have been able and allowed to write our own container controllers (which aren't actually much different from normal VCs). Generally this means that writing non-VC controller objects is less necessary than it used to be.
In your situation, where you want to replace the root view controller, your option 2 makes more sense. Use VCs where you can, and non-VC controller objects only when you can't. Since you're replacing the whole screen's content, just switching the UIWindow rootViewController makes the most sense (edit: alternately, many devs would just use a navigation controller to present the second view, because it's simple and convenient).

iOS App ECSlidingViewController: add new viewcontroller and transient to it

I use ECSlidingViewController in my App. There're four ViewControllers come with the sample code. These four ViewControllers corresponds to four option in the "MenuViewContoller".
Now I want to add a TableViewController in the "FirstTopViewController". Each cell of this TableViewController leads to a ViewController which is not one of the four predefined ViewControllers.
I saw that ECSlidingViewController ships a 'SampleTableViewController". But I don't know how to 'transient' to this ViewController.
I did work with UINavigationController before. I know how to push a view controller to NavigationController. I don't know how to cooperate UINavigationController with ECSlidingViewController.
Thanks a lot.
Here is a demonstration:
http://www.penghou.net/file/question.png
ECSlidingViewController is a container, just like UINavigationController. You give it view controllers and it displays and transitions between them.
ECSlidingViewController provides methods for setting view controllers and transitioning between them by calling anchor/reset. Check the header file for documentation on these methods.

Sharing the Same UIViewController as the rootViewController with Two UINavigationControllers

Update: I have decided to go a different route with my problem.
Instead of trying to reuse the same UIViewController directly, I use two vanilla UIViewControllers that are set as rootViewControllers. In their loadView methods, they make a call to [storyboard instantiateViewControllerWithIdentifier:] to get the former UIViewController and set their views to the controller's view. This is probably the preferred approach anyway, since I need to set several variables and delegates.
I have a UIStoryBoard with a UITabBarController as the entry point connected with two UINavigationControllers. Each of those share a common UIViewController as their root view controller. When the app starts, the first UITabBarItem is selected and the view loads as-expected. However, when I select the second UITabBarItem, the same view is not visible. I see the UINavigationBar with a black background. Am I doing something incorrect with the Storyboard interface, or do I need to manually instantiate the UIViewController via each UINavigationController's method--loadView for instance?
Strangely this is a question that no one else is asking. As far as I know it is not possible to share the rootViewController which I think is without a doubt a bug since when you inspect the connection on the storyboard you can see that the view controller is connected to both navigation controllers. I consider this a flaw in storyboarding because duplicating viewControllers and reapplying all of their connections is quite error prone and makes the storyboards overly complex.
I see your solution to the problem. Workarounds like this make me question if the current storyboard functionality in iOS is ready for creating apps. I think that there is a conceptual problem with the storyboards, Apple needs to decide if a viewController on a storyboard represents an instance or if it represents just the class, right now it is not consistent as you can see that multiple segues can actually point to the same viewController but in reality each segue has its own instance, why this is not also followed for rootViewController connections?, I don't know.
Just as a note, with your solution take into account the following from Apple's documentation:
"Important A view controller is the sole owner of its view and any subviews it creates. It is responsible for creating those views and for relinquishing ownership of them at the appropriate times, including during low-memory conditions and when the view controller itself is released. If you use a storyboard or a nib file to store your view objects, each view controller object automatically gets its own copy of these views when the view controller asks for them. However, if you create your views manually, you should never use the same view objects with multiple view controllers."
http://developer.apple.com/library/ios/#documentation/UIKit/Reference/UIViewController_Class/Reference/Reference.html
Definitely a bug in Storyboards. Another way to do this is to create basic UIViewControllers for each UINavigationController, then have a UIContainerView that points to the same UIViewController in each of the basic view controllers.
I think the easiest solution is to set no root view controller for your nav controller in the storyboard and then do something like this with your nav controller:
- (void)viewDidLoad
{
[super viewDidLoad];
UIViewController *topVC = [[UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil] instantiateViewControllerWithIdentifier:#"myTopVC"];
[self pushViewController:topVC animated:NO];
}
The first-pushed VC becomes the root.

Resources