An object that controls swipe navigation and animations between views or view content.
SDK
- macOS 10.8+
Framework
- App
Kit
Declaration
class NSPageController : NSView Controller
Overview
NSPage
is useful for user interfaces which control navigating multiple pages as in a book or a web browser history. Page controller inherits from the NSView
class . You must assign the view
property to a view in your view hierarchy. The NSPage
class does not vend a view and does insert itself into the responder chain.
Conceptually, the page controller manages swiping between an array of pages, the arranged
. Using the selected
property, you can determine how many pages forward or backward the user may navigate.
Note
When using history mode, do not wire page
to an NSSplit
subview. This will actually cause a 3rd split when the private transition view is put in the view hierarchy. You can work around this by using an empty NSView
instance as the NSSplit
subview and placing the real contents in a subview of the empty view.
Page Controller Modes
There are two modes that an NSPage
instance may operate in, history mode and book mode. The main difference between the two modes is that History mode expects page
to be the content and Book mode expects page
to be be a container for the content that you will supply by returning view
in your delegate methods.
History Mode
History mode is designed to be the easiest way to create a history user interface. The page controller will manage the history (the arranged
property), snapshots, and user navigation between pages in the history.
As the user navigates to new content, add to the history by calling navigate
. The page controller will remove any arranged
after the selected
and then add the object to the end of the arranged
array and update the selected
property. Just like navigating in a new direction in a web browser, all forward history is lost once the user starts navigating a new path. After returning from navigate
you are free to update the contents of page
.
Delegate Method Invocation During History Mode Swiping
During swiping, the following optional delegate methods are called in the specified order:
The page
delegate method is invoked when the user starts a swipe action. This is the appropriate point at which to save information that you may need to restore, such as a page’s scrolled location.
Upon returning from the this delegate method, page
is hidden. In it's place the page controller shows a private view hierarchy to animate previously taken snapshots of the page history. This allows the page controller to remain responsive to the user without any required action by your application.
Next, if implemented, the page
delegate method is invoked. This delegate method is called after a physically successful swipe, but before the animation has completed. The supplied object is the page the user navigated to – the new selected
object in arranged
. If background loading tasks need to be initiated this is the appropriate time to do so. However, do not block the main thread or the animation will stutter or pause.
Finally, the pageControllerDidEndLiveTransition: delegate method is invoked after the swipe and swipe animations are complete. You should any position settings or other display specific state stored in the page
implementation. The page
is still hidden at this point and you must call complete
on the page controller instance to inform the instance to hide the private transition view and show page
. Often you do this immediately, however, if your content is not ready you can call this at a later.
Book Mode (View Controller Mode)
Book mode is designed to give you more control over the swiping process and to facilitate more user interface designs than just history, although you can use book mode to create a history user interface.
In this mode, page
is a container view and the content views are vended by view controller instances supplied by the delegate object.
To enable book mode, you must implement the following two methods in your delegate: page
and page
.
The page controller instance caches the view controllers supplied for each identifier and only asks it's delegate to create more if one does not already exists in its cache. If you have different type of views you want to swipe in, then supply a different identifier for each type.
When needed, you will be asked to prepare a view controller instance with a page via the optional delegate method page
. If you do not implement this method, then the represented
of the view controller that would have been passed to this delegate method is set as the object.
The delegate will be asked to prepare a view controller with a nil
object for each unique identifier it encounters. The NSPageController instance will use this to generate a default snapshot for that identifier.
When using the book mode, if page
is layer backed, live layers are used during transition instead of snapshots.
Generally, when using book mode, the set of pages are known and it is your responsibility to set the arranged
array property and initially selected page using the selected
property.
Delegate Method Invocation During Book Mode Swiping
During swiping, the following optional delegate methods are called in the specified order:
The delegate method page
is invoked when the user starts a swipe action. As in history mode, this is the appropriate point at which to save information that you may need to restore, such as a page’s scrolled location.
After returning from the page
delegate method, the page controller takes a snapshot of the view
in the specified selected
and then removes it from page
. The page controller replaces it with a private view hierarchy to animate previously taken snapshots. Unlike when building up a history, snapshots may not yet exist for the page being navigated to. In this case, a previously gathered default snapshot is used for that page's identifier. Regardless, if using a default snapshot or a previously gathered snapshot of actual contents, a view controller is prepared for the page being navigated to, and passed to the delegate. This view
is then asked to draw on a background thread while swiping continues. Note that at this point the view does not reside within a window. Once the background threaded drawing completes, the initial snapshot is replaced with the newly generated snapshot.
Next the page
delegate method is invoked after a physically successful swipe, but before the animation has completed. The supplied object is the page the user navigated to - the new object in the arranged
array at the selected
. Note that the page controller’s selected
has not been updated yet. If you need to start some background loading tasks, now is the time to do it. Do not block the main thread or the animation will stutter or pause.
Finally the page
method is invoked after the swipe and swipe animations are complete. The selected
is still detached at this point and you must call complete
on the page controller to hide the private transition view and update the selected
. Often you do this immediately, however, if your content is not ready you can call this at a later.
Completing the Page Controller Transition
An NSPage
instance uses a private view hierarchy during swiping. To create a seamless transition to the new content, it is your responsibility to inform the page controller when you are ready to draw the new content. Ideally, the new content should match the snapshot so the user is none the wiser. You inform the page controller to complete the transition by calling complete
. If needed, a view controller is prepared and then the content view is shown (or added) to the view hierarchy and the private transition view is hidden.
During page controller initiated animations, page
and page
are invoked on the delegate. Generally during page
you will call complete
. Programatic animations via the animator proxy do not call the delegate methods and you are responsible for calling complete
when the animation completes.This is easily done via a completion handler on an NSAnimation
grouping. For example:
//To instantly change the selectedIndex:
pageController.selectedIndex = newIndex;
//To animate a selectedIndex change:
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
[[pageController animator] setSelectedIndex:newIndex];
} completionHandler:^{
[pageController completeTransition];
}];