What I'm trying to do:
The bottom (and top) cells of a "grouped style" UITableView have rounded corners - as is the default in many Apple iOS and non Apple apps. Sometimes cells are inserted via an animation, i.e:
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationTop];
Now this works fine. Except: When a cell is inserted below the bottom cell, or above the top cell (for a grouped style table) - there seems to be no smooth animation.
The Problem:
What I mean is that the corners of the top/bottom cells need to animate from rounded to unrounded (as they are no longer top/bottom) - and this happens in a very jerky and unlike Apple fashion.
Does Apple ever do this in an App - if so, how do they do this smoothly? Otherwise, how could this be done right?
NB - I know this is a pretty minor detail, but I'm a bit of a perfectionist...!
Take a look at the Weather App that comes with iOS, in the flip side view where you configure your cities you will see a table that accommodates these animations. As Mark Adams points out, in iOS5 there is an enum for the OS to auto-detect which animation should be used. If you want to target pre-iOS5 then on your delegate method that commits deletion you should determine what cell is being deleted based on its row position, if its 0 you would use UITableViewRowAnimationBottom if it is the the last row you would use UITableViewRowAnimationTop and if it is somewhere in the middle you would likely use UITableViewRowAnimationTop so that the group shifts upward with the animation, you might want to play around with this though.
Sorry I don't have time to write actual code out and test it, but maybe I can give you a lead. You're basically asking to set the corner radius on only two corners of a cell, then fluidly animate them to sharp corners as they move toward the center of the table.
One way to do that might be to define your custom UITableViewCell with a CAShapeLayer as the background. CAShapeLayer is drawn from Quartz paths: see http://developer.apple.com/library/IOS/#documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_paths/dq_paths.html#//apple_ref/doc/uid/TP30001066-CH211 . CAShapeLayer's path is animatable, but not with implicit animation. You'll need to use CAPropertyAnimation or perform your own interpolation. In my experience, for example in http://itunes.apple.com/us/app/blaster-through-asteroid-field/id414827482?mt=8 , doing your own interpolation is faster and more reliable (but other than the interpolation and collision detection everything most of the game relies on quartzcore, so I don't think you'll have a performance issue).
SUMMARY: Custom cell has CAShapeLayer backing. CAShapeLayer has path with 2 rounded corners. You animate the path property as the position of cell moves toward a certain point on screen. Done, smoothly animated rounded-to-square corners. Let me know if you try it out :-)
Other references:
http://developer.apple.com/library/IOS/#documentation/GraphicsImaging/Reference/CAShapeLayer_class/Reference/Reference.html#//apple_ref/doc/uid/TP40008314
http://developer.apple.com/library/IOS/#documentation/GraphicsImaging/Reference/CGPath/Reference/reference.html
As there's no real (code complete) answer currently, here's a list of the best workarounds.
Change your interface so that this jerky animation never happens.
This might mean using a plain style tableView as opposed to grouped
style, or using the alternate style seen in the Weather app (see
Chris Wagner's answer. Thanks Chris!). It might even mean using a
completely different interface that doesn't incorporate this
animation - this was the solution I went for, I managed to work out
a UX alteration that is possibly even better than the original.
Deal with it. Apple seem to, in some of the less used parts of
the OS or their apps.
File a bug report on bugreport.apple.com. It would be nice if Apple
fixed this... I know it's a pretty minor detail, but that's why I
like Apple products - cos they get the little details right!
Create a custom UITableViewCell subclass. I've thought a bit about
this, and while it's a bit of a programming challenge, I think it's
doable. Here's how: EDIT: See Rab's answer for some more detail. Still haven't coded it out yet but may give it a go when I get some time.
The custom cell has a property that defines whether the cell is a top, middle, or bottom cell. This can be set by a UITableViewController (or subclass etc).
The backgroundView property of the custom cells can be custom drawn using quartz for the rounded corners etc.
Whenever the property that defines the position of the cell is altered, the change to backgroundView can be animated. The hard part
here is animating the curve of the corners... is this doable? Apple
seems to do it in the corners of notification center, when it is
pulled down and pushed up - so seems like it is possible.
If someone seriously want to do this, and put the code on gitHub... you would be awesome! And you would have bettered
Apple's UI ;)
Have you tried to insert two rows at a time? The one on the top with zero height, and the next with rounded corners (you can use quartz core for that)
If you check the Tags section of the 'Send to Youtube' part of the Photos app, you'll see apples animations are just as 'jerky' as the rest of ours. (obviously you need a video to upload to youtube)
I'll assume thats what you were thinking of...
Related
I'm trying to build a custom, reusable UIScrollView that can be added to multiple views. The scroll view is going to be a weight picker. For the life of me, I can't find a decent example for how to implement this neatly or cleanly.
I would love for someone to point me to an existing library or tutorial that shows me how to do this. I've hacked apart a few examples, but so far, nothing is very good or reusable. Please help!!
For what it's worth, I have an image that individual ticks for the weight. So I can select to the tenth of the number (e.g. 160.4). The image has the first tick bold and larger than the remaining 9. I'd like to have the weight/number centered over the large tick. I'll update the points to my label/datasource after scrolling stops.
UPDATE
I need to make this. I have the custom font, background, and ticker image.
I would not do this through an UIScrollView. I think it would be more complex and you would certainly end up having issues when trying to add you custom picker into another scroll view.
What I would do is:
building the picker view by means of a series of CALayers, each one representing a "building block" of your picker view; see attached image:
each building block would represent a specific value by mixing a UILabel (the text) and an image;
use a pan gesture recognizer, or alternatively define touchesBegin/Moved/Ended method to deal with panning;
when a pan is done, you displace the view content to the left or right according to the panning;
when panning, you also add new building blocks to the left or right end of the picker to account for empty areas that would be revealed by the displacement done at point 4.
I think that having a look at another kind of custom control source code would be of great help to you. You would not possibly find your custom picker already implemented, but could get some guidance. Have a look then at cocoa controls.
Hope this helps.
If I were going to implement this, I would create a really wide image that had every weight on it I'd ever need - I would probably create this in code when the app started up. This image is then used as the contentView of your picker. You get all the scrolling features "for free", and you could even update the values shown in the other parts of the view during scrolling (or dragging.
The scrollView is just the area with the tick marks and weight numbers, and resides in a subview above the background, but below the centered vertical line that shows the actual weight.
EDIT: on second thought, forget the image. If you have the code to draw the image, you can do the drawing in a custom UIView. So you get the draw rect, you know the contentOffset, so you can draw just what you need.
My app is behaving sluggishly. If i pop up a UIActionSheet, for example, instead of rolling in smoothly, it stutters in over about 5 frames. I know ideally you should have as little amount of views on screen as possible, but that's what I've got anyway.
Any suggestions for speeding it up?
EDIT:
On my view i have:
Custom navigation bar in place of the regular one. It's a UIImageView, using an image file. It has a quartzcore shadow. It contains 3 buttons. 2 of these buttons have 2 UIImages each, for normal and highlighted, generated from code when the view is shown. The other button uses an image file for normal and for highlighted.
An image file for a background lies under that. On top of the background is a UITableView. By default, it doesn't have any cells (the user adds them). We'll ignore the cell, since it's slugging regardless of their being there or not.
The header of the tableview contains some labels, and an editable uitextview. The size of the header changes as more lines are added to the textview. It also has a background image, which is transparent to allow you to see the view's background image behind it. It's loaded from a file, and a texture image on top of that is also loaded from a file.
The footer is simply a background image loaded from a file with the same texture on top.
Andrew, I'm afraid you haven't been quite specific enough to isolate the exact problem. However there are a couple of things I have picked out. Firstly, check your table view is set to be opaque. Also try to design your app so your table cells can be opaque. I'm assuming your design will allow this. You need to really know how to optimise view rendering performance if you want your table and it's cells to appear translucent over other content and it may be you would need to develop your own custom specialised alternative to UITableView if that is something you really need to know (can be done but quite advanced stuff).
Also you mention using Quartz shadow. You should be able to use UIKit for drawing shadows around images, unless you have some specialist requirement. Are you sure you need to use Quartz for what you want to do? Apologies if you already know this, but if you are fairly new to iOS development and have been looking up how to do shadows, you may have found the Quartz API's for doing that and assumed that is the solution, when (depending on what you need) you will probably be better off staying with UIKit. As a general rule of thumb, only use Quartz if you are sure you can't do what you want to do with just the UIKit API's.
Another thing to check. If you are using Quartz, then you are probably getting getting the graphics context for the UIImage view and drawing on the views context in drawRect: depending on how your view hierarchy is configured, and if you have your navigation bar view set to be transparent over the top of the UITableView, then your custom drawRect implementation may be getting called unnecessarily with every animation frame and this would be a big drain on performance.
Given the level of information you have given I'm having to guess a bit and can't give a precise answer. However for a definitive understanding of how to optimise UIView performance I recommend checking out this video (though you will need an Apple Developer account to be able to access it):
https://developer.apple.com/videos/wwdc/2011/
Session 121 – Understanding UIKit Rendering
Hope this helps. Paul.
I have an infinite scrollview in which I add images as the user scrolls. Those images have varying heights and I've been trying to come up with the best way of finding a clear space inside the current bounds of the view that would allow me to add the image view.
Is there anything built-in that would make my search more efficient?
The problem is I want the images to be sort of glued to one another with no blank space between them. Making the search through 320x480 pixels tends to be quite a CPU hog. Does anyone know an efficient method to do it?
Thanks!
It seems that you're scrolling this thing vertically (you mentioned varying image heights).
There's nothing built in to UIScrollView that will do this for you. You'll have to track your UIImageView subviews manually. You could simply maintain the max y coordinate occupied by you images as you add them.
You might consider using UITableView instead, and implementing a very customized tableView:heightForRowAtIndexPath: in your delegate. You would probably need to do something special with the actual cells as well, but it would seem to make your job a little easier.
Also, for what it's worth, you might find a way to avoid making your solution infinite. Be careful about your memory footprint! iOS will shut your app off if things get out of hand.
UPDATE
Ok, now I understand what you're going for. I had imagined that you were presenting photographs or something rectangular like that. If I were trying to cover a scroll view with UILeafs (wah wah) I would take a statistical approach. I would 'paint' leaves randomly along horizontal/vertical strips as the user scrolls. Perhaps that's what you're doing already? Whatever you're doing I think it looks good.
Now I guess that the reason you're asking is to prevent the little random white spots that show through - is that right? If I may suggest a different solution: try to color the background of your scroll view to something earthy that looks good if it shows through here and there.
Also, it occurred to me that you could use a larger template image -- something that already has a nice distribution of leaves -- with transparency all along the outside outline of the leaves but nowhere else. Then you could tile these, but with overlap, so that the alpha just shows through to the leaves below. You could have a number of these images so that it doesn't look obvious. This would take away all of the uncertainty and make your retiling very efficient.
Also, consider learning about CoreAnimation (CALayer in particular) and CoreGraphics/Quartz 2D ). Proper use of these libraries will probably yield great improvements in rendering speed.
UPDATE 2:
If your images are all 150px wide, then split your scrollview into columns and add/remove based on those (as discussed in chat).
Good luck!
I am working on an app in which I need to have the ability to tap and drag UILabels around. The second challenge is that I need the other UILabels on the screen to move out of each others way (never overlap). So as I drag one label and run into another, it bumps the second out of the way. Perhaps an important note is that each label will be random rotations as well.
I am sorry but I don't really have any code to share because I am not sure where to start with collision detection of this sort.
Perhaps CGRectIntersectRect() ?
Learn to use Cocos2d, you can set layers for images for your overlapping problem. You can create your own labels and just set their position to accelerate towards where the user touched, or customize this however you want.
Also with the collision detection, this would be easy to implement in Cocos2d.
Please pardon my lack of Photoshop skills, but I'm curious what type of strategy Apps like Facebook and AP Mobile News are using for the 'label slider' in their applications. Here's a quick snippet outlining what I'm talking about as I'm sure the name I'm labeling the utility as is being butchered: http://dl-client.getdropbox.com/u/57676/slider.jpg
Essentially the user can touch the label and glide it along the X axis. It has a smooth bounce effect also once it hits the edges. This gives quite a bit more real estate if you need to present more on the screen than what your portrait mode allows for and is thus very valuable.
Is it a matter of just creating a UILabel that's wider than the screen with a bit of Touch API + Core Animation? Would love insight on how to start tackling this thing.
You'll most likely want to use a UIScrollView, with a UILabel as its content view. Size the label appropriately to your content, and then set the contentSize property of the scrollview to that size.
I created a similar control, and it's much easier than you think. It's just a UIScrollView with a series of UIButtons added to it. You could use labels instead of buttons - just depends on the feel you want. I think Facebook is probably using labels for theirs.
In any case, you'll probably want to use a series of components rather than one component (which is what Ben suggested) in the event that you want to, say, style the "selected" label differently from the others. It also makes hit detection a little easier.
You get the bounce effect for free by default - you may have noticed that most scroll views in iPhone apps do the same thing. It can be turned off as well.