如何使用自动布局保持圆形imageView轮?

问题描述 投票:41回答:13

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

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

编辑

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

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

这是单元格约束设置:

screenshot 1

这是配置文件pic约束设置:

screenshot 2

这是没有代码的结果,没有舍入,但是很好和正方形:

screenshot 3

以下是圆形代码的结果:

screenshot 4

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

编辑2

这是当我删除底部约束并为superview添加相同高度的.637乘数时发生的情况。

screenshot 5

ios swift xcode autolayout ios-autolayout
13个回答
71
投票

不幸的是你不能使用cornerRadius和autolayout来做这件事 - CGLayer不受autolayout的影响,所以视图大小的任何改变都不会改变一旦设置的半径,正如你所注意到的那样,圆圈会失去它的形状。

你可以创建UIImageView的自定义子类并覆盖layoutSubviews,以便每次imageview的边界发生变化时设置cornerRadius

编辑

示例可能如下所示:

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

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

        self.layer.cornerRadius = radius
    }
}

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


0
投票

我是iOS原生开发的新手,但我遇到了同样的问题并找到了解决方案。

this was the goal

所以绿色背景有这样的限制:

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

图像约束:

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

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

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

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


-1
投票

imageView添加1:1宽高比约束,使其保持圆形,尽管有任何高度或宽度变化。


-1
投票

我添加了自定义IBInspectable cornerRadiusPercent,因此您无需任何代码即可完成。

class RoundButton: UIButton {

    override var bounds: CGRect {
        didSet {
            updateCornerRadius()
        }
    }


    //private var cornerRadiusWatcher : CornerRadiusPercent?
    @IBInspectable var cornerRadiusPercent: CGFloat = 0 {

        didSet {
            updateCornerRadius()
        }
    }

    func updateCornerRadius()
    {
        layer.cornerRadius = bounds.height * cornerRadiusPercent
    }

}

-5
投票

可以通过为需要在运行时更改的约束创建IBOutlet来轻松完成。以下是代码:

  1. 为需要在运行时更改的约束创建IBOutlet@IBOutlet var widthConstraint: NSLayoutConstraint!
  2. viewDidLoad()中添加以下代码: self.widthConstraint.constant = imageWidthConstraintConstant()

下面的函数确定设备类型相应地更改宽度约束。

func imageWidthConstraintConstant() -> CGFloat {

    switch(self.screenWidth()) {

    case 375:
        return 100

    case 414:
        return 120

    case 320:
        return 77

    default:
        return 77
    }
}

32
投票

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

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

10
投票

在你的.h文件中创建新界面

@interface CornerClip : UIImageView

@end

和.m文件中的实现一样

@implementation cornerClip


-(void)layoutSubviews
{
    [super layoutSubviews];

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

    self.layer.cornerRadius = radius;

}

@end

现在只需将class作为“CornerClip”给你的imageview。 100%工作......享受


4
投票

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

screenshot

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


4
投票

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

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

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

例如,让我们创建从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
        }

    }

}

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

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


1
投票

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

假设你有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
    }
}

然后你必须这样做:

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

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


1
投票

基于omaralbeik's answer的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
    }
}

UIViewController中的示例用法

1.简单的圆形UIImageView

override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()

        imageView.setRounded()
    }

2.ROUND UIImageView的边框宽度和颜色

override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()

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

0
投票

我有同样的问题,现在我得到了这里发生的事情,希望一些想法可以帮助你:在这些之间有一些不同的东西:

  1. 故事板中的profileImageView
  2. 你在viewDidLoad的profileImageView

viewDidLoad和storyboard中绑定和框架的大小不同,只是因为视图针对不同的设备大小调整大小。你可以在print(profileImageView.bounds.size)viewDidLoad尝试它viewDidAppear你会发现viewDidLoad你设置的大小cornerRadius不是真正的“运行”大小。

给你的提示:你可以使用ImageView的子类来避免它,或者不在故事板中使用它,


0
投票

如果您已经将UIImageView子类化了。然后只需在其中添加这段神奇的代码。

撰写于:Swift 3

override func layoutSubviews() {
    super.layoutSubviews()
    if self.isCircular! {
        self.layer.cornerRadius = self.bounds.size.width * 0.50
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.