I'm trying to create some sort of timeline view like in video editors: media elements in a row, which are UIView's. I can successfully drag these views inside currently visible part of scroll view using UIScrollView touch events like touchesBegan and touchesMoved. I want to scroll the scroll view once subview is dragged to one of the scroll view edges. The best I can think of now is to create a timer that will scroll the view while user holds the subview with the finger near scroll view edge.
There's a lot of questions here on the same topic, but I was unable to find one that covers scrolling.
Is there a good way to do this? Should I use gesture recognizers instead?
Thank you in advance.
Actually what you want IS a timed event. As soon, as the user is at the edge of the scrollview, you start a timer, which regularly increases the contentOffset. If you don't like your animation results (i guess you're using setContentOffset:animated:?), just try another timing and distance of animation.. I guess you have to try some different settings. What I would try first is 1px at a time. Perhaps every 0.3 second?
If that doesn't work you could also try another "extreme". Start a single animation, when the user reaches the edge, which animates the contentOffset until the end of the contentSize. But over a large timespan so the movement is slow. If the user stops dragging, or moves out of the edge, stop the animation at the current position. That would even be a solution without a timer, because the animation would be your timer itself.
I seriously doubt gesture recognizers would part of a good solution to this since they tend to be most helpful with discreet gestures.
I don't think I can improve on your general direction based on the assumption, implied above, that you are looking for continuous/gradual scrolling.
What I suggest instead is that you consider designing this to use a paged scrolling approach. When your user drags the object to the edge of the scrollview, cause the scrollview to move one page in that direction (by setting the contentOffset to move in that direction according to the bounds of the scrollview). When that even occurs, move the object slightly out of the "hot zone" at the edge of the scrollview so that the user is forced to explicitly express that they want to move another page, or something along those lines - that is, since the design approach depends on this "paging events" you need to implement some sort of gestural system for the user to keep paging.
I suppose you could use a timer in that same situation, so that if the user maintains the position and touch for another second, you would page again.
Related
I'm as beginner as you can get when it comes to iOS animation.
I know you can do fixed (non-gesture-controlled) animations, where you animate a property of a view over a fixed period of time. This however is entirely different than using a gesture to control the animation.
You know how you fold a sheet of 8x11 paper to put it in an envelope and mail it... you fold it in thirds. Well basically, my boss wants an interface such that 2/3rds of it is shown on the screen at a time, and the other third is slid on/off screen with a gesture. So basically, the screen would show either thirds 1 and 2, or thirds 2 and 3 depending on whether you swipe left or right.
Now this also means doing things like snapping/rubberbanding, bouncing, acceleration/deceleration, sticky. I have no clue where to even start to do something like this. I'm assuming those types of motions are not already built into any of the iOS framework and if you want snapping/rubberbanding, bouncing, acceleration/deceleration, you'd have to program that entirely from scratch.
Like how would a view know my artificial snap/bounce points are, and sticky behavior, such that you remove your finger from the screen before an arbitrary position is reached, and the view bounces back to it's previous position.
Where would you suggest I start on researching how to drive animations with gestures?
I suggest you take a look at Core Animation. You can do some really complex animations including accelerations, deceleration, bounce and others.
You could easily create a UIPanGestureRecognizer to track when someone has dragged their finger across the screen. Attach an action wasDragged to your gestureRecognizer
From the documentation:
A panning gesture is continuous. It begins (UIGestureRecognizerStateBegan) when the minimum number of fingers allowed (minimumNumberOfTouches) has moved enough to be considered a pan. It changes (UIGestureRecognizerStateChanged) when a finger moves while at least the minimum number of fingers are pressed down. It ends (UIGestureRecognizerStateEnded) when all fingers are lifted.
In wasDragged check what state the gestureRecognizer is in. If state is UIGestureRecognizerStateChanged, then you can adjust the size or position of your UIView so that it appears like you are dragging it out. If state is UIGestureRecognizerStateEnded, check whether the point at which the gesture ended is greater than your threshold point (e.g. halfway across the screen). If it isn't, then snap the view back using an animation, if it is then snap the view into where you want it.
Hope this makes sense.
I'm working on a control that looks like a wheel and is used for fast or precise scrolling of content. Here's an example from coach's eye app:
My first take looks like this:
Currently the vertical lines are implemented as set of UIViews. Sure enough these vertical line views could be easily replaced with image views to customise the look.
Each time user pans:
I modify frame.origin.x on all of the vertical line views
If some of the views go off screen - I remove them
If there's a gap on the left or on the right I create new views to fit the place
When user finishes the pan gesture, I start repeat NSTimer (with like 0.05 of a second) to animate decelerating of wheel movement. On each loop of timer in a nutshell:
Calculate distance to move lines and move them
Calculate velocity deceleration amount and adjust the velocity
A couple of questions:
Are there any iOS frameworks (e.g. CoreGraphics, CoreAnimation, UIKitDynamics) that are suited better for implementing these tasks than UIKit APIs I have used?
Can you suggest a better / more correct way to implement "infinite scrolling wheel" control ?
Can you suggest a better way to implement deceleration after user finishes panning the wheel?
Thanks
I had made my "infinite scrolling wheel" control by customizing SSRollingButtonScrollView. I think you should look at it once. I hope it will help you.
Apple already have a nice algorithm implemented for acceleration and deceleration - in UIScrollView. And you can use that, behind the scenes, to control other views / interaction. This is enabled by connecting the pan gesture from the scroll view to another view and acting as the delegate of the scroll view.
Check out the Enhancing User Experience with Scroll Views WWDC video for guidance.
I am trying to build a UI in iOS in which there is an outer paging UIScrollView inside of which are multiple UITableViews, each representing a page. The problem I am encountering is that the angle at which I must swipe to trigger the outer paging UIScrollView to scroll horizontally is too narrow. Here is a screenshot showing what I mean.
Essentially a user has to swipe at a near-perfect horizontal angle in order for the UITableView to ignore the gesture and pass it along to the outer UIScrollView, thereby triggering a horizontal page movement. This behavior quickly becomes annoying.
I have done extensive research on stack, to no avail. Some people suggest setting directionalLockEnabled, but that has no effect here (the inner UITableView has its direction locked, it's just too greedy with the angles it interprets as going in its direction). Others suggest modifications to the contentSize, but the contentSize of the views is, in point of fact, correct. Still others suggest using Gesture Recognizers, but those seem to have a delay before they kick in.
Can anyone suggest how to accomplish the behavior in the image above?
Scenario:
Horizontally scrolling UIScrollView with reused page views (so that there are only few page viewcontrollers which are being reused similar way like UITableView cells). So that they are able to be updated with new content and reused I need to know exact position of UIScrollView's content view (offset). This works great.
Now I need to implement custom scrolling animation - I mean to programatically move the content view, so that user touches some buttons, and the scroll view scrolls to desired position with this custom animation. The movement can be very quick and very far. I can not use Core Animation for this, as I could not track position during animation (CA reports you only start and end of the movement). So I decided to use CADisplayLink, and calculate each UIScrollView content for each position. This works great too.
The only problem is, that sometimes I see stroboscopic effect - say I am moving the content to the right, and it looks like it is moving to left. If I look at builtin animation within UISCrollView using setContentOffset:animated:, the animation is smooth and nice. Does anyone know, how to get rid of this stroboscopic effect?
Most likely your issue is that timestamp is a double and you're assigning it to a float
Floats have 7 digits while Doubles have 15-16 digits.
So in other words you're experiencing data loss. By using double you should see a silky smooth animation.
I have noticed a slight delay on the highlighted state of a UIButton when touched down if it is inside a UIScrollView (or a table view). Otherwise, the highlighted state is pretty much instantaneous.
I surmise this must be by-design to provide a chance for user to scroll. But it just seems like the button is unresponsive to me. Is there a way to fix this?
Indeed, it's a design choice. It needs this small time to differentiate a scroll (panGesture) from a tap. If you eliminate this delay, then the user won't be able to scroll if he places the finger on top of the button, which is not good user experience.
Because a scroll view has no scroll bars, it must know whether a touch signals an intent to scroll versus an intent to track a subview in the content. To make this determination, it temporarily intercepts a touch-down event by starting a timer and, before the timer fires, seeing if the touching finger makes any movement. If the timer fires without a significant change in position, the scroll view sends tracking events to the touched subview of the content view.
from the UIScrollView Documentation
I wouldn't recommend disabling the delay, but if you insist, you can set it in interface builder (select the Scroll View, and on the right panel, right under "Bounces Zoom"), or using this code:
scrollView.delaysContentTouches = false