This is not really a question because I just solved the problem, but I think the nature of the problem is common for many iPhone devs and the solution wasn't really plainly obvious so I wanted to share my experience. To keep with the StackOverflow "question/answer" format, I'm going to put the details into the question and my solution into the answer.
Details:
I had a tableview full of cells with two labels and a custom toggle switch control I put together from scratch. If I hid the toggle control, the tableview scrolled like a champ. However, with the toggle control shown, scrolling was painfully choppy / slow on the iPhone 3G. My tableview cells have no transparent components (other than the mask for the layer) and I'm reusing cells.
The custom toggle control extends UIButton and it's layer has two components - a UIImageView that contains the sliding "switch" part of the toggle and an elongated ellipse that is used as the mask, with a background drawn behind the layer in -drawRect:.
How did I fix it? Answer coming shortly...
I found out that the source of the slowness / choppiness was coming from having to re-composite the Layer with the mask sublayer on scrolling. I guess it was having to re-draw the toggle switch every time the screen was re-drawn.
My solution is to hide the toggle switch layer/mask unless it's changing state. When it's sitting fully in the on or off positions, I hide the toggle switch layer and replaced it with an image of the toggle switch. When the user touches the toggle, the dummy image hides, the actual toggleswitch component shows and does the animation to the opposite state. When the animation finishes, I hide the toggle switch component and show the dummy image, making sure to change the image to the current state. This improved scrolling performance SIGNIFICANTLY...I dare say that it's almost as good as a native default tableview cell.
Related
All I see is a list (which looks like a rolodex), how do I make this like the combo box option?
I haven't tried this, so I can't say that it will 100% work, but ths is what I would attempt to do:
Extend UIPickerView and initially give it only enough height for one row
In the new class, intercept touchesBegan: so that if the control is in the 1 row mode it will first expand to the full height (using an animation of course). I would probably NOT pass touches on to the super class in this mode
If the control is already in the full height mode, let the touch pass on through to the super class.
Add some other communication with other components on the screen so that when the user touches them, the extended UIPickerView shrinks back to it's 1 line height form, with more animation.
To make all of that work you probably need all your controls in a scroll view that does proper auto layout so that as you change the frame size of the UIPickerView thingie the other controls move around appropriately.
I agree with Henrik that this is very non-Apple however. It seems the prescribed Apple implementation would involve a tableview cell with a disclosure button that takes you to another screen containing the picker. I know the Apple HUI guidelines don't cover all cases, but it seems they probably cover this one.
I'm using a UISwitch-Component at the bottom of a view that sits within a UIScrollView.
Now the problem that appeared, is that the switch is nearly impossible to swipe because the UIScrollView seems to dominate the userinput.
Switching works very well by tapping the switch, but from my point of view, most users "switch" the UISwitch instead of tapping.
Did anyone of you face the same / or similar problems and managed to come up with a solution?
thx in advance
sam
You have a design decision to make: if your content is meant to scroll horizontally, then a user swipe over a switch is ambiguous -- does it mean they want to scroll, or toggle the switch?
The easiest solution is to modify your UI so that this ambiguity disappears. For example, if the scroll's contentSize is not wider than the bounds of the scroll view, then it can't scroll horizontally, and a horizontal swipe will always activate the switch.
If you do want to allow horizontal scrolling, then it makes sense to replace the UISwitch with a UIButton that toggles on touch, similar to a play/pause button.
On the other hand, if you don't want to modify your UI, you could always just do:
myScrollView.delaysContentTouches = NO;
This will cause your switch to "get" the touches immediately, rather than have them go to the UIScrollView first. More info here. (You can also set this boolean in Interface Builder, as pointed out by Squeegy.)
I have an iPhone app that displays a modal view controller. The modal view controller shows two instances of a custom subclass of UITextView called RoundedTextView, an MKMapView, and a UIToolbar. I construct the viewController only once, and reset its data and present it each time the user summons it.
When showing this view controller with presentModalViewController, I noticed that the animation to show the view was choppy on the 3G. So, to speed it up, I set the alpha of the MKMapView and the two RoundedTextView objects to 0 on viewWillDisappear and back to 1 on viewDidAppear. This made it nice and fast. I also presume that I could remove the views from the superview to speed it up as well.
Does anyone else jump through these kind of hoops on the iPhone. Is there something else I should be doing to avoid this hack?
It's not a hack to simplify drawing during animation in order to make the animation more smooth. It is indeed a very valid technique.
You may be able to achieve similar performance improvements by setting all UI elements to Opaque, a technique also used to fix table view cell performance issues. You just have to make sure background colors match.
The main problem I had was I subclassed UIButton to make gradient buttons and I had the boundary mask enabled. This made the performance terrible. I removed that option and made my buttons square and it's blazin now.
I'm trying to figure out if I can get what I want out of UIScrollView through some trickery or whether I need to roll my own scroll view:
I have a series of items in row that I want to scroll through. One item should always be centered in the view, but other items should be visible to either side. In other words, I want normal scrolling and edge bouncing, but I want the deceleration when the user ends a touch to naturally settle at some specified stop point. (Actually now that I think of it, this behavior is similar to coverflow in this respect.)
I know UIScrollView doesn't do this out of the box, but does anyone have suggestions for how it might be made to do this, or if anyone's spotted any code that accomplishes something similar (I'm loathe to reimplement all the math for deceleration and edge bounce)
Thanks!
There is not a whole lot of trickery to this. Just use an UIScrollView with paging enabled. Make it the size of one of your items, and locate it where you want that item to appear. Next, disable the "Clip Subviews" option on the scroll view (either in IB, or programmatically), and you are all set.
If i do a custom cell, is it best practice to...
put icons and labels all on one view and drop it to the contentView (assuming you want everything to shift in edit mode)
or
put all editable stuff (labels) on one view, and non editable (icons) on another view and drop them to the contentView
or
it doesn't matter because when the label text changes it redraws everything anyway ?
And part2... if I'm doing transparent background (I know, big performance hit)... and I'm using png icons with soft edges (same one for every cell)... would it be that much of a difference if I create a blur on the dynamic text as well ? -I'm not sure if the performance hit is due to the animating of transparencies or the drawing of the cell initially.
Any insight/suggestions ?
The more transparencies you have and the more alpha blending that has to happen the worse off you are. For simple cells it's fine to just throw subviews in, but for complex ones you'll want to create a custom contentView that does most of its own drawing programatically in drawRect rather than depending on the UIView drawing code. Paint the UIImages directly and just draw the text strings yourself. It's a little extra work but it will perform a lot better.