UIPageViewController: how to have "negative" spacing between view controllers (with scroll-transition style) - ios

I have to implement a view controller (on iPhone, portrait only, full screen view) where the upper part of the view must have an horinzontal, paged scrolling behavior, potentially infinite.
I already used for similar purposes UIPageViewControllers, to take advantage of the datasource and delegate protocols, which are very helpul for manage memory and other stuff (keeping only 3 view controllers in memory, providing delegates to handle actions exactly when a transition is done and so on): so I think that in this case too this component is the best choice.
But here comes my problem. In the view I'm realizing, I have to let the user understand that he can swipe left and right to move to another view: a page control is not a good choice, since the scroll could be potentially infinite, so I would like to let a small portion of the views of the left and right view controllers to be visible.
Something like that:
link to the image (sorry I cannot include images in my posts yet)
Up to now I have not been able to figure out how to realize this. In the options during initialization, UIPageViewControllerOptionSpineLocationKey can be specified to set (from documentation) "Space between pages, in points": but this seems to work only with positive value, so that the space increases, while it ignores negative values that could reduce the space.
I hope there might be a solution using page view controllers, since at the same time I need to refresh a table view in the lower part of the screen when a transition is complete, and the delegate method of page controllers is ideal for this aim.
Maybe a collection view can be an alternate solution, but it is more complicated and I'm not sure how to obtain a behavior like the one I described to refresh the table view.
If needed I can attach some code and a screenshot of the prototype

Ok, I understand that this is not possible and why it is.
The datasource methods load, when needed, the view controllers that are before and after the current one. Making these view controllers' views always visible, as I desired, will require that the datasource loads more than one view controllers after (or before, depends on the direction of scrolling) the current one, to be ready for the pan actions (potentially, before the animation is ended by the user lifting up its finger, two view controllers "after" or "before" could become visible it my desired configuration), and this is not intended by UIPageViewController on iPhone, especially in portrait mode.
So actually, the only way to achieve that more than one view is visible in an horizontal-scrolling component at any time, is to implement a UIScrollview with horizontal paging and calculate the contentSize and other sizes accordingly.

Related

Creating a menu in iOS

I'm currently creating an update of my iOS application and I'm a bit stuck. I've tried to googling around but cannot find a decent answer on this.
I've a menu which links to different views. And I'm not really sure if I've done it the best method.
I've created a view, and added the links into a stack view. Should I considering changing it all to a tableview? or a collection view? Or maybe there's another way?
The current look:
Should I change this to a tableview? collection view? or something else? Or just let it stay as it is?
If the number of items in your menu changes at runtime and is large, you should use a table view, because a table view is good for efficiently displaying a screen's worth of items from a large list of items.
If the contents of your menu is small (under maybe two screenfuls of items) and fixed at compile time and you are using a storyboard, then you could use a table view with static cells, if you can make it look the way you want.
If the contents of your menu is small, then you can use a stack view (inside a scroll view) if that is easier for you. There is no particular advantage to using a table view over a stack view to display a small amount of content, unless you need other features of the table view (like the ability to select/deselect rows).
Based on the screen shot you posted, I'd either use a table view with static cells (since the screen shot is from a storyboard) or a stack view, depending on whether I can get the appearance I want from a table view. If, as in the screen shot, the buttons must be centered vertically, I'd use a stack view, because it's easier to vertically center the content with a stack view.
Look, the fact of have many itens on your screen is clear on the mobile applications, to make it easy, we have collecions view like UITableView and UICollectionView. On the UITableView's case, this implements the scrolling and have methods do handle the operations' list, you can see the documentation to check these methods: https://developer.apple.com/documentation/uikit/uitableview.
Main reasons to use UITableView
Implements scroll behavior.
Independent of size screen you can access all itens.
Easy to detect interactions like tap on cell.
Easy to make changes, like insert and remove content.
The UITableView exists precisely to solve problems like you has.

UICollectionView vs UIPageViewController

I need to have different full screen views in my app. Very similar to how snapchat works. The views should be able to communicate between each other.
My question is: Should I use a UICollectionView with cells same size as the screen or should I use UIPageViewController?
Please provide some background info to support your opinion!
I think both have pretty different purposes.
UICollectionView is great to build a mosaic of views (think an image gallery for instance), whereas UIPageViewController is kind of similar to the flipping pages of a book. The latter seems to be what you need, but UIPVC doesn't seem to offer many tweaking/customizations, like custom transitions for example. In which case you may want to start from a UIScrollView with paging enabled to recreate something similar but with more potential. Here's an example.
Personal opinion: for this specific case I'd use a page view controller. Collection views have any things you have to consider, like when the device rotates you have to recalculate where you are, which cell you have to display, ask to scroll to the current cell, and if you are displaying a video or using the camera you might have to control it perfectly, otherwise issues will come.
However think about new features that might be added to your app, if you think you might show more than 2 items on screen, then you'd better choose a collection view.
A page view controller lets the user navigate between pages of
content, where each page is managed by its own view controller object.
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIPageViewControllerClassReferenceClassRef/
So If you plan to swipe from one ViewController to another, go for PageViewController. If you plan to have only one ViewController that deal with a list of fullscreen image or so, go for a view controller with a collectionView, or maybe your own swipeView.
UIPageViewController use different view controller and load multiple controller so obvisioly take more memory as compared to UICollectionView. So if your required task is less calculation or step to do then its recommended to use UICollectionView, other case preferred way is to user UIPageController.

UICollectionView for Springboard like folders

I am trying to achieve the following effect:
A UICollectionView displays a grid of cells for a parent type of object, e. g. a photo album. When I tap one of these items, I would like to scroll that element to the top of the screen and open a Springboard like folder from it. Inside that folders area, another collection should be shown, consisting of the detail items, i. e. the individual photos of that album. Tapping in the remaining "parent" view closes the folder again. See this schema:
What I have done so far is a regular collection view for the albums. When I select one, it scrolls to selected item to the top and then uses JWFolders to open an empty folder at that place. Once that is shown, I trigger the surrounding UINavigationController to push my 2nd view controller with the detail items. That one is layed out so it appears to the user as if it were still the same view.
There are several problems with this approach, and I would like to know how to do this better:
JWFolders takes a screenshot and animated two halves of it up/downwards to achieve the opening effect. This is ok, but pretty slow on an iPad3, because it moves a lot of pixels and the iPad3's GPU is not quite up to the task.
The 2nd view needs to be pixel-perfect to match on top of the first one. This is likely to break accidentally.
I am limited as to what animations are possible for the view controller transition. The default UINavigationController's push from the right is not fitting. I override that to do a cross-dissolve, but still it is far from ideal.
I would like to get pointers as to how to approach this problem in a maintainable manner that does not require to much creative hacking against what the frameworks are designed to do. I might be missing something obvious here, so pointers to examples or general advice are appreciated.
Update:
I changed the approach a bit. Now I use a container view controller that has two embedded collection view controllers. One for the "Album" and one for the "Photos" part at the bottom. Using a UIImageView in the middle between the two I can get the triangle pointing upward done. This is also nice from a maintenance point of view, because it makes maintenance easier with the two collections being handled completely separately.
The app uses Auto Layout, so I can change the amount of space each of the two embedded views takes by modifying the constraints. This is way faster than the screenshot based approach with JWFolders and works nicely on an iPad3 as well.
This almost gets me where I want to be. The one thing that remains is to get the opening animation right. I would like to simultaneously scroll the Albums collection, so that the tapped item goes to the top and expand the photos collection with the triangle pointing at the Album cell.
Can I somehow "connect" the lower view to that cell via layout constraints, so that the scrollToItemAtIndexPath:atScrollPosition:animated: call drags the lower view open?
To get around it I would lose the library and cause iOS to move those display elements around without screenshots or other tricks. On the tap, cause the tapped icon to retain its normal appearance while you dim all the others. Find the contents of the collection view from the top to the end of the line where the tapped icon is. Create two new collection views - one which contains the top half, including your tapped icon and one containing the rest, below. Animate those views apart to make room for the folder view.
The folder view is another UICollectionView that appears in the gap created.
In the main view there are either one or three views presented depending on whether the drawer is open or closed. I would probably look at creating a view controller with a collection view, and using view controller containment to manage all three views. You have complete control over how those views are presented, so you could animate top and bottom views up and down simultaneously to reveal the folder view in place, as Springboard does.
When that's all working then you could generalize and start doing things like deciding to make the tapped icon part of the bottom collection with the folder appearing above if the icon was low on the screen.
(I hesitate to answer this because of the large number of upvotes yet no answers, so I may have missed something - but that is how I would begin trying to achieve the Springboard effect.)
To solve this problem in a relatively easy way, you could try to make the folder a simple UICollectionView subclass and then insert that cell when the albums cell is tapped.
In the collection views data source you would have to return different size etc. for the folder cell.
In the folder you would have to create the folders collection view, avoid making the folder cell the data source of the cell folder collection view tho.

UIPageViewController cache and swap view hierarchy for pages with similar content

My UIViewController has a UIPageViewController embedded in it. The pager can contain anywhere from 5-38 pages (each page is an instance of a UIViewController subclass) depending on the situation. I've noticed that depending how complicated I make the UI elements on each page, the app slows down considerably, and is very slow when swiping to go to the next page.
Here's the thing - the view on each page is identical, except for the values of a few UITextViews. I am building the view in each page's viewDidLoad method each time viewControllerAtIndex is called for a new page. I feel like there must be a way to re-use the same view for each page, and just swap the text values that are supposed to be different. Can anyone describe a strategy to do this?
Like I said, every page has an identical view hierarchy except the values for some of the text, so I'm really just looking for a way to maintain one basic view controller per page, but cache the view hierarchy to be re-used on each page, and swap out some simple text values depending on page number.
EDIT
Something I forgot to mention in the original post is that I'm building my view programmatically because the number of elements on the page is dependent on choices the user made on previous screens. i.e. there may be 5 TextViews or 10 TextViews, etc. depending on what the user selected on a previous screen (before coming to the UIPageViewController). With that said, I do not believe an xib based approach will work because the initial layout is dynamic.
Thanks in advance!
Have you tried using a xib based view, which you could pull out of the xib once, and keep a strong reference to it. In your controller's viewDidLoad, you could set its view to this xib base view, then populate the text view(s) with the proper text.

UITableView display distorted before setAnimationTransition during view swap

Greetings! I'm trying to borrow the view flip concept from Apple's TheElements sample app. This sample employs a container UIView in which you can swap between two subviews. The flip is achieved using setAnimationTransition:forView:cache: and removing/adding each subview.
In general, the flip works and I can swap between my two views (a UITableView with headers/footers, and a MKMapView). However, various (consistently chosen) areas of my table view are obliterated (using the table view's background color) before and after the table view is flipped, and I don't understand why.
I can't find anything unusual about the table view in terms of drawing. In the case of the sample app, the view is drawn from scratch, but I would hope that doesn't factor in to it! I'm really hoping it's something simple - maybe a UITableView property setting?
Clues appreciated. Thanks!
Update: When I slow down the animation, I begin to get an understanding of what is being disturbed (though I still don't know why). My table header view contains an image view and label view. Now, imagine the CGRects for those two views (only without any visible content, just a background color) being redrawn further down the table view, right over the table rows.
This also happens with another chunk of real estate that appears to come from a table view cell that was set with a custom height (to size the text within it).
I dropped a gratuitous number of breakpoints throughout my code (where things are sized, created and whatnot), and not one of them is hit during the transition.
I even tried placing my table view inside a UIView and targeting that for the transition instead. No difference.
From the API docs:
Caching can improve performance but if
you set this parameter to YES, you
must not update the view or its
subviews during the transition.
Updating the view and its subviews may
interfere with the caching behaviors
and cause the view contents to be
rendered incorrectly (or in the wrong
location) during the animation. You
must wait until the transition ends to
update the view.
That's all well and good, but I'm not updating any views or subviews during the transition. (At least not on purpose! Again, see the note about the breakpoints. Nothing got hit.)
Amazing.
Answer: In this case, it appears to only happen in ... the Simulator! On the device, it's fine.
I was thinking that such a run-of-the-mill transition would be the sort of thing that would render identically on the Simulator and the device.
I was wrong. So if you ever see animation transition glitches on the simulator, hang in there. It might be fine after all.

Resources