Suppose I have a VFM full of both focusable and non-focusable fields. Since most of them are spread out far apart, the movement from one focused field to another is jerky at best, even with NullFields in between. In other words, it just sets the current y position to the next focused field without smoothly scrolling the screen.
What I want to achieve is to be able to scroll at a fixed rate between fields, so it it doesn't just focus from one to another that instantaneously. After reading up on how to do this, it is a matter of overriding moveFocus and setting it via a TimerTask from an accessor method to set moveFocus, as per this link. However, I haven't seen a practical implementation of how to do this, complete with the routines that are called in the TimerTask's thread.
Is there any way to achieve this type of behavior?
Related
I implemented a custom iOS control (looking similar to a ruler), where the user can easily scroll to select one of about 300 values.
My control also has the trait UIAccessibilityTraitAdjustable and implements the methods accessibilityIncrement() and accessibilityDecrement() of the protocol UIAccessibilityAction to update its value. So far everything works as intended.
The problem is that adjusting the value via VoiceOver can be tedious, since each swipe gesture increments/decrements the value only by one. Is there a good way to offer a second mode where the updates happen, e.g. in steps of ten?
UIAccessibilityTraitAdjustable uses a fixed, developer-defined increment size. You can adjust the slider with a larger step size in -accessibilityIncrement if you think users will find it tedious to adjust the control one unit at a time. VoiceOver users desiring finer control can use the pass-through gesture (double tap and hold) to interact with the control directly, sliding to adjust it in single-percent increments. This does, however, assume users have fine motor control.
If you feel strongly that UIAccessibility should support a "fine-grain" adjustment mode, file an enhancement request with supporting use cases.
I'm using CPPickerView in my app to accomplish a horizontal UIPickerView, and it works great, but with large data sources (dozens of items) it scrolls very slowly which makes navigation before (especially considering a normal UIPickerView can go very fast through them).
I don't mean performance-wise, by the way, I mean the view decelerates very quickly, making traversal difficult.
It's just a subclass of UIScrollView with pagingEnabled set to YES. What can I do?
I looked in the source, and it seems CPPickerView is using a scroll view. Scroll views have a decelerationRate property. Play with that and see which value makes for the best result.
Don't fill CPPickerView with all data.
For example fill with first 20 items and if it reaches to the end add another 20.
Creator of CPPickerView here - I've recently updated CPPickerView to add an allowSlowDeceleration property, which should do what you're looking for. Check out the latest code on Github, or Cocoapods version 1.2.0.
For the purposes of documentation, here's how the solution works. Like you mentioned CPPickerView just a scrollview with pagingEnabled set to YES, so the solution I found was to disable paging when the user scrolls with enough velocity.
UIScrollViewDelegate has an optional method scrollViewWillEndDragging:withVelocity:targetContentOffset:, which is called when the user's finger is lifted after swiping/scrolling on the scrollview, and it's still called even when paging is enabled. Based on that value you can tell if the user was trying to scroll quickly through items, or just move one or two items.
I played around with the CPPickerViews in the Demo project, and found that a velocity of about 2.9f seems to be about the normal "fast swipe" threshold. So if the velocity is greater than this threshold (which I defined as kCPPickerDecelerationThreshold in CPPickerView.m) and allowSlowDeceleration is set to YES, CPPickerView now sets pagingEnabled to NO before the deceleration starts. This allows the picker to "coast" and decelerate like a normal scrollview.
It then catches the end of the deceleration, OR the user touching to stop the scroll, by the call to the scrollViewDidEndDecelerating: delegate method. The current item is determined (based on the offset of the scrollview), and then if the scrollview's pagingEnabled property is set to NO a call to the private method scrollToIndex:animated: is made with animation set to YES. This scrolls the CPPickerView to the current item, which necessary as it's unlikely the coasting scroll ended right on a page boundary.
Finally, when the animated scroll completes, the scrollViewDidEndScrollingAnimation: delegate method is called, at which point pagingEnabled is set back to YES.
If you find that you're having trouble getting it to recognize a "fast" swipe, try playing with the kCPPickerDecelerationThreshold value. In hindsight that maybe should be a customizable property, so perhaps I'll roll that into the next update.
As mentioned above you can use the decelerationRate property, setting it to UIScrollViewDecelerationRateNormal might help.
scrollView.decelerationRate = UIScrollViewDecelerationRateNormal;
If that still doesn't solve your problem you could also buffer your inputs into the scroll. See this:
Advanced scrollview techniques
Hoping some of you out there will be able to point me in the right direction to handle flickering/tearing/redrawing issues when I 'animate' some components in an application.
I have to preface this by saying that the 'animations' seem to work correctly (no flickering etc) when not in full screen mode - i.e. 1024*768, borderstyle=bsSingle. It's when the application takes over the full monitor and the borderstyle becomes bsNone, that this issue becomes more than apparent.
There are 2 'animations':
1 - Panels scrolling left to right using the Winapi AnimateWindow method
2 - TmsAdvPolyPager stepping through each one of its items and subsequently showing its 'page'
When scrolling the panels, the panel that is 'exiting' leaves a 'trail' and the animation seems to shudder, it's no longer smooth.
When stepping through the TmsAdvPolyPager items, the next item will sometimes not become highlighted and the page it shows often has 'residuals' from the previous panel.
Both animations are triggered by a timer - the AnimateWindow is in it's own thread, the PolyPager stepping is not.
So, basically - any thoughts on how to smooth out these animations and force a correct redrawing of the TmsAdvPolyPager component? First time trying this kind of stuff, so not totally sure what to look into it.
As always, help is much appreciated!
TPaintBox is what you need, possibly on a TScroller.
Dump the Panels and draw to rectangles on the TPaintbox canvas.
You have obviously written 99% of this code already so moving to defined rectangles shouldn't be a problem :)
I have a custom view which has a piano keyboard drawn inside of it. Each key is drawn as a separate call, so that I can only draw keys that need redrawing. The view supports multiple touch, so multiple keys can be held down at once.
Each key is somewhat expensive to draw, so I pass a specific region to setNeedsDisplay whenever a touch is detected on the view in order to avoid re-drawing the entire view (which produces noticeable lag).
In order to handle multiple touches, I iterate over the set of received touches, check if each touch is within one of the keys, and if so, update it and call setNeedsDisplay with the rectangle of that key. In short, setNeedsDisplay is called multiple times in one function, but with a different rect each time.
The behavior that I expected was that drawRect would be invoked multiple times with different dirty regions, however, it seems that if I press the far left and far right keys at the same time, the entire view is redrawn, rather than just the far left and far right keys (as in, all the keys in between are redrawn unnecessarily).
What can I do to achieve what I want? I want to just draw the keys that are touched, not all the keys in between the two dirty keys.
The system will send you one drawRect: message per turn of the main run loop, regardless of how many times you called setNeedsDisplayInRect:. It passes you a rect that is at least the "union" of all of the dirty rects you passed to setNeedsDisplayInRect:. The system provides no way to find out exactly which rects were passed to setNeedsDisplayInRect:.
You could override setNeedsDisplayInRect: to keep an array of dirty rects (you will find +[NSValue valueWithCGRect:] useful), and clear out the array in drawRect:.
You could create your own setNeedsDisplayForKey: method that keeps an array of dirty keys and calls setNeedsDisplay.
I ran into this and posted my question here.
Curiously, I was also drawing a keyboard (with 88 keys). I never solved it and decided that I would deal with it if it turned out to be a performance problem (don't optimize until you need to etc.). One thing I did do was at start up, rendered that default keyboard into an image and use that as a base, so that I was only drawing the keys which were depressed and not the entire keyboard. Faster to draw an image than all that CGPath stuff.
I was displaying midi notes as they played and performance was fine - so maybe you don't need to be concerned # this right now.
See UPDATE below:
I am confused about UIView layout as subviews are moving. I have a "Surface" UIView with several "Item" subviews on it. The user is allowed to click on the items and drag them around the surface.
What I have noticed though is that whenever a Surface's Item subview moves (is dragged by the user), the Surface is marked as needing to be relayed out.
I do not want the Surface to layout the Items after the user moved them. That is pretty much the whole point, I am allowing the user to position them as they see fit. However, there are times, initial creation is a prime example, where I do need the Surface to place the items.
So I would like one of two things:
A) Suppress SetNeedsLayout() calls on the surface when one of its Item subviews changes (moves).
--OR --
B) Know why I was asked to relayout, and if it was caused by Item motion then do nothing.
I cannot imagine I am the first to have this question.... :)
###### UPDATE:
After more investigation, I discovered more about what is going on. It is not that moving the Surface's items causes a Surface relayout, as I originally thought. It was only the initiation of a drag which caused the relayout. In digging further I discovered that it wasn't even the drag that was the cause, but a call to the Surface's bringSubviewToFront.
I need to bring the Item to the front, so that when it is dragged it appears on top of the others.
I can understand why bringing a subview might trigger a relayout, but again it is not what I want to happen.
May be you should override layoutSubviews in your Surface UIView.