Adjusting UIButton's imageView properties - ios

I'm having trouble figuring out how much I can adjust the imageView property of a UIButton. In the docs, it says:
Although this property is read-only, its own properties are read/write. Use these properties to configure the appearance and behavior of the button’s view.
I'm hoping to perform a transition where I shrink the size of the button in an animation, and I'd like the button's image to also shrink accordingly. However, I find that I have no control over the size of that image. If I modify either the frame or the bounds of UIButton's imageView property, nothing seems to happen. The image seems to want to retain it's standard dimensions. Changing autolayout properties on the imageView or contentMode properties on the button don't seem to help either.
In fact, the only thing that seems to work at all is to use UIEdgeInsetsMake to "squish" the image from all sides, but that isn't animatable.
I'd prefer not to use the backgroundImage property either, since I'm already using that to style the button in the first place.

You could add an own UIImageView as subview to the UIButton instead of using the "embedded" imageView.
You can set the frame of "your" image view as usually relative to the surrounding UIButton. You will have full control on it's position and you will also be able to animate it.

Related

Resize UIImageView with autolayout

I've got a view with a UIButton in it. I've got some animations that, among others, scale the button down.
The problem is that the button is actually scaling well following the constraints when it only has text, but when I set an image either as its image or its backgroundImage, the button is not scaling anymore.
I've tried playing with contentModes of both the UIButton and its inner imageView with no results (I've tried, literally, all the possible answers of this, this, this and this stackoverflow questions).
Any hint? Thank you all in advance
UPDATE
I've noticed that the issue is with UIImageViews, not UIButtons (the problem with the button was due to the imageView inside it).
I think your UIButton does resize, but the ImageView inside does not. The ImageView of the UIButton is always displaying normal image size.
The only possibility I found is to put another UIImageView below the Button with constraints top/bottom/left/right to the Button. This one can be perfectly adjusted.
I use subclassed buttons for that with their own ImageView inside. There you need to be careful to put userInteractionEnabled = false on the ImageView.
Finally I've got it.
In both cases (UIButton and UIImageView), you have to adjust Content Compression Resistance Priority to let the view to scale down. In my case, setting it to 749 was enough.

UILabel redraws the view. How to avoid this behavior?

I have a viewController with three subviews.
Also I use AutoLayout and size classes.
These views are animated and change location and size.
After the animations I update a label but the whole view is redrawn so each view is in their initial position and size. This shouldn't happen.
As in Apple developer reference that says:
"The default content mode of the UILabel class is
UIViewContentModeRedraw. This mode causes the view to redraw its
contents every time its bounding rectangle changes. You can change
this mode by modifying the inherited contentMode property of the
class."
It doesn't seem clear to me how to modify the -contentMode- in order to update that label and leave the view -as is-. Can anyone give me a clue?
Thanks in advance.
It sounds like you may be using autolayout to lay out your view (e.g., via constraints in IB), but then you're manipulating your views' frames directly for your animations - is this the case? If so, you should instead be animating the constant values of your constraints, or possibly the transforms of your subviews.
If you manipulate frames directly in a view which uses autolayout, your changes will be over-written the next time the system lays out your view (e.g. after a label's text changes).
You have 3 options to overcome your issue -
Stop using AutoLayout in your Storyboard/Xib.
Not a great solution
Animate changes to the transform property of your subviews. e.g. myView.transform = CGAffineTransformMakeScale(2.0,2.0);
Useful for presentation and dismissal animations, but mixing AutoLayout and transforms has some issues pre-iOS 8.0
Add IBOutlets for the constraints you need to change in your animations. Animate changes to the constant values of those constraints.
Most robust approach but can lead to a lot of properties and code for complex animations
Try contentMode = UIViewContentModeCenter. This should prevent the redraw you're seeing. Although that may not be the contentMode you want.
Option 2 is to use CATextLayer to draw the label. It will resize very nicely with animation, but it's a lot more work to set up.

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.

What do these settings do in Interface Builder?

This is for a UIButton specifically. I understand that the alignment options pertain to the label inside the UIButton, but what does the 'content' settings do? They seem like they correspond to the setEnabled:, setSelected:, setHighlighted: methods of the UIButton class, however clicking Highlighted or Selected doesn't seem to change the button's behavior outside of interface builder. Also, what would be the use-case for having a button always show highlighted or selected?
There are several practical uses for these methods. As #MHUMobileInc. pointed out, it can be used for a game like Minesweeper. It can also allow you to use the button as a sort of switch, where the selected button has "ON" and the non-selected version has "OFF". It seems that Apple wants developers to shy away from this, and to use either UISwitches or a custom view. However, Apple does contradict itself in some of its apps.
These settings set the initial state of the UIButton- so if in your viewDidLoad you set it not selected nor highlighted, this Interface Builder setting will not be seen on screen when you run the app. If you want to ensure that these settings are kept, it might be easier to say so in code (viewDidLoad) rather than Interface Builder.
It may be easier not to use these settings too much. They can lead to confusion for you (the developer), other programmers who are working on the project (if any) and the user. Use the built-in UIKit views when it works and create your own subclasses when there is nothing that fits best for the situation.
The upper portion addresses the alignment of the content within the view. For example, if you have a large UIButton with an image that's smaller than the bounds of the button, by default the image will be centered vertically and horizontally inside the button. However, if you want the image to hug the left/right/top/bottom, you can set the alignment using those options.
All of these controls pertain to the UIControl, not UIButton, as indicated by the header in that screenshot.
Alignment refers to contentHorizontalAlignment and contentVerticalAlignment. Many controls return a fixed width or fixed height, or both, from sizeThatFits:. For example a UISwitch object has a fixed size, whereas a UISlider object has a fixed height. If you assign a frame to a control object that does not correspond to these fixed dimensions, these properties determine where in that frame the control interface should be drawn. Instances of UIButton do fill their frame, so the UIButton class interprets these properties differently to apply to the button's content, but the properties themselves are not specific to the UIButton class.
Similarly the selected, highlighted and enabled properties are defined by UIControl not UIButton, not all subclasses of UIControl utilise them.

imageView in UITableView cell violates setClipsToBounds on touchdown event

I have a UITableView whose cells have an image from a URL placed in its imageView.
The image isn't square (is landscape), however it initially loads as a square in the manner in which contentMode is scaleAspectFill and setClipsToBounds is YES. So far so good.
However, when I touch down on the cell, the image "widens", that is, it retains the scaleAspect part, but now bleeds out to the right as if setClipsToBounds is NO.
The same behavior occurs if the row/cell goes out of view and comes back in recycled, in which case I have the image obtained from an in-memory cache rather than from the URL.
I've tried explicitly setting both contentMode and setClipsToBounds for the cell.imageView, but it seems to have no effect. Any advise? Thanks.
Before touchdown:
After touchdown (or recycle from cache):
imageView cell property (same as other textLabel and detailTextLabel) changes its frame in layoutSubviews accordingly to image size. So you can reimplement layoutSubviews to have fixed frame rect. BTW, try TableKit library.
Heres a link to the correct answer if anyone is still looking for a solution: UIImageView in UITableViewCell changes size on touchDown
Its because you named it imageView, name it something like image_View and the problem is solved!

Resources