Intercept ScrollView zoom before it bounces back - uiscrollview

I want to receive a callback when a ScrollView's zooming view is scaled below a threshold so that I can dismiss the view controller. I'm currently using the bouncesZoom property which allows the zooming view to be scaled below the minimumZoomScale and snap back when the user ends the gesture.
I considered using scrollViewDidEndZooming with passes me a scale parameter. The problem is that this callback is triggered after the zooming view bounces back to minimumZoomScale. Therefore, the dismissal transition would include the bounce back and that isn't the desired effect.
I considered setting the bouncesZoom to NO when the zoomScale dips below the threshold. But that has the effect of snapping the zooming view instantly to the minimumZoomScale.
I also tried setting the minimumZoomScale to the current zoomScale once it dips below the threshold. This solves the bounce back problem because the zoomScale is now equal to the minimumZoomScale and so the bounce back is imperceptible. This has a visual glitch as well when I make that assignment. I'm not sure it is safe to set the minimumZoomScale to the current zoomScale while zooming. So this approach isn't ideal either.
What can I do to hook into the gesture end event and avoid the bounce back?

Related

Unity slider interaction with controller

So I have a laser pointer attached to my controller, shooting rays and triggering events whenever I press the tigger.
Tho it still has one or two buggs (Unity VR Controller UI Interaction), I have a more general question.
The UI needs some kind of slider. And I can't figure out how to make my pointer collide with the slider and behave in a slider-ish way. When I press my button, it currently ends up setting the slider to zero and not letting me drag it.
Thankful for any tipps on how to make them actually slide, since my ridgidbody-cube doesnt move and doesn't allow movement! Unitys documentation on e.g. the beginDragHandler isn't really helpful...
Cheers, Flo
If your enter/exit events already works, juste check the x (or the axis corresponding on your slider) of your "laser" every frame or so, and increase/decrease the value of the slider based on the delta between two check.
Hope it is clear, it should not be hard if you already have your events sets up !

Discard mouse events on NSWindow based on click position

Let's say I have a floating, borderless, circular NSWindow.
It is circular because the content view simply draws a red circle.
That content view needs to be layer-backed ([contentView setWantsLayer:YES]), because I'm applying CoreAnimations on it, e.g., animated scaling.
Usually, the clickable area of a NSWindow is defined by the transparency of the pixels of the content view. However, once the content view of a NSWindow becomes layer-backed, transparent areas will also receive clicks, unfortunately.
In my case, this is a serious problem, because I only want to receive clicks within the radius. But now, a click within the rect of the window, but beyond the circle radius, will activate the window (and thus, the entire app), which it shouldn't. Also the window is draggable via the corner of its content view.
My initial thought was to implement [NSWindow sendEvent:] in a subclass and check whether the click was performed within the radius, using [theEvent locationInWindow]. I thought I could simply discard the event, if it's beyond the radius, by not calling [super sendEvent:theEvent] then. This however did not work: I noticed, that the mouseDown:; window method is called even before the sendEvent:; method.
I've search a lot but the only idea I found, was to have a proxy like non-layer backed NSWindow on top of the window, which delegates clicks conditionally, but this led to unpredictable UI behavior.
Do you guys have any idea, how to solve it?
So after a few weeks, I came to the following results:
A) Proxy window:
Make use of a non layer-backed proxy window, which is placed on top of the target window as a child window. The proxy window has the same shape, as the target window, and since it is not layer-backed, it will properly receive and ignore events. The proxy window delegates all events to the target window by overwriting sendEvent:. The target window is set to ignore all mouse events.
B) Global Mouse Pointer observation:
Install both a global and local event monitor for NSMouseMovedMask|NSLeftMouseDraggedMask events using addGlobalMonitorForEventsMatchingMask and addLocalMonitorForEventsMatchingMask. The event monitors disable and enable ignoring mouse events on all registered target windows based on the current global mouse position. In the case of circular windows, the distance between the mouse pointer and every target window must be calculated.
Both approaches work well in generally, but I've been experiencing some unpredictable misbehaviors of the child window approach (where the child window is 'out-of-sync' of its parent's position).
UPDATE: Both approaches have some significant disadvantages:
In A), the proxy window sometimes may be out of sync and may be placed slightly off the actual window.
In B), the event monitor has a big impact on battery life while moving the mouse, even if the app is not the front-most application.
If you want to Discard mouseDown event based on position you can use the:
CGPathContainsPoint(path,transform,point,eoFill):Bool
Setup your path to match your graphics. Circles, ellipses, rectangles, triangles or paths and even compositional paths (paths with holes in them).

scrollRangeToVisible doesn't update contentOffset until a refresh

I am calling scrollRangeToVisible on a UITextView to scroll to the top. I then ask for its contentOffset, and this comes back as (0,0).
However, after a screen refresh, the contentOffset comes back as (0,8) - probably something to do with the font of the text. And the (0,8) is the correct value as it works for the rest of my app if I hard-code it in.
My problem is that I need to get the (0,8) as soon as I have made the call to scrollRangeToVisible. Perhaps it is not there because it needs that screen refresh - if so, how can I force a refresh and return immediately to do my further processing.
Alternatively, it may be animating the scrollRangeToVisible, and therefore the animation is right at the start when I get contentOffset. So how do I stop the animation?
NSRange start = NSMakeRange(0,1);
[self.recipe_view scrollRangeToVisible:start];
start_scroll = self.recipe_view.contentOffset;

How do you animate a scroll and zoom atomically?

I have a custom view in my application, which is layer-backed and embedded in an NSScrollView. I allow the user to zoom in (which is accomplished by increasing the size of my custom view). I'm having trouble zooming in on an arbitrary point, though, since the NSScrollView keeps getting in the way and causing the view to jump around (typically to the view's origin) before I point it to the new scroll point. I would really like to use a CAScrollLayer, since I know I could definitely get the zooming right with it and have it move smoothly, but then I lose all built-in scrolling facilities.
Is there any way to leverage CAScrollLayer within an NSScrollView, possibly backing the NSClipView? If not, what purpose does CAScrollLayer actually serve? Is it possible, with a different approach, to change my view's size and the scroll point atomically and have that animate?
In short, is CAScrollLayer completely useless, or mostly useless?
Update
I've gotten my inner view to jump around less by making a CALayer subclass to display my view's contents. Rather than sizing with layout constraints, I have it sizing in an override of -resizeWithOldSuperlayerSize:. I still can't change the frame size and origin of my view simultaneously and get a smooth animation, though. To get a sense of what I'm looking for, open an image in Preview and zoom in and out. It zooms about the center of the image in a smooth manner.
In the limit, you can use an NSScroller instead; that way you would be able to use CAScrollLayer, if that’s your preferred implementation.
Note that on some (older) versions of Mac OS X, NSScroller has a bug that causes it to invoke an Apple private method on its containing view. You’ll know if this happens because you’ll get an exception about your custom view not responding to a method starting with an ‘_’.

How can I trigger Core Animation on an animator proxy during a call to resizeSubviewsWithOldSize?

I have some NSViews that I'm putting in one of two layouts depending on the size of my window.
I'm adjusting the layout when the relevant superview receives the resizeSubviewsWithOldSize method.
This works, but I'd like to animate the change. So naturally I tried calling the animator proxy when I set the new frames, but the animation won't run while the user is still dragging. If I release the mouse before the animation is scheduled to be done I can see the tail end of the animation, but nothing until then. I tried making sure kCATransactionDisableActions was set to NO, but that didn't help.
Is it possible to start a new animation and actually have it run during the resize?
I don't think you can do this easily because CA's animations are run via a timer and the timer won't fire during the runloop modes that are active while the user is dragging.
If you can control the runloop as the user is dragging, play around with the runloop modes. That'll make it work. I don't think you can change it on the CA side.
This really isn't an answer, but I would advise against animating anything while dragging to resize a window. The screen is already animating (from the window moving) - further animations are likely going to be visually confusing and extraneous.
CoreAnimation effects are best used to move from one known state to another - for example, when a preference window is resizing to accompany a new pane's contents, and you know both the old and new sizes, or when you are fading an object in or out (or both). Doing animation while the window is resizing is going to be visually confusing and make it harder for the user to focus on getting the size of the window where they want it to be.

Resources