When using UIScrollView, how do I make the viewed ends to be complete, not partial - objective-c

How do I make the UIScroll view show complete views, not partial views?
(Note) I don't want it jumping to a complete view. It needs to move naturally or at least not immediate... needs to be smooth.
thanks

If your views are all of a constant size and you just want left/right or up/down scrolling, set pagingEnabled on the scroll view to YES. Supposing you wanted your scroll view to be 320x480 but to show the sides of the next and previous pages (so, e.g., each thing inside the view was 280 points wide), you'd size the scroll view to be 280x480 but set masksToBounds to NO.
If you have a more complicated scheme, install a scroll view delegate and act on scrollViewDidScroll:, paying attention to contentOffset. Probably you want to implement logic like:
add an observer on tracking; when it transitions to NO from 'YES' enable your logic inside scrollViewDidScroll: In there:
if a forced scroll is pending, cancel it
calculate where you'd force the scrolling to from the current position
schedule a scroll to there (which you'll effect via setContentOffset:animated:) for half a second from now
You can use a non-repeating NSTimer for the scheduling aspect. The logic you've essentially implemented is that if the user stops adjusting the view, wait for the natural inertia to end (which you'll detect by the 0.5 second gap since last movement), then transition smoothly to the nearest aligned position.

Check out Apple's documentation here first:
https://developer.apple.com/library/ios/#documentation/WindowsViews/Conceptual/UIScrollView_pg/Introduction/Introduction.html
Then the ScrollViewSuite sample:
https://developer.apple.com/library/ios/#samplecode/ScrollViewSuite/Introduction/Intro.html#//apple_ref/doc/uid/DTS40008904
I think you are referring to Paging techniques.

Related

How does NSSplitView track the cursor when using the thin divider style?

NSSplitView has a divider style called NSSplitViewDividerStyleThin which has become the norm on recent versions of OS X. It simply draws a 1-pixel wide solid line to represent the divider. However, the actual tracking area for the divider is about 3-5 pixels wide, allowing the user to click a little bit on either side of the divider to initiate a drag.
Since the split view's subview are laid out such that they really are only 1-pixel apart from each other, how is the split view tracking the cursor and mouse down events such that it's able to intercept any that occur slightly outside of the divider's "frame". Normally those events fall through to the underlying view (like a scroll bar flush against the divider).
First Thought:
That NSSplitView just had a 3-5 pixel wide, transparent subview for the divider that was placed over top of its two (or more) content views. But if I query the splitview for the number of subviews it has, it only returns 2. Likewise if I inspect using something like F-Script.
It's also been discussed in this Stack Overflow question that overlapping sibling views aren't really supported by AppKit:
Is there a proper way to handle overlapping NSView siblings?
Second Thought:
That maybe NSSplitView was just using an NSTrackingArea for the divider but if I ask the split view for its tracking areas, none are returned. And even if it was using a tracking area, can tracking areas on parent views override tracking areas on subviews? (I'd assume a subview's scrollbar, for example, would take precedence- but it doesn't in a split view.)
Third Thought:
That maybe NSSplitView was using some sort of transparent window that is overlaid on top, but unless I'm looking in the wrong place, I don't see any extra windows being created.
Fourth Thought:
That NSSplitView was using a local event monitor to track all mouse moved and mouse down events that are destined for the application and intercepting any that would be inside the split view's tracking area, even if the events technically over a subview.
So how is NSSplitView able to intercept mouse events that are outside of the visual 1-pixel wide space drawn for the thin border and instead occur over one of its subviews?
First, it appears that NSSplitView overrides -resetCursorRects and, within that method, adds cursor rects using -addCursorRect:cursor:. That's how it arranges for the cursor to change in the vicinity of its divider(s).
Second, it overrides -hitTest:. Mouse events are routed to views by -[NSWindow sendEvent:]. That uses -hitTest: to ask its views (e.g. contentView and the theme frame stuff) which descendant view was hit. When it finds which view was hit, it calls -mouseDown: (or similar, depending on the exact event) on that view. So, a view can prevent mouse events from being routed to its subviews. It can return itself from its override of -hitTest: even if the point is actually within one of its subviews and the mouse event will be delivered to it rather than the subview.

How can I set the end dragging velocity of a UICollectionView with custom flow layout?

I'm working on a UICollectionView with a custom flow layout subclass which, among other things, does some custom "paging". Everything's fine but for the fact that depending on how I drag, when I release and after - (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity gets called, the collection view (or some part of the UICollectionViewFlowLayout which I did not yet know I need to override) is controlling the velocity with which the animation of an item snapping happens.
That is, if I slightly offset an item from the center of the
collection view qnd release, it snaps back to its position pretty
quickly (desired).
But If I drag the item, say, half way past the collection view's
frame and/or change swiping directions while still dragging and then
release, the "snap" animation takes too long (not desired: I'd like the velocity to adjust so that the end drag animation takes the same amount of time always, regardless of distance).
I tried modifying the decelerationRate of the collection view but it doesn't seem to do anything. And I'm thinking of writing my own animation block in one of the collection view delegate methods, but I'm wondering if there is a different way (perhaps from within the flow layout subclass?).
Well, actually setting self.collectionView.decelerationRate = 0.; seems to work for now. It at least does not decelerate the scrolling and so it looks like constant velocity which is not exactly what I wanted but feels almost right.

UITableView w/ paging & momentum

OBSERVATIONS
I've discovered some "weird" things about how UITableViews / UIScrollViews scroll in Objective-C for iOS after you've lifted your finger:
A scroll view's "velocity" (as in scrollViewWillEndDragging:withVelocity:targetContentOffset:) is not based in time but rather based in "render" or "frame refresh" or the like; i.e., a "velocity" of 3.0 means that the scroll view will scroll 3.0 points before the next refresh, at which point it will scroll yet another 3.0 points if the "velocity" is still 3.0. (in contrast to an absolute time-based velocity)
"Refreshes" occur approx. 950 times per second, but this varies and is not dependable. (again, in contrast to an absolute time-based refresh rate)
Because UIScrollViews decelerate exponentially rather than linearly, decelerationRate is a geometric constant. This means that on every "refresh" of the scroll view, its "velocity" is set to its previous velocity times the "deceleration rate".
At some point, the scroll view's velocity is close enough to zero that it stops scrolling. I am not sure what this threshold is yet.
DESIRED BEHAVIOR / PROBLEM
I've derived an equation that can theoretically be solved to adjust the deceleration rate so that the scroll view can scroll naturally with momentum and settle close enough to the top of a table view cell to mimic "paging." (I also have an animation added to scrollViewDidEndDragging:willDecelerate: to adjust the content offset minimally. Adjusting content offset w/o adjust decelerationRate looks weird; the scroll view either jumps ahead or lurches backwards suddenly.)
This equation is: Ad^n + Bd + C = 0 where d = decelerationRate. Ideally I'd like something in the form of d(n, A, B, C), but my math PhD friend tells me that's not deterministically possible. He pointed me towards the following page: http://en.wikipedia.org/wiki/Root-finding_algorithm
I've also found that I can't animate a scroll view to a predetermined content offset using animation style UIViewAnimationCurveEaseOut because setContentOffset:animated: doesn't take an options: parameter. Otherwise I would "fake" scrolling to my precalculated content offset.
(I found this on SO but it didn't work for me: UIScrollview setContentOffset with non linear animation ?)
I'd like to use a table view because all of my elements in my scroll view will be cells essentially, and table views are best for displaying a list of cells. However, based on my difficulty getting my desired behavior, I might just make a paging scrollview, which will probably be easier. I can then just limit the number of "cells" visible in my custom "table view" or else load them post-hoc as necessary.
What are your ideas / thoughts / suggestions?
So far I have not found existing examples of what I would like.
ILLUSTRATION
OR
So this answer was obtained via a snarky but brilliant user on the #iphonedev IRC channel!
From within scrollViewWillEndDragging:withVelocity:targetContentOffset: you can just re-set the targetContentOffset to whatever you desire via *targetContentOffset = newTargetOffset;! Incredibly simple and elegant.
My mistake was trying to call [scrollview setContentOffset:newTargetOffset animated:YES] from within scrollViewWillEndDragging:withVelocity:targetContentOffset:, which didn't work at all, and therefore I set out to "solve" my problem in a more convoluted way that also didn't work at all.

Change size of window in Cocoa?

I have a window whose size I need to change when the user clicks on it. I am using [self setFrame:windowFrame display:YES animate:YES] to accomplish this.
Even though the window successfully changes size (I increase its height), it moves the contents of the window up with it. How do I prevent this from happening? I want the contents to remain in place.
I am on OSX Mountain Lion developing an app for OSX using Objective-C and Cocoa.
EDIT: Constraints and/or Springs and Struts will not work as I need to move the contents around after the window is resized.
Constraints and/or Springs and Struts will not work as I need to move the contents around after the window is resized.
In that case, you should use NSViewAnimation.
A single view animation can actually perform multiple animations to multiple views, and you can even do one to a window, despite the class's name and the fact that windows aren't views in Cocoa.
You create a view animation with initWithViewAnimations:, which takes an array of dictionaries. Each dictionary identifies the target (NSViewAnimationTargetKey) and what to do to it: Either change the target's frame (NSViewAnimationStartFrameKey and NSViewAnimationEndFrameKey) or fade the target in or out (NSViewAnimationEffectKey). For your case, you'll be changing the targets' frames.
When the user does the thing that causes the resize of the window, you'll need to compute the desired overall size of the window (taking care to adjust its frame's position so it doesn't grow off the screen), as well as the new frames—both positions and sizes—of your views. Everything that will move and/or change size, create a dictionary for it and throw it into the array. Then create the view animation.
An NSViewAnimation is a kind of NSAnimation, which provides all the methods for starting and stopping the animation, monitoring its progress, hooking into it, and chaining multiple NSAnimations together. If nothing else, you'll need to start the animation.
If you are using the Interface Builder to build these views, then I believe one approach is to set the "struts and springs." These are available under the "size inspector" and are the red arrows and bars above the "autosizing" label. Play around with these to get the effect that you want, but the general idea is that the arrows control how the size of the view adjusts to changes in the size of the parent view, and the bars control the relationship of the edges of the view to the edges of the parent view as the size changes.
In constraint-based layout, set the views around the edge of your window to be a fixed distance from their superview's edge.
Xcode will infer a lot of resizability from that; if anything still isn't resizing properly, adjust its constraints so that its width and/or height is no longer constant.
The easiest way is to move your views until blue lines show up in the editor. Each blue line corresponds to a rule in the HIG about how things should be lain out, and if you drop the view there, Xcode will create constraints matching those guidelines. For example, if you set a view 20 points from the right edge of its superview, you'll get a blue line for that, and if you drop the view there, you'll create a constraint that the view must remain that distance from that edge.
The superview isn't the only view with which you can create HIG-based constraints. You can also create guideline constraints between sibling views. For example, if you put a button next to another button at the appropriate distance, you'll get a blue line across that distance, and if you drop it, you'll create a constraint that those two buttons must remain that distance from each other.
If you want to do something really custom, the three buttons in the lower-right corner of the nib editor will let you create any constraint you want. What you have selected determines what constraints you can create; the nib editor's outline view will help you make sure you have the selection you want.
You are going to have to iterate through all of your subviews and change their frame positions based on the delta of your window frame.
so if you expand your window frame by 20 in all directions, all your subviews are going to have to increase their frame positions by (20,20) to offset the windows movement.

how to make a UIScrollView track something?

i want my app to be able to track a person when he moves 2 dimensionally upwards. I already have a vertical scroll view, and it works, but when i press a button i want it to track a little stick figure as he walks vertically, how can i control this? i thought of setting the scrollview.contentoffset to a certain position, but it just changes it one time when i want it to change it fluidly. i suppose i could make a timer that updates the contentOffSet every like .001 seconds or something, but i thought there maybe a better way.
Also, while i was searching the UIScrollView's methods, one of them was an isTracking method, which is a boolean, and I'm assuming it returns yes if the scrollview is tracking something, so given that i assume there is a feature in scrollview so that you can track things. Also, if to track things you have to use some other framework thing that would be helpful to know too
THANKYOU FOR ANY HELP!
The property tracking tells if the scroll view is currently tracking the user's movement. So that definitely won't help you out. (See David H's post for a better explanation for what the property indicates.)
However, you could use setContentOffset:animated: and set animated to YES. That results in a smooth transition to the new content offset.
Instead of calculating a reasonable content offset you might find it easier to specify an area that contains the stick figure. If so use scrollRectToVisible:animated:. Obviously animated should be set to YES.
1) don't use contentOffset = x;, use "- (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated". Experiment how often you need to send this message, but I'm guessing you can do it no more than 10 times a second and get nice results.
2) isTracking means the user has their finger on the scrollView but has not started dragging it. By testing "isTracking" and "isDragging" you can determine if the user is fiddling with your scrollView or not.