I am trying to create an inifite timeline in my iPhone app to scroll through time. I think I am going to use a UIScrollView to do that but I am not sure how to do that.
Am I supposed to create a reusable queue of views from scratch with views containing the days/months/years and reuse them when they go out of the screen ? (I have in mind the reusable cells from the tableviews).
Maybe there is a better way to do that ?
Thanks for your help !
As far as I know, UIScrollView doesn't have width limitation since 3.0. Nevertheless, you cannot use it to display large amount of data because it would take too much time to draw it and your application will work slow.
You can use UIView of width = 320.0 * 3 instead. Handle
- (void) touchesMoved: (NSSet*)_touches withEvent: (UIEvent*)_event
And apply
view.transform = CGAffineTransformMakeTranslation(-shift, 0.f);
Shift here can be calculated depending on delta X found from touches set. Affine translation allows to emulate smooth movement. When user releases their finger (touchesEnded), redraw your view (setNeedsDisplay) with shifted content and get it back:
self.transform = CGAffineTransformIdentity;
Don't do setNeedsDisplay while touchesMoved because it can affect performance if you are drawing graphs or calculating something.
For 2D map you would have to think about both X and Y and have 320*3 X 480*3 view :)
It's interesting task. Enjoy.
Well, the UIScrollView is finite, so you will hit the end at one point. You can of course move items around (back) to make sure you will never hit that limit.
You certainly want to do some caching/paging ala UITableView to make sure you only keep the minimal needed views in memory.
Ask a more specific question if you are ready for that :-)
Related
I have a UIScrollView that contains a custom UIView. In my UIView I am overriding drawRect to draw a path using CGContextStrokePath. I would like to slightly alter the way the zoom works. Pinch zooming out will show more of the paths on the screen. This is what I want but i want the line width to stay the same not shrink as you zoom out so that they are still clear to the user.
I thought I would just do this (myUIView zoom target is called _lineView)
-(void)scrollViewDidZoom:(UIScrollView *)pScrollView
{
_lineView.zoomScale = _scrollView.zoomScale;
[_lineView setNeedsDisplay];
}
and then just calculate an appropriate line stroke size in my _lineView drawRect method to give the effect of constant line width as you zoom out.
This is really slow and I have read that this is expected as drawrect is not optimised to be called many times a second.
I then started looking at using a GLKView instead and just rendering the whole thing in opengl directly. The problem with this is I will have to implement all of the zooming and panning myself (with all the lovely zoom and pan bounce effects you get for free in UiScrollView). I will also have to implement all the controls I want to use in opengl, buttons etc.
Is there a way to do this whilst still using Quartz2d? I feel like opengl will give me lots of power but it will take me much longer to get the rest of my app done if I go down that route.
I figured this out. I found a simple way to do this that does not slow down the zoom / bouncing animations at all and is very fast and fluid.
Quartz has a class called CAShapeLayer that lets you do some pretty cool stuff. Among these is being able to set a CGPath property and specify a linewidth. Changes are reflected in the view.
So i basically call shapeLayer.lineWidth from my scrollViewDidZoom method and it does exactly what i need.
What I would do if I were you is to use default zooming behavior while zooming (which does not redraw, but instead just applies a transform to the zoomed view, which can be done by the GPU very, very quickly, but as you have noticed can lead to inferior quality).
Then, when the user finished zooming, redraw the whole view as you do now. The appropriate delegate method is scrollViewDidEndZooming:withView:atScale:.
This way, you have fast (but slightly ugly) zooming, and nice (but slightly, probably unnoticeably, slower) display after the zoom is finished.
I've got a pretty big UIScrollView (contentSize 8000 x 960) with a lot of small labels.
The labels are only added if you scroll to the area where the label belongs to. So, at first, the scrooling is smooth, but as more and more labels are loaded, the performance suffers more and more.
What's the best solution to keep the scrolling smooth? Is CATiledLayer the way to go? Or should the labels off the view should be hidden or something like that?
Thanks a lot for your help!
Stefan
EDIT:
I got a huge performance boost when drawing some objects instead of using views; but now, I've got another problem; if I draw directly onto the UIScrollView, everything performs great. But if I lay a UIView on the UIScrollView and draw on that, performance goes down again (both times calling setNeedsDisplayInRect: in scrollViewDidScroll:).
So hierarchy:
UIWindow
UIView
UIScrollView <== drawing here
works fine.
But hierarchy:
UIWindow
UIView
UIScrollView (size 8000x960)
UIView (size 8000 x 960) <== drawing here
performs bad.
Any ideas why? I need the UIView above because the drawing should be ABOVE some UIViews; so I place the UIViews in the scrollView and draw in the view above...
The label reuse is one of possible solutions. Sometimes it doesn't help a lot. When a new UIView appears the drawRect method will be called and that may cause problems with animation.
Sometimes the best solution is to draw your labels (I mean text) directly using Core Graphics without creating UILabel object or something else. The CALayer will increase performance, but there is a possibility that it will not help you a lot.
But first of all you should remember that views with alpha = 0 or hidden = YES will not be drawn at all, so there is no penalty for using them. Maybe you should try to hide unused ones before using Core Graphics.
The best way would be to have just a few UILabels and reuse them. But you'll have to implement - (void)scrollViewDidScroll:(UIScrollView *)scrollView and do some reusable logic there. Depending on case it might be easier to use a UITableView and transform it 90 degrees, so it looks like a scrollview. That way you can the build-in reusable logic of the TableView.
For others seeing this post, I was able to drastically improve performance and keep UIViews in a scroller without using a table by enabling each view's "shouldRasterize" property. This property renders the view as an image while the view is in motion, causing it to not need to be reprocess for each pixel it moves. So, if I have a UIButton called button, it gets enabled like this:
button.layer.shouldRasterize = YES;
Did u try something similar to UITableViewCell
dequeueReusableCellWithIdentifier
Using a reuseIdentifer to reuse the once allocated labels..
I have a single UIView for drawing any one of a set of items. That is, my one UIView subclass can be told to draw a square, circle, or triangle for example.
I'd like to have a transition to flip that view so that the UIView draws a different shape at the half way point, so it looks like I'm transitioning to a different view, but really I'm just redrawing the single view at the half way point of the transition.
Is there a way to do this, without resorting to using two different UIViews and swapping them out in the animation block? It would be a bit clumsy for me to rejig what I have to use the swap mechanism.
Ideally all I need is some callback or notification that the animation is at the half way point, and then I can redraw the view with the new shape then.
Thanks for any help!
Even if you did know the midpoint of your flip animation, I don't believe redrawing the content of the view mid-animation would do anything. I think that the contents of the view's layer are cached at the beginning of the animation and don't change as the animation proceeds. Also, your view would end up inverted, left to right, at the end of the animation if you didn't have another view behind it.
If you wish to do this as a custom animation, you could break this into two halves using something like Mike Lee's Lemur Flip implementation that I describe in this answer. After the first half of the animation, you could redraw the view's content and complete the animation, ending up back where you started.
However, to me it seems more clumsy to not switch views in response to the transition. It certainly will take a lot more code to do.
you can try to do smth like this. For example, your animationDuration property is set to 2.0f. so, the half way point is 1 second, so you can use
[self performSelector:#selector(yourCallbackMethod) withObject:nil afterDelay:1.0f];
The only way I know of that you could do this is to re-create the animation using Core Animation and then monitor the animation in the presentationLayer using a timer. There is no KVO available in Core Animation Layers so you have to monitor it explicitly.
In other words, it's probably not worth it. I suggest you think of a different way to solve your problem.
I'm developing a game. I'm using about 150 UIImageView to hold the graphics. I'm simulating a 3D enviroment, so i would like to change the z-order (how close is an object to the camera).
I know there exists :
[superWindow exchangeSubviewAtIndex:i withSubviewAtIndex:j];
But for some reason it's not working, some of the subviews disappear an re-appear.
Now I just remove all subviews and add it again in the correct z-order, this is ok with 50 subviews (on a 2G iphone) but with 120 it takes half a second so the gameplay sucks (I dont have and 3GS so i didn't try there).
I'm using so many subviews because each one is a square, then i colored it, resize it and move it somewhere in the screen. I'm holding the subviews under a NSMutableArray...
The iPhone documentation often warns about multiple UIViews nested, as it has a large performance hit after a certain point. If you start getting into issues one option is to render your UIViews into an image and using that to lower the number of on-screen views, but if you're simulating 3D that probably isn't going to help much since your composite view would need re-rendering too often.
The iPhone has full support for OpenGL ES, which allows for a real 3D environment. Take a look at some of the samples and/or a good book on the topic and you'll find that it's much easier to simply use OpenGL.
Z-order is an artifact of where your view appears in your superview's list of subviews. The last subview added to the superview is "on top".
So, to move a view to the be "on top", you remove it from its superview, then re-add it -- making it the last one and, therefore, on top.
Here, a view moves himself to the top of the z-order.
// move to the to in the z-order
UIView* superview = [self superview];
[self retain];
[self removeFromSuperview];
[superview addSubview:self];
[self release];
Note the [self retain] to prevent your dealloc if your superview is the only reference to you.
If you don't want to take the plunge to OpenGL ES, as Timothy suggests, you might want to look at replacing the UIViews with CALayers and placing them within a CATransformLayer. CATransformLayer is new in iPhone OS 3.0, and it lets you do a true 3-D layout of the CALayers. You can then set the zPosition property on your CALayers to locate them in the Z plane of the CATransformLayer. For more on 3-D layout of CALayers, I direct you to this article.
You can even add a nice perspective effect on the overall CATransformLayer by setting the m34 element in the CATransform3D applied to that layer.
Changing from UIViews to CALayers is pretty straightforward, because their structures have much in common.
Consider using a 2D game library like Cocos2D. I'm pretty sure that supports Z-ordering, and the performance will be vastly better than using UIViews.
I know this is old, but you can change the z order with bringSubViewToFront.
See this answer: How to set iPhone UI View z index?
I'm well aware that there are only two available UIView transitions, UIViewAnimationTransitionFlipFromLeft and UIViewAnimationTransitionFlipFromRight. I'm wondering if there is anyway that I can implement / emulate a UIViewAnimationTransitionFlipFromTop or UIViewAnimationTransitionFlipFromBottom.
The only way I can think to do this is by flipping the x axis with the y axis, but I've not seen any information about how to do this. Just setting the coordinates of each axis won't fix the issue as the x-axis till remains the x-axis.
Does anyone have any ideas how this can be accomplished?
You can do this by applying a CATransform3D to your main view's CALayer, with your secondary view's CALayer behind it. In this response, I point to a Mac implementation of this animation by Mike Lee. You should be able to reuse a significant portion of that code to recreate this effect on the iPhone.
Guess you'll have to use a UIView animation block with a 3D rotation transform of 90 degrees, have the Core Animation delegate call you when that's done, swap the view with the new one (3D rotated on the other side) and chain with the last 90 degrees for the new view...
Although its not exactly what you want, there are two built-in animations that you didn't mention: UIViewAnimationTransitionCurlDown and UIViewAnimationTransitionCurlUp.
CurlUp looks like someone is peeling the view from the bottom up to the top, and CurlDown looks like the view is being unrolled down on top of the screen from top to bottom. You should at least give them a look, as they are easy to use.