How does sizeToFit determine appropriate size? - ios

I have an app where the user can add what I call palettes at will. Items (UIViews) can be dragged from one palette to another. I thought I could use the sizeToFit method to automatically resize the palettes when items were added ore removed, but no resizing seems to be happening.
The documentation states that the method resizes to the most appropriate size to fit the subviews. Does that not include UIViews that I add programmatically?
I'd really not have to write my own sizeToFit method if I can get the built-in one to work for me. But it seems to be doing nothing. Here's the code I've tried:
CGPoint oldCenter = self.pieceBeingMoved.center;
CGPoint newCenter = [self.pieceBeingMoved.superview convertPoint:oldCenter toView:destinationView];
[destinationView addSubview:self.pieceBeingMoved];
self.pieceBeingMoved.center = newCenter;
if (destinationView.tag == 20000) {
NSLog(#"DestinationView is a palette, so resize it from %#",NSStringFromCGRect(destinationView.frame));
[destinationView sizeToFit];
NSLog(#"DestinationView resized to %#",NSStringFromCGRect(destinationView.frame));
}
The size is identical before and after the subview is added. The subview being added is the only subview at the time I ran this test, and the size of the subview is about 5% the size of the palette it is being moved to. So why isn't the view being resized? Do I need to do it myself?

The sizeToFit method just calls sizeThatFits: and then resizes the view accordingly. The default implementation of the UIView class returns the current size so you don’t see any effect. You will have to write your own sizeThatFits: method to do your calculation.

Here's an idea. Don't call sizeToFit. Just set the frame to the frame you actually want.

Related

What method is called on UIView when it's resized by autolayout?

I have an image view that I'm implementing rounded corners on by overriding the following in my subclass:
-(void)setFrame:(CGRect)frame {
[super setFrame:frame];
[self.layout setCornerRadius:frame.size.width/10.0];
}
This works great for the initial display of the image, but if the size of the image changes due to device rotation or some other mechanism, this method does not get called to implement the resize.
I'm using autolayout, and I want to know what method of UIView (and thus UIImageView) is being called when my constraints resize the view so that I can recalculate my corner radius whenever this resize takes place. My (apparently false) assumption was that the autolayout system called setFrame: to move / resize views as needed.
From the docs on updateConstraints:
Custom views that set up constraints themselves should do so by overriding this method
....
Before layout is performed, your implementation of updateConstraints will be invoked, allowing you to verify that all necessary constraints for your content are in place at a time when your custom view’s properties are not changing.
Or from the docs on layoutSubviews:
You should override this method only if the autoresizing and constraint-based behaviors of the subviews do not offer the behavior you want. You can use your implementation to set the frame rectangles of your subviews directly.
but if you need to do this on rotational changes, check out willTransitionToTraitCollection:withTransitionCoordinator:
Implementors of this method can use it to adapt the interface based on the values in the newCollection parameter. A common use of this method is to make changes to the high-level presentation style when the current size class changes.

Custom UIView drawRect is called even with UIViewContentModeScaleAspectFill

I'm having the following issue.
Suppose I have my own image view class (to make it easy I've trimmed all the code and just included only the code that is causing the issue). That class inherits from UIView and overrides drawRect. Here is how my drawRect looks (the code is in Xamarin, but it's easy to understand)
public override void Draw(RectangleF rect)
{
base.Draw(rect);
CGContext context = UIGraphics.GetCurrentContext();
if (Image != null)
{
var imageRect = getAspectFillRect(rect, Image.Size);
context.DrawImage(imageRect, Image.CGImage);
}
else
{
this.BackgroundColor.SetFill();
context.FillRect(this.Bounds);
}
}
This code simply draws the image set to Image property in a way which simulates UIImageView's aspect fill option by calculating the rect which will display the image in aspect fill mode using this line of code getAspectFillRect(rect, Image.Size);
I also have UICollectionView with custom layout and custom cells. Each UICollectionViewCell UI is defined in an xib file, there I have all the necessary constraints set and I there I also have my custom view for displaying images. For that view in interface builder I've set content mode to "Aspect Fill". The custom UICollectionViewLayout is made in a way that cell expands (you'll see the example shortly). So the image inside the cell should also scale, and as I've set "Aspect Fill" option, it shouldn't call drawRect of my custom view, rather it should just scale already drawn content. But that is not the case!
LOOK HERE to see the video which demonstrates what happens.
You may see that the image inside is not growing together with the cell. The problem is that before ever the cell expanding animation begins drawRect of my custom UIView is called with the rect which will eventually be established after the animation. So I get a jerky animation. In my custom layout code I just call LayoutIfNeeded after updating the constraints of the cell. I don't call setNeedsDisplay, so I don't get why my drawRect is called.
For experiment I replaced my custom image view with UIImageView, I set it's content mode to "Aspect Fill", and LOOK HERE what happened. YEAH! It worked. So I suppose the issue is not in the custom UICollectionView layout, rather in my custom image drawing class.
What I should take into account?? How I should handle this case? Any Ideas?
Thanks!
DrawRect will be called whenever the system "feels" it should call it. Regardless if you call SetNeedsDisplay or not.
Now, setting ContentMode to ScaleAspectFill does not mean that DrawRect will not be called. In fact, from Apple docs here:
Instead of redrawing the contents of the view every time, you can use
this property to specify that you want to scale the contents (either
with or without distortion) or pin them to a particular spot on the
view.
This means that, if you don't want to go through the hassle of drawing the contents yourself, just set the ContentMode property. In your example, you are using DrawRect to draw.
Now, the fact that DrawRect is called before the animation starts, but with the target value for Bounds (or Frame?) means that the target value of these properties is set before the change finishes. During an animation, these properties do not change. Note during. A Bounds' or Frame's value will not change through all the values of an ongoing transition. It only knows "start" and "end".
What would happen in your app if DrawRect was not called before the animation start, but was called after animation end? Well, the cell would resize along with its subview, but your image would remain small throughout the animation and snap to the large size after the animation finished. So it's one way or the other.
Solutions:
Use the UIImageView.
If you do need to use a custom view, use a UIImageView on that to display your image.
Draw your image on a custom CALayer and add that layer on your custom view's Layer.

Changing size of UIButton while using Autolayout?

I'm pretty new to ios. I'm working on a project which is going to frequently use a certain UIView class throughout the application. This class is simply an image that does some transparency stuff with the background color. This works fine so far. This class sometimes exists as a lone UIView, and sometimes as a button subview.
When I add this UIView to a UIButton as a subview, the full contents of the UIView are displayed at full size, but the clickable area of the button remains the size defined in the xib unless Use Autolayout is turned off (even if I manually try to set the button's frame.)
I would like to put a UIButton on the xib as a placeholder, and then later define its size/clickable area based on the size of the overlay image which the UIView that was initialized with.
Should this be possible, or am I misinterpreting the usage of xib files/autolayout?
under ViewDidLoad I have...
- (void)viewDidLoad{
[theButton addSubview:_theView];
CGRect buttonFrame = theButton.frame;
buttonFrame.size = CGSizeMake(_theView.getSize.width,_theView.getSize.height);
[theButton setFrame:buttonFrame];
}
However, the frame stays the same size when I print the button info before and after I try calling setFrame.
(note that '_theView.getSize' was added by me)
Under Autolayout, views don't have frames at viewDidLoad. Try your code in viewWillAppear: or viewDidLayoutSubviews.
Under Autolayout, you don't set frames. You edit constraints instead. Setting a frame will work until the next layout pass, when your layout will revert to that described by your constraints.
To size a button to fit a subview, you can try something like this (in visual format language): |-[theView]-| but it would depend what constraints are in place from your xib.

Resize content of UIScrollView when zooming

I know there is many topics for this subject but I didn't found a good solution for my problem.
I have a UIScrollView with an image and other components added as subviews.
I just want to know the good way to resize the components into in real time when I zoom.
- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
// Resizing image and other components
}
EDIT : I would like the impression that the components inside the scroll view keep the same size when I zoom. I don't want to they become bigger or smaller during zooming.
EDIT : Here is the hierarchy of my UIViewController
View
ScrollView
Main view
Image view
Drawing view (UIView with components like UILabel, other UIViews, etc...)
Thanks a lot !
I would leave the scale of UIScrollView as it is and reach the goal with the help of subview (adjusting its size, not scrollView's), where I would place all the subviews and the images you work with.
So it would be something like:
yourView.transform = CATransform3DMakeScale(newScale, newScale, 1.0);
where yourView is the main (only) subview of scrollView. Then I would recalculate the size of the yourView with new scale and set it to scrollView's contentSize parameter so that you could actually scroll the enlarged content, not only see it truncated.
The only thing is that in this case you should use the callback other than scrollViewDidZoom: I guess.
P.S. Changing UIView's transform parameter you don't need to bother about subviews' scale: CoreAnimation does everything for you.

Is there a way to know how many pixels a user moves a scrollview?

Is there a specific way to know how many pixels a user moves a scrollview? I would dynamically resize a textview accordingly.
Thanks
Fran
Look at the UIScrollViewDelegate Protocol Reference. Specifically check the Responding to Scrolling and Dragging methods, from there you can check the contentOffset property of the scrollview which will give you the x and y offset from the origin.
EDIT: Added the proper name of the contentOffset property.

Resources