NSPageController resizing and snapshots - cocoa

I have an NSPageController in book mode with two pages and each contains an NSTableView. If I start my application and resize it vertically and then swipe to the other page the snapshot used in the swipe animation is of the NSTableView before the resize. This view swiping in only covers part of the previous view and this looks terrible. Is it possible to get the NSPageController to invalidate the snapshots when the view is resized?

The PictureSwiper example does exactly this by setting the frame of the view whenever the window is resized. However, you need to have your layout/resize constraints set. Whenever the view size changes, the constraints will cause NSPageController's view objects to be resized as well. If you're doing something unique with your view layout/size, you will need to manually resize as in the example linked.
Also, snaphots are generated on-the-fly. From the arrangedObjects method documentation of NSPageController:
The delegate will be asked for snapshots as they are needed.
And this is useful to keep in mind:
When using the book mode, if pageController.view is layer backed, live layers are used during transition instead of snapshots.

Related

How to place an NSButton over an NSTableView

1) No table behind button
2) Table loaded
3) After scrolling
If I place a button over an NSTableView I get artifacts being left behind after scrolling. Does anyone know how to fix this?
My current solution is just to split the section with the table into 2. The lower portion is a disabled button in the background.
Try the NSScrollView method
- (void)addFloatingSubview:(NSView *)view forAxis:(NSEventGestureAxis)axis
All tableviews should normally be inside a ScrollView.
I had a similar problem and solved it, see Why does an NSTextField leave artifacts over an NSTableView when the table scrolls?. Essentially OSX will draw the contents of the table and let the parent scroll view move the cached contents avoiding redraws to be performant. By subclassing the parent scroll view it can be forced to refresh the table hooking the reflectScrolledClipView: method. Then the whole table, including overlays, will be redrawn with each scroll.
That's because the scroll view is set to copy its contents when scrolling and only redraw the newly-uncovered part as a performance optimization. To turn that off, use
myTableView.enclosingScrollView.contentView.copiesOnScroll = NO;
though that will make scrolling use more CPU (you can also do this in the XIB, look for a 'copies on scroll' checkbox).
Probably a better approach would be to switch the scroll view and the button to be layer-backed:
myTableView.enclosingScrollView.wantsLayer = YES;
myButtonView.wantsLayer = YES;
(Again, you can set this in the 'Layers' inspector of the XIB file, where you can click a checkbox next to each view to give it a layer) Now, the scroll view will only copy stuff from its own layer (which no longer includes your button). Also, now all the compositing of the text view will be done using in the graphics card. This works fine with an opaque push button, however, if you put text on a transparent background in its own layer (e.g. if you have a transparent pushbutton with a title), you lose sub-pixel anti-aliasing.

UIScrollView zooming and GUI management

I would like to programmatically allow the user to zoom away from a current page inside of UIScrollView and present them with an overview of multiple pages. Then allow them to touch/choose one of the pages to zoom in.
I have multiple sub ViewControllers for each page. The important aspect is that each ViewController contains detailed information, so I want most of that information to be visible when they get a "birds eye view" of what's happening.
What's the best way to do this?
Additional detail: pretend each UIViewController has a UiTableView within them. There's 5,6,3,1,0,10 Cells in each of these (respectively) is there a way to show all Cells at once in the larger view?
Perhaps is there a way to screenshot the Views and present them as smaller objects?
Currently I have the UIPinchGestureRecognizer already working, just need a way to control the transition of these viewcontroller into the middle. Is there a way to screenshot each controller and transition to a different view for selection?
If You want it for a pdf viewer, then maybe you can implement something similar to this:
https://github.com/vfr/Reader
When corresponding button is pressed - modally rises up a gridview with smaller pages. When taped on any - it then opens that page.
If it's not about pdf viewer, and You still desire the pinch-zoom effect, then maybe you can implement two different views - where - on one will be in grid - all viewcontroller thumbnails, and on other - scrollview. When you pinch -zoom out - scrollview alpha changes to 0, while gridview list alpha changes to 1. When taped on any viewcontroller thumbnail - alpha changes.
If You still want without fade-in fade-out, then it's even harder. On each pinch zoom movement, you need to recalculate each viewcontroller positions and sizes. and start moving them where they should be.
Hope that helps.. somehow..

Drawing an NSTableView's background outside of its bounds

I'm having a problem since Lion introduced elastic scrolling (pictured below). When you scroll my table view (cell-based, with alternating row colors) beyond its bounds, the background doesn't draw. I've tried doing my own drawing in -[drawBackgroundInClipRect:], but it seems like you can't exceed the bounds of the table view. How can I extend the background into elastic scrolling territory?
In Answer to Your Question
A view drawing outside its bounds is generally a no-no. When using alternating background colors, the NSTableView draws its background directly. But in a view-based table view, NSTableRowView is used and if it has its own background color, this is poses even more challenge.
The Bad News
The assemblage of NSScrollView (and its various parts), NSTableView, and NSTableHeaderView is complicated on its own. Once you throw view-based functionality into the mix (where each row has a view and each cell has their own view, and each are reused, animated around, etc.), overriding this behavior "is no way to go through life, son!" ;-)
The Good News
The issue of alternating background colors not extending in an elastically-stretched scrolled table view has been resolved (at least on 10.10, that I can tell), so this is no longer an issue unless you have row/cell views with custom backgrounds or just background colors.
A General Solution For Custom Document (Scrolled) Views
For all other scrolled views with ruled backgrounds you wish to extend for elastic scrolling, you'll need a custom NSScrollView subclass that expects a document view (your custom scrolled view) to conform to a protocol you define (like -(NSImage *)backgroundImageForClipViewBounds:). The scroll view would observe its content view (NSClipView) for bounds change notifications and flag itself for display. When the scroll view's -drawRect: is called, it would then ask the scrolled view for its -backgroundImageForClipViewBounds: (via your protocol) and draw it into itself, thereby making the scrolled view's "infinite background" its own.
I haven't tested this theory but I believe it would work. I hope this helps.

Resize animations on a scrollview

I am trying to create a resize toggle animation on this simple custom TUIScrollView class (from TwUI open source project and very similar to UIScrollView) that I have built. It is called TUILayout and supports horizontal layout as well as vertical, animated insertions and removals and has a more declarative way of supplying data to it's cells that I prefer over delegation. It recycles views similar to TUITableView or UITableView. Anyway if you want to follow along, it's just one class and is here.
https://github.com/mrjjwright/TUILayout.
In the example code, the user clicks the blue button in the lower left and all the rows shrink smoothly to a size where the user can reorder and delete some rows (right click on a row in the example to see this in action), etc... and then the user toggle the rows back out to their original size by clicking the blue button again.
While doing this resize in setObjectHeight:animated I first resize my model objects that represent the rows, recalculate and set the contentSize on the TUIScrollView, cycle in all the new views (say 10 more views will fit in the shrunk view so dequeueReusableView and addSubview gets called 10 times) and finally I animate the frames of all the views to their size and location in layoutSubviews.
The result is that the scrollview correctly shrinks to a size where the scrollbar no longer displays, the views that are on screen animate smoothly down to their reduced size, but the newly added subviews that can now fit in the visibleRect animate in much later as one block of subviews.
So all the newly added subviews lag behind the views that were on the screen and I can't figure out why the animation isn't all happening together. I have tried lots of different combos of things with no luck including CATransactions. I am wondering if it has to with how a CAScrollLayer works or if somebody can help me think through this.
The more general issue is how to smoothly handle resizing animations on scrollviews that recycles their views and I have looked at several other grids out there in the iOS world and have got some inspiration but am looking for more.
Thanks!
I think I might have solved my own issue here (as I was making my bed this morning it hit me). I forced the current runloop to run after cycling in all the necessary subviews and very importantly not setting the contentSize of the scrollview until after the run loop completes and adds the needed subviews for the animation. In order to get the run loop to fire I used the trick from this SO question:
skipLayout = YES;
[[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode beforeDate: [NSDate date]];
skipLayout = NO;
If skipLayout is set TUILayout just returns from layoutSubviews so that the views just added are not immediately removed by the recycling layout code. Forcing the run loop to run made sure that all subviews were on the screen for the animation. After this I performed the resize animated layout. I updated the code on github if anybody is interested. I will leave this question open for a while to gain some further insight.

How to "stick" a UIScrollView subview to top/bottom when scrolling?

You see this in iPhone apps like Gilt. The user scrolls a view, and a subview apparently "sticks" to one edges as the rest of the scrollView slides underneath. That is, there is a text box (or whatever) in the scrollView, that as the scrollView hits the top of the view, then "sticks" there as the rest of the view continues to slide.
So, there are several issues. First, one can determine via "scrollViewDidScroll:" (during normal scrolling) when the view of interest is passing (or re-appearing). There is a fair amount of granularity here - the differences between delegate calls can be a hundred of points or more. That said, when you see the view approach the top of the scrollView, you turn on a second copy of the view statically displayed under the scrollView top. I have not coded this, but it seems like it will lack a real "stick" look - the view will first disappear then reappear.
Second, if one does a setContentOffset:animated, one does not get the delegate messages (Gilt does not do this). So, how do you get the callbacks in this case? Do you use KVO on "scroll.layer.presentationLayer.bounds" ?
Well, I found one way to do this. When the user scrolls by flicking and dragging, the UIScrollView gives its delegate a "scrollViewDidScroll:" message. You can look then to see if the scroller has moved the content to where you need to take some action.
When "sticking" the view, remove it from the scrollView, and then add it to the scrollView's superview (with an origin of 0,0). When unsticking, do the converse.
If you use the UIScrollView setContentOffset:animated:, it gets trickier. What I did was to subclass UIScrollView, use a flag to specify it was setContentOffset moving the offset, then start a fast running timer to monitor contentOffset.
I put the method that handles the math and sticking/unsticking the child view into this subclass. It looks pretty good.
Gilt uses a table view to accomplish this. Specifically, in the table view's delegate, these two methods:
– tableView:viewForHeaderInSection:
and – tableView:heightForHeaderInSection:

Resources