ios 如何使用自动布局保持圆形图像视图?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/32362934/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 07:30:27  来源:igfitidea点击:

How to keep a round imageView round using auto layout?

iosswiftxcodeautolayoutios-autolayout

提问by CHM

How do I turn a rectangular image view into a circular image view that can hold shape in auto layout without setting width and height restraints? Thereby allowing the imageView to define it's size, and size bigger and smaller relative to objects around it with leading, trailing, top, and bottom constraints.

如何将矩形图像视图转换为可以在自动布局中保持形状而无需设置宽度和高度限制的圆形图像视图?从而允许 imageView 定义它的大小,并且大小相对于它周围的对象更大或更小,具有前导、尾随、顶部和底部约束。

I asked a similar question the other day, but I think this might be posed in a more concise way. Thanks so much!

前几天我问了一个类似的问题,但我认为这可能以更简洁的方式提出。非常感谢!

EDIT

编辑

Ok, I started over to make this as simple as possible. I have a view named "Cell" and a UIImageView named "dog" within the cell, and that's it. I don't have "unable to simultaneously satisfy constraints" in the console anymore, just two simple views using auto layout. I'm still trying to use this code to round the UIImageView:

好的,我重新开始使这尽可能简单。我在单元格中有一个名为“Cell”的视图和一个名为“dog”的 UIImageView,就是这样。我在控制台中不再有“无法同时满足约束”,只有两个使用自动布局的简单视图。我仍在尝试使用此代码来舍入 UIImageView:

profileImageView.layer.cornerRadius = profileImageView.frame.size.width / 2
profileImageView.clipsToBounds = true

Here is the cell constraint setup:

这是单元格约束设置:

screenshot 1

截图 1

Here is the profile pic constraint setup:

这是配置文件图片约束设置:

screenshot 2

截图 2

Here is the result without the code, no rounding, but nice and square:

这是没有代码的结果,没有四舍五入,但很好且方正:

screenshot 3

截图 3

Here is the result with the code to round:

以下是四舍五入代码的结果:

screenshot 4

截图 4

This makes no sense to me, because without the rounding code the image is square, and with the code it's diamond shaped. If it's square shouldn't it be a circle with no issues?

这对我来说毫无意义,因为如果没有舍入代码,图像是方形的,而代码则是菱形。如果它是方形的,它不应该是一个没有问题的圆形吗?

EDIT 2

编辑 2

Here's what happens when I remove the bottom constraint and add a multiplier of .637 for equal height to superview.

这是当我删除底部约束并添加一个 0.637 的乘数以获得与超级视图相同的高度时会发生什么。

screenshot 5

截图 5

回答by Rupert

Unfortunately you cannot do this using cornerRadiusand autolayout — the CGLayeris not affected by autolayout, so any change in the size of the view will not change the radius which has been set once causing, as you have noticed, the circle to lose its shape.

不幸的是,你不能使用cornerRadius和自动布局来做到这一点——CGLayer它不受自动布局的影响,所以视图大小的任何变化都不会改变已经设置的半径,正如你已经注意到的那样,圆失去了它的形状。

You can create a custom subclass of UIImageViewand override layoutSubviewsin order to set the cornerRadiuseach time the bounds of the imageview change.

您可以创建UIImageView和覆盖的自定义子类,layoutSubviews以便在cornerRadius每次更改图像视图的边界时进行设置。

EDIT

编辑

An example might look something like this:

一个示例可能如下所示:

class Foo: UIImageView {
    override func layoutSubviews() {
        super.layoutSubviews()

        let radius: CGFloat = self.bounds.size.width / 2.0

        self.layer.cornerRadius = radius
    }
}

And obviously you would have to constrain the Foobarinstance's width to be the same as the height (to maintain a circle). You would probably also want to set the Foobarinstance's contentModeto UIViewContentMode.ScaleAspectFillso that it knows how to draw the image (this means that the image is likely to be cropped).

显然,您必须将Foobar实例的宽度限制为与高度相同(以保持圆形)。您可能还希望将Foobar实例设置为contentModeUIViewContentMode.ScaleAspectFill以便它知道如何绘制图像(这意味着图像可能会被裁剪)。

回答by Omar Albeik

Setting radius in viewWillLayoutSubviews will solve the problem

在 viewWillLayoutSubviews 中设置半径将解决问题

override func viewWillLayoutSubviews() {
  super.viewWillLayoutSubviews()
  profileImageView.layer.cornerRadius = profileImageView.frame.height / 2.0
}

回答by Mr.Javed Multani

create new interface in your .h file like

在您的 .h 文件中创建新界面,例如

@interface CornerClip : UIImageView

@end

and implementation in .m file like

并在 .m 文件中实现,如

@implementation cornerClip


-(void)layoutSubviews
{
    [super layoutSubviews];

    CGFloat radius = self.bounds.size.width / 2.0;

    self.layer.cornerRadius = radius;

}

@end

now just give class as "CornerClip" to your imageview. 100% working... Enjoy

现在只需将类作为“CornerClip”分配给您的图像视图。100% 工作......享受

回答by Hamsternik

First of all, I should mention that u can get a circle shape for your UIView/UIImageView onlyif the width and height will be equal. It's important to understand. In all other cases (when width != height), you won't get a circle shape because the initial shape of your UI instance was a rectangle.

首先,我应该提到只有当宽度和高度相等时,您才能为 UIView/UIImageView 获得圆形。理解这一点很重要。在所有其他情况下(当 width != height 时),您不会得到圆形,因为您的 UI 实例的初始形状是矩形。

OK, with this so UIKit SDK provides for developers a mechanism to manipulate the UIview's layerinstance to change somehow any of layer's parameters, including setting up a maskto replace the initial shape of UIView element with the custom one. Those instruments are IBDesignable/IBInspectable. The goal is to preview our custom views directly through Interface Builder.

好的,有了这个,UIKit SDK 为开发人员提供了一种机制来操纵 UIview 的图层实例以某种方式更改图层的任何参数,包括设置一个mask用自定义元素替换 UIView 元素的初始形状。这些工具是IBDesignable/IBInspectable。目标是直接通过 Interface Builder 预览我们的自定义视图。

So using those keywords we can write our custom class, which will deal only with the single condition whether we need to round corners for our UI element or not.

所以使用这些关键字我们可以编写我们的自定义类,它将只处理单个条件,无论我们是否需要为我们的 UI 元素圆角。

For example, let's create the class extended from the UIImageView.

例如,让我们创建从UIImageView扩展的类。

@IBDesignable
class UIRoundedImageView: UIImageView {

    @IBInspectable var isRoundedCorners: Bool = false {
        didSet { setNeedsLayout() }
    }

    override func layoutSubviews() {
        super.layoutSubviews()

        if isRoundedCorners {
            let shapeLayer = CAShapeLayer()
            shapeLayer.path = UIBezierPath(ovalIn:
                CGRect(x: bounds.origin.x, y: bounds.origin.y, width: bounds.width, height: bounds.height
            )).cgPath
            layer.mask = shapeLayer
        }
        else {
            layer.mask = nil
        }

    }

}

After setting the class name for your UIImageView element (where the dog picture is), in your storyboard, you will get a new option, appeared in the Attributes Inspectormenu (details at the screenshot).

为您的 UIImageView 元素(狗图片所在的位置)设置类名后,在您的故事板中,您将获得一个新选项,出现在Attributes Inspector菜单中(详细信息见截图)。

The final result should be like this one.

最后的结果应该是这样的。

回答by Randel S

It seems when you add one view as a subview of another that netted view will not necessarily have the same height as its superview. That's what the problem seems like. The solution is to not add your imageView as a subview, but have it on top of your backgroundView. In the image below I'm using a UILabel as my backgroundView.

似乎当您将一个视图添加为另一个视图的子视图时,网状视图不一定与其父视图具有相同的高度。这就是问题所在。解决方案是不将您的 imageView 添加为子视图,而是将其放在您的 backgroundView 之上。在下图中,我使用 UILabel 作为我的背景视图。

screenshot

截屏

Also in your case, when you're setting the cornerRadius use this: let radius: CGFloat = self.bounds.size.height / 2.0.

另外,在你的情况下,当你设置cornerRadius使用:let radius: CGFloat = self.bounds.size.height / 2.0

回答by Oleksii Nezhyborets

With my hacky solution you'll get smooth corner radius animation alongside frame size change.

使用我的 hacky 解决方案,您将在帧大小更改的同时获得平滑的角半径动画。

Let's say you have ViewSubclass : UIView. It should contain the following code:

假设您有 ViewSubclass : UIView。它应该包含以下代码:

class ViewSubclass: UIView {
    var animationDuration : TimeInterval?
    let imageView = UIImageView()
    //some imageView setup code

    override func layoutSubviews() {
        super.layoutSubviews()

        if let duration = animationDuration {
            let anim = CABasicAnimation(keyPath: "cornerRadius")
            anim.fromValue = self.imageView.cornerRadius
            let radius = self.imageView.frame.size.width / 2
            anim.toValue = radius
            anim.duration = duration
            self.imageView.layer.cornerRadius = radius
            self.imageView.layer.add(anim, forKey: "cornerRadius")
        } else {
            imageView.cornerRadius = imageView.frame.size.width / 2
        }

        animationDuration = nil
    }
}

An then you'll have to do this:

然后你必须这样做:

let duration = 0.4 //For example
instanceOfViewSubclass.animationDuration = duration
UIView.animate(withDuration: duration, animations: { 
    //Your animation
    instanceOfViewSubclass.layoutIfNeeded()
})

It's not beautiful, and might not work for complex multi-animations, but does answer the question.

它不漂亮,可能不适用于复杂的多动画,但确实回答了这个问题。

回答by Maverick

Swift 4+clean solution based on omaralbeik's answer

基于omaralbeik 的答案的Swift 4+清洁解决方案

import UIKit

extension UIImageView {

    func setRounded(borderWidth: CGFloat = 0.0, borderColor: UIColor = UIColor.clear) {
        layer.cornerRadius = frame.width / 2
        layer.masksToBounds = true
        layer.borderWidth = borderWidth
        layer.borderColor = borderColor.cgColor
    }
}

Sample usage in UIViewController

UIViewController 中的示例用法

1.Simply rounded UIImageView

1.简单圆润的UIImageView

override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()

        imageView.setRounded()
    }

2.Rounded UIImageView with border width and color

2.带边框宽度和颜色的圆形UIImageView

override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()

        imageView.setRounded(borderWidth: 1.0, borderColor: UIColor.red)
    }

回答by MoTaher

write this code

写下这段代码

override viewDidLayoutSubViews() {
profileImageView.layer.cornerRadius = profileImageView.frame.size.width / 2
profileImageView.clipsToBounds = true

}

in this case it will called after calculating the autolayout calculations in the first code you called the cornerradius code before calculating the actual size of the view cuz it's dynamically calculated using aspect ratio , the actual corner radius is calculated before equaling the width and the height of the view

在这种情况下,它将在计算视图的实际大小之前在您调用的第一个代码中计算自动布局计算之后调用,因为它是使用宽高比动态计算的,实际角半径是在等于宽度和高度之前计算的风景

回答by Amol Dumrewal

Solution: Crop the image

解决方案裁剪图像

[imageView setImage:[[imageView image] imageWithRoundedCorners:imageView.image.size.width/2]];
imageView.contentMode = UIViewContentModeScaleAspectFill;

I was looking for the same solution for profile pictures. After some hit and try and going through available functions, I came across something which works and is a nice way to ensure its safe. You can use the following function to crop out a round image from the original image and then you need not worry about the corner radius.

我正在为个人资料图片寻找相同的解决方案。经过一些尝试并尝试使用可用功能后,我发现了一些有效的方法,并且是确保其安全的好方法。您可以使用以下功能从原始图像中裁剪出圆形图像,然后您就不必担心圆角半径了。

Post this even if your view size changes the image remains round and looks good.

即使您的视图大小发生变化,也可以发布此图像,图像仍然是圆形的并且看起来不错。

回答by Vasil Kanev

I am quite new to iOS native development, but I had the same problem and found a solution.

我对 iOS 原生开发很陌生,但我遇到了同样的问题并找到了解决方案。

this was the goal

这是目标

So the green background has this constraints:

所以绿色背景有这个限制:

backgroundView.translatesAutoresizingMaskIntoConstraints = false
backgroundView.topAnchor.constraint(equalTo: superview!.safeAreaLayoutGuide.topAnchor).isActive = true
backgroundView.leftAnchor.constraint(equalTo: superview!.leftAnchor).isActive = true
backgroundView.widthAnchor.constraint(equalTo: superview!.widthAnchor).isActive = true
backgroundView.heightAnchor.constraint(equalTo: superview!.heightAnchor, multiplier: 0.2).isActive = true

The image constraints:

图像约束:

avatar.translatesAutoresizingMaskIntoConstraints = false
avatar.heightAnchor.constraint(equalTo: backgroundView.heightAnchor, multiplier: 0.8).isActive = true
avatar.widthAnchor.constraint(equalTo: backgroundView.heightAnchor, multiplier: 0.8).isActive = true
avatar.centerYAnchor.constraint(equalTo: backgroundView.centerYAnchor, constant: 0).isActive = true
avatar.leadingAnchor.constraint(equalTo: backgroundView.leadingAnchor, constant: 20).isActive = true

on viewWillLayoutSubviews()method I set the corner radius to

viewWillLayoutSubviews()方法上我将角半径设置为

avatar.layer.cornerRadius = (self.frame.height * 0.2 * 0.8) / 2

Basically, I am simply calculating the height of the image and then divide it by 2. 0.2 is the backgroungView height constraint multiplier and 0.8 the image width/height constraint multiplier.

基本上,我只是简单地计算图像的高度,然后将其除以 2。0.2 是 backgroungView 高度约束乘数,0.8 是图像宽度/高度约束乘数。