Smooth scaling when adding/removing subviews to/from a UIView - iphone

I'm using a subclass of a UIView to (programmatically) contain and manage numerous subviews (mostly UIImageViews) that are to be positioned adjacent to each other such that none are overlapping. It's safe to assume that the subviews are all the same height (when originally drawn), but are varying widths. The container might be resized, in which case I want the subviews to be scaled proportionally. Moreover, there are times when I need to add/remove/edit any given subview (which might change its width).
I'm had some success in using:
[self setAutoresizesSubviews:YES];
[self setAutoresizingMask:(UIViewAutoresizingFlexibleLeftMargin |
UIViewAutoresizingFlexibleWidth |
UIViewAutoresizingFlexibleRightMargin |
UIViewAutoresizingFlexibleTopMargin |
UIViewAutoresizingFlexibleHeight |
UIViewAutoresizingFlexibleBottomMargin)];
to have the container automatically resize its subviews when its frame changes.
Unfortunately, I'm having a lot of trouble dealing with the case when the contents of a subview changes, causing it to widen or contract. Simply setting 'setNeedsLayout' and defining 'layoutSubviews' for the subview doesn't seem to do the trick because, at the beginning of 'layoutSubviews' the subview's frame hasn't been adjusted. If I force it, then the current contents are stretched or contracted, which looks terrible.
I'd certainly appreciate it if someone could explain how sizeToFit, layoutSubviews, sizeThatFits:, setAutoresizesSubviews:, setAutoresizingMask:, and setContentMode: should be used in a case like this. I mean, if I want to adjust the contents of a subview (and widen it), then how do I do it such that:
the subview is widened (without adversely stretching or autoresizing anything else within it)
the container is widened a proportional amount
none of the other subviews are affected
?
Thanks in advance!

I ended up going the manual route, using sizeThatFits: and layoutSubviews to explicitly control how everything was redrawn and organized. I found that setAutoresizesSubviews: and setAutoresizingMask: helped for simple layouts, but that it was hard to debug if I tried anything even slightly atypical.
My advice: the first time you're experimenting with programmatic layouts define sizeThatFits for all of your (sub) views and define layoutSubviews for each. Use debugging statements to log the placement of everything. Once you're happy, consider incorporating autoresizing to simply your code (if possible).

For animating the resizing of a view do:
[UIView beginAnimations:#"resizeAnimation" context:nil];
youView.frame = newFrame;
[UIView commitAnimations];
If you don't want an element to resize automatically, just set its autoresize to UIViewAutoresizingNone.
If I get your case correctly, you don't want the containter and its subviews to resize automatically, except the subview you mention. That view should have some autoresizing options enabled (I don't know exactly the ones you want, just try some and see their effect). That subview should also have its subviews resizing disabled.

Related

UIView transitionWithView: clipping my UITextView

I have a UITextView with scrolling disabled, and I am using the page-curl transition to change the text inside. This is the code I'm using:
myView.text = nextString;
[UIView transitionWithView:myView duration:PAGE_CURL_DURATION options:UIViewAnimationOptionTransitionCurlDown animations:nil completion:nil];
Everything works fine, except if the new text string has more lines than the old one, it will be clipped until the animation is complete. It looks as though the contentSize is changing AFTER the animation. I tried adding myView.contentSize = myView.bounds.size; but that didn't change anything. I also turned off "clip subviews" to no avail.
This is what it looks like immediately after the animation:
a moment later the text is no longer clipped.
Any ideas how to prevent clipping?
The easy way is to do what Bjarne said - use the clipsToBounds property. But watch out - this will make the text expand downwards indefinitely. So you will also need to surround your text field with a container view that DOES clip to bounds to set an upper limit.
If that doesn't do the trick, you will have to do some work to manually expand your text view's bounds and reposition it before animating if the new text is larger.
To get the size of a text, check out UIStringDrawing.h in the UIKit framework. Specifically, I'm thinking of using:
- (CGSize)sizeWithFont:(UIFont *)font forWidth:(CGFloat)width lineBreakMode:(UILineBreakMode)lineBreakMode;
together with UILineBreakModeWordWrap.
another alternative to getting the size may be this function in UILabel:
- (CGRect)textRectForBounds:(CGRect)bounds limitedToNumberOfLines:(NSInteger)numberOfLines;
and passing in the bounds of the largest possible text view.
For extra credit, you may also want to consider animating the change in your view's bounds for a smoother transition.
This is rather odd. The contentSize should not clip contents, but only determine scrolling behavior. This however is the only thing changing from pre- to post-animation. And unchecking Clip Subviews always worked for me.
A few options:
I assume your UITextView.frame is large enough to contain the largest of both texts initially? If you set it just before the animation kicks in, you might be too late. In such cases tricks like [self performSelector(continueWithStuff:) withObject:object afterDelay:0.01] do wonders, because you give the UI the time needed to effectuate your changes before the animation will determine the initial state.
But the easiest fix by far will be to add a bunch of newlines to every text. Since you're not scrolling anyway this should not be a problem.
Another angle may be to embed your UITextView in a UIView which you then animate. Reading the docs, the view argument should be "The container view that contains the views involved in the transition." Ignoring this fact may be giving your surprising results.
The easiest way to fix you problem is to set the UITextView's property "clipsToBounds" to NO. This will make your text view available to draw outside of its bounds

don't allow subviews to scale vwith view?

i am scaling UIView using
[UIView setTransform:CGAffineTransformMakeScale(2.0*scale.value, 2.0*scale.value)];
its works fine for me but all subviews are also scale with the UIView, but i don't want to scale all subviews of that UIView.
i tried following this to stop scale subviews.
[UIView setAutoresizesSubviews:NO];
UIView.autoresizingMask = UIViewAutoresizingNone;
but still it scale subviews.
please help me.
Transformation works on the whole view as it is. There is no way to take things out of it.
Using transform matrices don't alter the size of your views directly, it just determines how the whole thing thing is then rendered on the screen.
If you just scale it by a factor of two, you won't gain or lose any level of detail either. Maybe set its frame instead.

Border image on UIView

I want to have a UIView subclass that has a border image, but I don't want or care about this 'new' frame/bounds around the border image itself.
What I wanted to do was just use drawRect and draw outside of the rect but all drawing is clipped and I don't see a way to not clip drawing outside of this context rect.
So now I have added a sublayer to the views layer, set [self clipsToBounds] on the view and override setFrame to control my sublayers frame and always keep it at the proper size (spilling over the views frame by 40px).
The problem with this is that setFrame on a uiview by default has no animation but seTFrame on a calayer does.
I cant just disable the animations on the calayers setFrame because if I were to call setFrame on the uiview inside a uiview animation block the calayer would still have its animation disabled.
The obvious solution is to look up the current animationDuration on the uiview animation and set a matching animation on the sublayer, but I don't know if this value is available. And even if it is, I'm afraid that calling an animation from within another animation is wrong.
Unfortunately the best solution is to not use a calayer at all and just add a uiview as a subview and draw into that just like I am drawing into my layer, and hope that with autoresizingMask set to height and width that everything will 'just work'. Just seems like unnecessary overhead for such a simple task.
My solution would be to override the initWithFrame: to add the surrounding border pixels and contain the content in a subview. It probably is unneccesary overhead but definietly the "cocoa" route. It's probably going to be easier in the end too since a subview structure will allow you to edit the content seperatly from the border so you dont have to redraw the border when you redraw the content. And keeping them seperate simply makes sense from a OOP perspective.
The clipsToBounds route is probably the easiest route besides the subview structure but managing the border and content in one drawing cycle and in one object will probably be a lot more work so it'll be worth the overhead.
Excuse any typos, typed this from my iPhone.

UIView Autoresizing Resources

I've been working on autoresizing a custom UIView and Apple has very little to say on this. What's more, based on SO feedback, Apple's documentation is actually wrong in some areas. Finally, even though there are six resizing components in Interface Builder and six for setAutoresizingMask: they don't seem to correlate at all. For example, setting the width spring in IB has a different effect from setting the autoresizingMask to UIViewAutoresizingFlexibleWidth when rotating the device.
Are there any good resources for learning about how resizing works on the iPhone?
Edit:
I understand the basic concepts Apple uses such as contentMode, and I've read through the UIView documentation. I'm looking for something that explains a little more deeply what the IB settings do in terms of what's available in the SDK programmatically.
setting the autoresizingMask to UIViewAutoresizingFlexibleWidth, is equivalent to setting the width spring, plus both the left and right struts in Interface Builder (the struts mean that the edge is not flexible). If you wanted to replicate the behavior of only setting the width spring, but not the left and right struts, you would have to set autoresizingMask to UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin
I found this post helpful in understanding iOS autoresizing.
http://www.techotopia.com/index.php/IOS_4_iPhone_Rotation,_View_Resizing_and_Layout_Handling
From Apple:
... specify the value of this mask by combining the constants described in
UIViewAutoresizing using the C bitwise OR operator. Combining these
constants lets you specify which dimensions of the view should grow or
shrink relative to the superview. The default value of this property
is UIViewAutoresizingNone, which indicates that the view should not be
resized at all.
... For example,
suppose this property includes the UIViewAutoresizingFlexibleWidth and
UIViewAutoresizingFlexibleRightMargin constants but does not include
the UIViewAutoresizingFlexibleLeftMargin constant, thus indicating
that the width of the view’s left margin is fixed but that the view’s
width and right margin may change. Thus, the view appears anchored to
the left side of its superview while both the view width and the gap
to the right of the view increase.
If the autoresizing behaviors do not offer the precise layout that you
need for your views, you can use a custom container view and override
its layoutSubviews method to position your subviews more precisely.
So if you would like to position a view anchored to the top and right side of its superview, you might apply a mask like:
[myView setAutoresizingMask:UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleBottomMargin];
superview has such parameters as contentMode and autoresizesSubviews, wich all with autoresizingMask of its subviews makes resizing behavior
read attentively http://developer.apple.com/library/ios/#documentation/uikit/reference/UIView_Class/UIView/UIView.html
For the record, I scratched my head for an hour because my one-line UILabel just would NOT auto-adjust font size to fit width. Finally, this did the trick:
label.contentMode = UIViewContentModeScaleAspectFit;
I googled and no one seems to clarify this detail (everyone tells you the label should be 1 line, set adjustsFontSizeToFitWidth/minimumFontSize etc.).
Perhaps this property's default value changed? I'm on iOS 4.3.

Get correct bounds for navigationItem.titleView layoutSubviews

I have a subclass of UIView that I've added to as the titleView of a navigationItem using the following line of code:
self.navigationItem.titleView = tempview;
Easy enough. That works fine. My problem is that this navigationItem sometimes has the rightBarButton updated (sometimes there is no button, sometimes there is one standard sized button, sometimes there is a larger button).
I figured that I could simply use the layoutSubviews method of the tempview class that I've added as the titleView so I put this in:
-(void)layoutSubviews {
[super layoutSubviews];
self.mylabel.frame = self.bounds;
}
This does not seem to work, as it does not correctly resize the titleview when the rightBarButton item is updated.
I've noticed also that the bounds do not grow once they gotten smaller, they simply change the position.
I've tried using setNeedsLayout and layoutIfNeeded but those simply "resize" the view with the incorrect bounds.
I've also made sure that the rightBarButton item is set to nil, but the view still does not correctly expand once shrunk.
Thanks for any help!
configure your view with
[self setAutoresizingMask:UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth];
(probably in the initWithFrame)
then implement
- (void)willMoveToSuperview:(UIView *)newSuperview
{
[self setFrame:[newSuperview bounds]];
}
now you have your view matching the size of the container, and resizing automatically.
By default, layoutSubviews does nothing. You also never change the size of the titleView, so unless a navbar does that for you, it's no surprise that nothing is changing.
Since you're only changing the size of one of the subviews, I don't think autoresize will work, as I'm pretty sure it's triggered when the superview (the one with autoresizesubviews enabled) changes size. I would suggest recalculating the size of the titleview when you change the rightbutton. If it automatically fits when you add it the first time, you could remove and readd it, but I know that's a pretty ugly hack.
Instead of overriding -layoutSubviews have you tried setting the autoresizingMask property?
tempView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;