Scrolling a patterned background on UIView - iphone

I have a simple UIView which I'm filling with a patterned background, like this
self.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"backgroundPattern"]];
I want to be able to manipulate the origin of the view by modifying its bounds property and have the background move with the change in origin (as if the background is fixed to the content, not floating behind it). Is this possible? Currently the background seems fixed to the view's frame instead of its bounds and the pattern doesn't scroll when the content scrolls.
To change the origin I'm using a block animation like this
self.bounds = CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height);
[UIView animateWithDuration:1. delay:0. options:UIViewAnimationOptionRepeat animations:^{
self.bounds = CGRectMake(0, 100, self.bounds.size.width, self.bounds.size.height);
} completion:nil];
With that code, my child views repeatedly scroll and reset, but the background stays fixed in place. Is it possible to specify a background which scrolls with the view instead?

I haven't been able to find anything on making a background pattern "stick" to the view's origin system. My solution for my app has been to make a child view and set the background pattern on that. Then the child view will move with the origin change and the background pattern will follow it. The problem with that solution is that the child view has to be large enough to cover the range of potential origins, which can be difficult to handle if the content area might be arbitrarily large.

You could generate an instance of your view again in the direction the user pans and add it to an NSMutableArray to be accessed and moved based on the offset of the world.

Related

Animate UIView grow/shrink

I have a UILabel I want to animate growing and shrinking. While the size changes I need the bottom left corner to remain static so that it always appears directly above a bottom toolbar. I am using the following code to make the label grow:
[UIView animateWithDuration:kAnimationDuration delay:0.0 options:UIViewAnimationCurveEaseInOut
animations:^{
CGFloat lblHeight = 42.0f;
[label setFrame:CGRectMake(0.0,
CGRectGetMaxY(self.view.bounds) - kBottomBarHeight - lblHeight,
CGRectGetMaxX(self.view.bounds),
lblHeight)];
} completion:^(BOOL finished) { }];
and to make it shrink I use the same logic except that lblHeight is set to 17.0f
The view correctly grows but when I try to shrink it the frame change animation is not animated. It blips into the new size and then animates into the new origin/location. I need the frame change to be animated. Can anyone see what I'm doing wrong?
After some tinkering I've managed to get the desired behavior by doing the following.
In the expand method, I use the UIView animation to alter the frame.
In the shrink method, I use the UIView animation to alter the bounds and center.
I'm a little baffled as to why this works but trying to shrink with the frame does not. If anyone can share some insight into this that would be great.
You should not really use frames to animate, rather you should use the transform property of your label.
However, since you want one corner to remain static, I think its best you use Core Animation. CALayer has a property called anchorPoint that determines which point a layer will rotate with respect to, and I'm pretty sure it is also valid for grow/shrink effects.

clipping a UIView while zooming in

I have a custom UIView that draws some data and another more detailed view that draws some components of the first view. What I want to do is after a gesture, zoom in, hide the first view, and then show the second view to give the user the context that they are zooming in deeper.
I thought I would start by doing something like this:
CGAffineTransform zoomTransform = CGAffineTransformMakeScale(2, 2);
[UIView animateWithDuration:2 animations:^{
self.chromoMap.transform = zoomTransform;
}];
It does zoom in on that area by scaling that UIView. However, I don't want my UIView to actually grow. If my CGRect is {0, 0}, {100, 100}. I want the frame of the UIView to stay the same, but just the contents inside it to scale by 2 to give that zoom effect. How would I do that? Thanks.
It seems I can just add another transparent UIView on top of it with clip subviews enabled to get that effect.

UIView Resize Animation Glitches

I've been struggling with a seemingly minor glitch in the way my UIView subclass animates. I have the view set up like so:
self.contentMode = UIViewContentModeRedraw;
self.contentStretch = CGRectMake(0.5f, 0.5f, 0.0f, 0.0f);
The view is basically a rectangle that's got a 3-pixel stroke. Because I want the view to not distort during resize, I have the contentStretch property indicating the middle of the view. Problem is, distortion happens while it's growing, but it's smooth when shrinking.
Seems like my animation block is pretty straightforward; I have separate ones for growing and shrinking. Here's the growing block:
[UIView animateWithDuration:0.5 animations:^{
CGRect newRect = CGRectMake(20, 100, 200, 70);
[titleView setFrame:newRect];
}];
This is pretty hard to describe in a post, so I assembled a test case project that isolates only the parts that are relevant to this. If anyone wants to take a look?
http://dl.dropbox.com/u/2798577/ViewResizeTest2.0.zip
You can see when you hit the "Grow" button, the view's left side "glitches" before animating. While clearly visible here, it's a tad worse in my real project.
I'm not sure if this is a bug in my code or a problem with the framework? Hoping it's the former...
Thanks,
Aaron.
Update
Having received Jacob's generous feedback, I've gone back to my app and removed the contentMode setting as UIViewContentModeRedraw, noting as he does that it will run drawRect every time it animates, and perhaps that initial frame is part of the glitch. So now the animation is smooth, but it animates to the wrong position, squishing the sides. I've updated the linked project to something that more-accurately reflects what's happening in my app.
If I had to guess, it would appear that the contentStretch property isn't doing its job. It's resizing the whole thing and not a CGRect region inside as per my instruction. Can anyone confirm if I'm using that correctly?
I've got this figured out now; here's the answer for anyone else who runs into this issue.
The documentation for UIView's contentStretch property doesn't tell the full story. In my case, I understood the purpose of contentStretch to describe a "sample" of an area that should be altered in size. Instead, contentStretch needs to describe the entire region that would be subject to resizing. That makes better sense now as I write this, but it wasn't obvious to me before.
Therefore, my original declaration:
self.contentStretch = CGRectMake(0.5f, 0.5f, 0.0f, 0.0f);
described a rect that had no width or height! So it effectively did nothing. Instead, I am using a rect that includes most of the view's bounds, except for the ends, which I didn't want distorted by the size change:
self.contentStretch = CGRectMake(0.2, 0, 0.7, 1);
Note that it's a rect that starts 20% into the x axis, and spans 70% of the width, at full height. In the sizes that my rect needed to be, this covered it beautifully.
Thanks to Jacob for his advice to remove the contentMode property; that was totally unnecessary in this circumstance.
Cheers,
Aaron.
Get rid of the following line in TitleView.m:
self.contentMode = UIViewContentModeRedraw;
The reason why you don't want to use UIViewContentModeRedraw is because it redisplays the view’s content when its bounds change. Hence the jagged first frame in the animation.
The default contentMode is UIViewContentModeScaleToFill, which is what you want.

Problem animating size change in a custom-drawn view

I have a custom UIView that implements drawRect to render itself with a gradient background and a 1-pixel black border around the edge, and some text.
In my parent view controller, I am attempting to animate my custom view from its original small size to full-screen, using this code:
[UIView beginAnimations:nil context:NULL];
CGRect screenRect = [[UIScreen mainScreen] applicationFrame];
CGRect detailRect = CGRectOffset(screenRect, 0, -screenRect.origin.y);
[dayDetail.view setFrame:detailRect];
[UIView commitAnimations];
This works, but drawRect in my custom view is only called once when the view is initially created. As a result the resizing just takes the original small image and blows it up to full screen, so the border of the full-size view is now many pixels wide instead of one, and the text gets expanded in size as well.
Is there any way to set this up so that drawRect is called continuously as the view is being animated from small to full-screen? Basically, I want my custom rendering code to handle the appearance of the view no matter what size it is at any point in time.
I've had the exact same problem and this is how I solved it:
dayDetail.view.contentStretch = CGRectMake(0.5, 0.5, 0.0, 0.0);
That will stretch the middle pixel only. Read the documentation to learn how to modify that behavior. You could certainly ask me, but reading the documentation is probably quicker. Besides, I would probably just quote the documentation.

backgroundColor colorWithPatternImage ends up stretched if the view size changes, instead of repeating?

I'm setting a view background to a repeating image using colorWithPatternImage. If the view size changes after I've set the background the first time, the image gets stretched instead of repeating for the new size - any ideas how I can fix this?
This is what I'm doing:
set the view's background in viewDidLoad:
frameView.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"frame-repeat.png"]];
grab some information from the server, update the view, change the view's frame size to fit the new content (usually, this means making the view taller)
background image is now stretched, even if I set the backgroundColor again using the exact same code.
If I don't do the initial background set, and only set it after I retrieve the data from the server and change the view size, it looks great - repeats, and no stretching. However, that means no background while the data is loading... looks ugly.
Has anyone else run into this?
Thanks for any help!
The answer is quite simple, just call
[[self view] setNeedsDisplay];
in the controller when you change the frame size (or whereever you do it, bottom line is to send a setNeedsDisplay message to the view after the frame change).
If this doesn't work - try setting the background color.
And if this doesn't work, implement your own custom view, and in it override the method
- (void) drawRect:(CGRect) rect {
CGRect myBounds = self.bounds;
CGContextRef context = UIGraphicsGetCurrentContext();
[[UIColor colorWithPatternImage:[UIImage imageNamed:#"frame-repeat.png.png"]]set];
CGContextFillRect(context, myBounds);
}
And then, after the resizing, send the setNeedsDisplay to your view. This will work for 100%.
Hope this was helpful, Pawel