How do I add some extra drag to the UIScrollView physics. It scrolls just a little too fast for what I am doing. (I don't want to disable altogether, I still like the rubber band effect when you get to the end or beginning of the view.) Is there any way to slow it down?
Actually, after reading the Documentation I discovered the property for UIScrollView that slows down the scrolling, so Apple does make this readily available for anyone else looking for this:
scrollView.decelerationRate = UIScrollViewDecelerationRateFast;
Even though it says fast, it is speeding up deceleration which in effect slows the scrolling down. This was exactly what I needed. And no worry of patents. :)
Actually you can make block animation with ScrollToRowAtIndexPath:... with animated set to NO. This way block animation will animate underlaying variables manually using selected time.
As UICollectionView is subclass of UIScrollView You can use scroll view delegate method.
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
targetContentOffset->x = (targetContentOffset->x - scrollView.contentOffset.x) / 6 + scrollView.contentOffset.x;
targetContentOffset->y = (targetContentOffset->y - scrollView.contentOffset.y) / 6 + scrollView.contentOffset.y;
}
This is simply not possible right now. Maybe Apple will one day extend the API to allow it, but until that day, you're stuck with the default speed.
The only other option would be your own scrolling implementation. This isn't trivial if you want all the nice polish such as the rubber band effect and the way the scrolling will lock horizontally or vertically. Not to mention flicking to scroll.
Also, Apple has a patent for some of those scrolling behaviours, so writing your own might even be dangerous. Source: "Apple's touch-screen patent".
A computing device, comprising: a touch screen display; one or more processors; memory; and one or more programs, wherein the one or more programs are stored in the memory and configured to be executed by the one or more processors, the one or more programs including: instructions for detecting one or more finger contacts with the touch screen display; instructions for applying one or more heuristics to the one or more finger contacts to determine a command for the device; and instructions for processing the command; wherein the one or more heuristics comprise: a vertical screen scrolling heuristic for determining that the one or more finger contacts correspond to a one-dimensional vertical screen scrolling command rather than a two-dimensional screen translation command based on an angle of initial movement of a finger contact with respect to the touch screen display; a two-dimensional screen translation heuristic for determining that the one or more finger contacts correspond to the two-dimensional screen translation command rather than the one-dimensional vertical screen scrolling command based on the angle of initial movement of the finger contact with respect to the touch screen display; and a next item heuristic for determining that the one or more finger contacts correspond to a command to transition from displaying a respective item in a set of items to displaying a next item in the set of items.
But of course, I am not a lawyer.
Related
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.
I am creating an app for practice that is a simple drawing app. The user drags his/her finger along the screen and it colors in a 100px x 100px square.
I currently achieve this by creating a new colored UIView where the user taps, and that is working. But, after a little time coloring in, there is substantial lag, which I believe is down to there being too many UIViews as a subview of the main view.
How can I, and others who similarly create UIViews on dragging a finger reduce the lag to none at all, no matter how many UIViews there are. I also think that perhaps this is an impossible task, so how else can someone like me color a cube of the size stated above in the main view on a finger dragged along the screen?
I know that this may seem like a specific question, but I believe that it could help others understand how to reduce lag if there are a very large amount of UIViews where a less performance reducing option is available.
One approach is to draw each square into an image and display that image, rather than keeping around an UIView for each square.
If your drawing is simple enough, though, you can use OpenGL to do this, which is much faster. You should look at Apple's GL Paint Sample Code which shows how to do this in OpenGL.
If your drawing is too complex for OpenGL, you could create, for example, a CGBitmapContext, and draw each square into that context when the user drags their finger. Whenever you draw a new square into that bitmap, you can turn the bitmap into an image (via CGBitmapConxtextCreateImage) and display that image an a UIImageView.
There are two things that come to my mind:
1- Use Instruments tool to check if you are leaking any memory
2- If you are just coloring the views than instead of creating images for each of them, either set the background color property of UIView or override the drawRect method to do custom drawing
I think what you are looking for is the drawRect: method of UIView. You could create your custom UIView (you propably have that already) and override the drawRect method and do your drawing there! You will have to save your drawings in an array or another container and call the setNeedsDisplay method whenever the array content is changed.
According to Apple's Document, any touchable view should at least have a touchable area of 44 by 44 points.
I did some investigation, and it looks like a good solution is to over-write pointInside like following:
- (BOOL) pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
CGRect l_bounds = self.bounds;
float lengthOf44 = [self convertLengthFromScreen:44];
CGFloat l_widthDelta = lengthOf44 - l_bounds.size.width;
CGFloat l_heightDelta = lengthOf44 - l_bounds.size.height;
if (l_widthDelta < 0)
{
l_widthDelta = 0;
}
if (l_heightDelta < 0)
{
l_heightDelta = 0;
}
l_bounds = CGRectInset(l_bounds, -0.5 * l_widthDelta, -0.5 * l_heightDelta);
if (CGRectContainsPoint(l_bounds, point))
{
return YES;
}
return NO;
}
However, this solution has many draw backs. Given a small view having a 5 by 5 pixel size.
Suppose the small view is very close to the edge of super view, then this method will cause a half of the extend touchable area outside the superview, and The touchable area is actually 22 by 44. In that case I need to further overwrite superview's pointInside.
Suppose we have three small view like which are very close to each other, Then the upper view will always win the touch.
Ideally, I want the view closest to the touch point win.
Is there a better or more elegant solution other than this please?
To better illustrate my concerns, let me give an example: Everyone should familiar with iOS's Copy Blue Hint Dot (not sure if I am using the correct word, but please see the image).
http://i.stack.imgur.com/FtjYi.png
Suppose we are going to implement it (the copy blue dot). For that blue dot, I have no idea how Apple implemented. However, I assume that blue dot is a kind of UIImageView, and assume it is a subview of some UITextView (this may be not sure). Can that UIImageView response to touch? In my solution, it can response touch by overwriting pointInSide.
My two question here in this context can be translated to be:
what if the word we are selected is too close to the UITextView edge? So the ideal touch area of that blue dot might be even outside the superview (UITextView).
what if the upper blue dot is too close to lower blue dot and we drag from middle point of two dots (it looks like lower blue dot have a higher chance to win.)
You missed the point of the docs.
Apple UI guidelines suggest that you do not make touchable areas (which are usually represented by a view of some kind, button, image whatever) less than 44X44 for usability reasons. (the size of a fingertip)
If you have a view 5X5, its not the right view to receive touch events and you should use a super view of that view to receive the touch events for the child.
(edited to remove my incorrect statement that this is not possible)
You are taking the wrong aproach, first you should not mess with the touch detection from the ios, it is preconfigured and optimized to function correctly with finger touch, thats why if u touch you usually get the reading a bit higher than where you actually touched.
It seems you want to do some sort of "where did the user touched in a place", If i was you i would get a uiview for example which is my complete touchable area and just add the rest of the elements there, you can put them wherever you want, they can even overlap or anything, just make sure the superview is the one receiving the touches and do some math to see which one was the closest to the elements center. Even if they are buttons you can disable the interaction and programatically show their state changes. as if they were pressed or something.
To restate my point. Use just 1 big touch area and do the math for the rest, in this way you will have complete control over the touch areas and such.
(Then again i could be completely misunderstanding your purpose)
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.
I'm trying to draw a graph that is indefinitely large horizontally, and the same height as the screen. I've added a UIScrollView, and a subclass of a UIView within it, which implements the -drawRect: method. In the simulator, everything works fine, but on the device, it can't seem to draw the graph after it reaches a certain size.
I'm already caching pretty much everything I can, and basically only calling CGContextAddLineToPoint in the -drawRect: section. I'm only drawing what's visible on the screen. I have a delegate to the UIScrollView which listens for -scrollViewDidScroll: which then tells the graph to redraw itself ([graphView setNeedsDisplay]).
I found one method that tells me to override the +layerClass method and return [CATiledLayer class]. This does allow the graph to actually draw on the device, but it functions very poorly. It's incredibly slow to actually draw, and the fade in that occurs is undesirable.
Any suggestions?
Well, here's my answer: I basically did something similar to how the UITableView works with cells: I have an NSMutableSet of GraphView objects which store unused graphs. When a section of the scroll view becomes visible, I take a graph view from that set (or make a new one if the set is empty). It already had a scrollX property to determine which part of it was supposed to draw. I set the scrollX property to the correct value and, instead of using the screen width, I gave it an arbitrary width to draw. When it goes out of the scroll view, it is removed from the UIScrollView and added to the set.
I wonder though if I really even need to remove them when they go outof the view? It may be prudent to try leaving them in and remove the ones not on screen only if I get a low memory warning? This might get rid of the pause whenever it needs to redraw a section of graph that hasn't changed.
My saving grace here was that my GraphView already was set up to draw only a portion of the graph. All I needed to do then was just make more than one of them.
I think this is a limitation of the iPhone graphics hardware. Through experimentation, I have seen that the iPhone will refuse to draw a frame that is bigger than 2000 pixels in either height or width. It probably has something to do with limited size for frame buffers in hardware.
Watch the 2011 WWDC session video entitled "Session 104 - Advanced Scroll View Techniques".
Thanks, that's helpful. One question -- what did you use for the contentSize of the UIScrollView? Does UIScrollView tolerate large content sizes (over 2000 px) as long as you're not creating buffers to fill the entire space in your content view? Or are you keeping the UIScrollView a constant size (say, 2 screen widths) and resting the UIScrollView contentOffset property each time you draw (using scrollX instead of the contentOffset to store your position)?
I think I answered my own question (the latter seems like a better alternative), heh, but I'll go ahead and post this in case other people need clarification.