Is it possible to get hold of the transitionCoordinator of a UIPageViewController?
Every time I try to get it it's nil. Does it even use one?
If not, is there a non-hacky way of responding to the progress of a scroll between pages?
(hacky = iterating subviews to get scroll view and becoming scroll view delegate. This isn't ideal because of the "magic" that UIPageViewController does with its scroll view)
Thanks
After years of experimentation, I have never found a situation in which the transitionCoordinator of a UIPageViewController is not nil.
And, as Fogmeister points out in this original question and comments, there is also therefore no good way to track the page view controller scroll animation and coordinate with it.
My solution is to roll my own paging scroll view that does what UIPageViewController does, i.e. it preloads the page views before and after this one, but no others, so that memory is conserved. Now we have an ordinary scroll view, we can serve as its delegate, and we can do anything we like in connection with the scroll.
It isn't all that difficult, and is in fact what we used to do before there was a UIPageViewController (yes, I remember those days well).
Apple even provides an example of how to do it: https://developer.apple.com/library/archive/samplecode/PageControl/Introduction/Intro.html
Related
There are many questions like mine both in Objective-c and Swift that ask how to lock a UIToolbar to the top of a UITableViewController, and I will add to that list. However, my question is unanswered by the others. I need to lock it to the top of the view controller so that it is not considered a cell by the program, and so it doesn't move down, but is locked at the top instead.
All other answers that I found want me to either
(a) Embed the UITableViewController in a UINavigationViewController
or
(b) To use a UIViewController with a UITableView inside it
I do not want to do either one of these.
I want to keep a UITableViewController––not to replace it with a UIViewController and UITableView––and I do not want to use a UINavigationViewController––I am trying to not do that because it causes a plethora of issues in the rest of the app (which can be fixed but I don't want to have to do that).
What you are asking for is not reasonably possible.
The root view of UITableViewController is a UIScrollView. Every view you add as a subview in a UITavleViewController must be part of that scrollview and thus will scroll with it.
To make it work you must take one of the options you mentioned (moving to a UIViewController with a table view as a subview is likely to cause the least disruption in your app).
There are a couple of unreasonable solutions I can think of:
- implement the scrollviewDidScroll delegate method in you UITableViewController and constantly reposition tour toolbar within the scrollview to make it appear to always be at the top (the toolbar will probably jitter around and look terrible)
- create a new UIWindow on top of your entire app that hold the toolbar (don't do this, it is a terrible idea).
All,
I am running into some performance/memory issues when using several ChildViewControllers in my ParentViewController. Here is my situation: I have a ParentViewController with a dynamic number of ChildViewControllers - some times as many as 20. They are contained in a UIScrollView, and are paged through. I'm running into problems when I have several on the page (I'm only loading the first two, then building the others as I swipe), however, having that many within the ParentViewController is starting to cause some crashes due to memory.
The ChildViewController has a lot going on in it, and I'm going through it to make sure it's as efficient as possible, however, I have concerns about this approach working on older devices (as is, I'm getting crashes on the 5S).
It seems that it would help to change the view controllers to just be views, but it'd be a pretty large endeavor as the VC is complex. One suggestion I had was to create a view from the existing view controller's view, and set several delegate methods on the view and interact with the views from the ParentViewController that way. Does any one have any thoughts on the efficiency of that method as a opposed to the current method of using ChildViewControllers?
Another thought I had was to build a custom ContainerViewController and have all the children in there to swipe through, but I wasn't sure if that would give me an advantage over using the children in a UIScrollView.
Any thoughts?
I personally would not advocate the refactoring of your code to use views rather than view controllers. The view controller, itself, is unlikely to be the source of the memory problems, but rather the model objects they keep track of (as well as the assets the view controller's view uses). I think the key is to simply remove the view controllers (and their views) as they scroll off of the screen.
In your scrolling logic, as you're adding child view controllers that scroll into view, you are presumably doing all of the appropriate containment calls:
UIViewController *newChildViewController = ...
[self addChildViewController:newChildViewController];
newChildViewController.view.frame = ...;
[self.scrollView addSubview:newChildViewController.view];
[newChildViewController didMoveToParentViewController:self];
(See WWDC 2011 video Implementing UIViewController Containment for a discussion about why it's important to do these containment calls, namely to keep your view controller hierarchy synchronized with your view hierarchy.)
As the child views scroll out of view, you just do the appropriate containment calls to remove the child controller (and its view):
[childViewControllerToRemove willMoveToParentViewController:nil];
[childViewControllerToRemove.view removeFromSuperview];
[childViewControllerToRemove removeFromParentViewController];
// also remove any other strong references you have to that childViewControllerToRemove
Alternatively, you might want to contemplate using a UIPageViewController which (in iOS 6+) offers scrolling page view (UIPageViewControllerTransitionStyleScroll) for the transitionStyle. This simplifies the amount of custom container code you have to write to handle the view controllers that scroll in and out of view. The UIPageViewController is designed precisely for this situation of scrolling (or paging) through a bunch of different view controllers' views. See the Page View Controllers discussion in the View Controller Catalog for iOS.
I don't think moving them all to UIView's will help. You could achieve this same effect by just adding aChildViewController.view without ever technically adding it as a childView. I would optimize the loading of the views into your UIScrollView. Make sure you only have, say 4 views loaded into memory at any one time. Another option would be to use a horizontal UITableView or UICollectionView so you can reap the memory management features they have build in.
You could turn it into a tableview and let the cell dequeue take over. You would only have a few in memory at a time then.
In my view I am using a UITableView that is controlled by a UITableViewController on the top half of the screen. The remaining screen is used for a UIScrollView that contains a view that is controlled by the main UIViewController.
When I perform a pull down to refresh in the UITableViewController, (for some reason if the number of table entries is less than or greater than the initial load value, the UIScrollView in the main UIViewController's frame gets changed to the screensize...
Essentially it breaks my paging unless I reset the scrollview back to the intialized size...
I have no idea why this happens as the UIScrollView is not used in the UITableViewController. The only scrollview that is used in the UITableViewController is the UITableView's to handle pull down to refresh...
Does anyone know why the main UIScrollView's contentSize gets changed randomly when it shouldn't even been accessible from the UITableViewController class?
Thanks
Just tried it here, and I can't duplicate your experience. I'm guessing you have an unexpected or inconsistent view/controller hierarchy? Look at the controller of the table and scroll views' common superview. Anything fishy there? Remember: view controllers manage sets of views. Container view controllers manage other view controllers and have special rules (see: The View Controller Programming Guide, esp. -addChildViewController:, etc.).
I'd suggest opening a blank project and trying to recreate the problem in its simplest possible form. If it's magically fixed, what's different? If it's still giving you trouble, send us a link so we can see the details of how you have things wired.
Here is a screenshot of a view that I have right on my device.
The design issue that I am having here is that the top part of the screen is always going to be static - as far as its placement. The rest of the screen are a row of buttons added to this view programmatically. The arrows represent the idea that you could swipe in 4 directions(from top, from bottom, from left and from right) which would animate a new view onto the screen. This view is the same instance as the view before it. In fact all these views are the same instance but the buttons will be different.( i dont want to get too specific here.)
My design right now calls for pre-loading the views ahead of time. The data for each button for each view will be in core data. I will not know ahead of time how many views there are. One view might just have a view to the right that you can swipe in from the right and that view might have a top and bottom arrow - that would allow you to swipe from bottom or top that would be another view(same UIView subclass). So basically a tree of views.
I guess I am trying to figure out my options. A NavigationController is not really what i want because i have no need for a navigation bar, although in my mind it makes sense that i would have an array of view controllers here each with its view property pointing to each view that is allocated and then as i swipe i would bring in the appropriate view by using the view controller index.(through some animation code)
Another possible option would be UIScrollView but that seems cumbersome and may not be what i would really want.
One of the easiest setups would be to create a XIB file that would consist the top part of the screen and on the bottom an empty UIView that i would programmatically populate with the buttons(and their unique data). The problem that I am havign with this is, is how would i swap the views this way. I guess i could make the rootViewcontroller the first viewcontroller instance with the first view and then swap them.
I guess I am wanting to see if anyone had any questions or suggestions to come up with the easiest(most modular) approach to swiping in different views. Is using an array of view controllers the way to go?
A couple of thoughts:
This screams custom container view controller to me (if iOS 5 and above). See Creating Custom Container View Controllers in the View Controller Programming Guide for iOS.
You talked about using UISwipeGestureRecognizer. You could always contemplate UIPanGestureRecognizer, too. It's nice to have continuous gestures. Think about reading a book where the page swipe tracks your finger, but you can stop and go back, mid gesture. Sure, start with swipe gestures for now, but if your UX lends itself to it, you can always contemplate continuous gestures in the future.
You said that you're planning on "pre-loading the views ahead of time". Generally, given the limited memory capacity of mobile devices, you'd want to be more conservative than that. Maybe load the current view, and the four ones that you're likely to go to in each of the four directions (so that you can present them instantaneously), but leave it at that. Once you go to one of the four possible destinations, then go ahead and release the ones that are not reachable from the current one and tee up the ones that are.
One Xib is enough for you (for this part of your app anyway).
Don't use a UINavigation Controller. The NavController metaphor is one of a stack of cards. You don't have that data structure.
The general idea is one ViewController for one screenful of stuff. If you feel the need for two viewControllers (one for the top part, one for the bottom part) then you are going to have to look at custom container controllers, to ensure that the contained controllers receive their instance methods correctly (viewDidLoad, viewWillAppear, etc). An example container controller managing a pair of viewControllers is the iPad splitViewController. But I don't think you need to do this.
I suggest placing a scrollView on the lower half of the screen and using that to manage your data views. If the upper part of the screen also needs to change (under other conditions) you could even have two scrollViews, one up top, the other below. They can be paged, and contain views that are the exact size of the respective screen portion. They can share their single containing viewController as a common location for their delegate methods.
I can't really help you with more detail, as I don't have a precise enough idea what you want to achieve. Perhaps you should try and implement it one of these ways and come back here with more questions as your ideas become concrete. Start out with the simplest idea (eg scrollView inside a single viewController), only chuck it out when you find you have to break it!
update
following your comments, I do think a scrollView might work for you. I think that managing a stack of view controllers with a custom container controller (as Rob suggests) could get overcomplicated. You will have to create your own custom container controller, the pre-existing ones such as UINavigationController are not appropriate for your data strucure (from what I can gather anyway).
You won't need to manage tons of UIViews, in fact you only need 5 - one for the onscreen portion of the scrollView, one for the screen immediately left and right, and similarly for the immediate one above and below. You can reuse these views as you swipe, much the way that tableViews reuse their cells. The rest of it will be about manipulating your data so the right content is arranged in the views as they come onscreen.
See my answer to this question for some more ideas on this: UICollectionView horizontal continuous loop
I have a uitableview controller which is a subview to a view managed by a uiviewcontroller. nothing really out of the ordinary but the tableview tracks gestures on the wrong axis(only on device).
Basically you scroll up/down table doesnt do anything, and left/right scrolls table up/down. its super weird. i was hoping somebody has seen this before and maybe know what causes it?
Edit: heres a video
http://c.drunknbass.com/EB7m
at the end i am scrolling a uiscrollview that scrolls normally and is a child of the same uiviewcontroller.view
UIKit relies on there being a key window, and that window having a root view controller, to be able to correctly handle events, and forward them to your code. I suspect that perhaps one of those things is not set up correctly in your app. (Such that the device orientation isn't matching up with the visual orientation of your UI.)
Also note that prior to iOS 5, making one controller's view the child of another controller wasn't really supported by UIKit. It can be done, and mostly works, but you are going to have to manage the forwarding of all of your lifecycle events. (See the notes on controller containment in the docs, and the description of -automaticallyForwardAppearanceAndRotationMethodsToChildViewControllers as well.)