圆形UIView不是完整的圆形

3
我有一个有趣的问题,但是我不知道如何解决它。我试图使用@IBInspectable扩展UIView。然而,使用这种方法,圆角似乎是从nib文件的默认边设置的,而不是视图的实际大小。
因此,当我在IB中将“View as”设置为iPhoneSE并为iPhoneSE构建时,视图是一个圆。然而,如果我为iPhone7构建,则边角没有完全变成圆形。相反,如果我将“View as”设置为iPhone7并为iPhone7构建,则视图是一个圆,但是如果我为iPhoneSE构建,则角落会过度圆润。
以下是图片和代码:
扩展
extension UIView {

    @IBInspectable var cornerRadius:Double {
        get {
            return Double(layer.cornerRadius)
        }
        set {
            layer.cornerRadius = CGFloat(newValue)
            layer.masksToBounds = newValue > 0
        }
    }

    @IBInspectable var circleView:Bool {
        get {
            return layer.cornerRadius == min(self.frame.width, self.frame.height) / CGFloat(2.0) ? true : false
        }
        set {
            if newValue {
                layer.cornerRadius = min(self.frame.width, self.frame.height) / CGFloat(2.0)
                layer.masksToBounds = true
            }
            else{
                layer.cornerRadius = 0.0
                layer.masksToBounds = false
            }
        }
    }

}

"View as"设置为iPhone SE在IB中

专为iPhone SE打造

enter image description here

为 iPhone 7 构建

enter image description here

"

“视图”设置为iPhone7

建立适用于iPhone SE的应用程序

"

enter image description here

适用于iPhone 7

enter image description here

IB设置 在此输入图像描述 在此输入图像描述


请在界面构建器中选中环形 UIView,然后将自动布局设置和屏幕截图捕捉下来,可以吗? - Samuel Tulach
@SamuelTulach 我已经为您添加了这些。 - steventnorris
4个回答

3

只有当你的视图是正方形时,才会得到一个完整的圆。

如果你的视图是矩形,并且你设置了小于最小尺寸一半的cornerRadius值,你将得到第二个视图;如果它的值大于那个值,你将得到菱形的视图。

检查你的约束条件,你应该在视图完成布局后,在viewDidLayout中计算cornerRadius,这样你就可以得到正确的大小。

这里有一个Playground展示了这个问题(我添加了一个动画来更好地展示这个问题):

import UIKit
import PlaygroundSupport

extension UIView
{
  func addCornerRadiusAnimation(from: CGFloat, to: CGFloat, duration: CFTimeInterval)
  {
    let animation = CABasicAnimation(keyPath:"cornerRadius")
    animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
    animation.fromValue = from
    animation.toValue = to
    animation.duration = duration
    self.layer.add(animation, forKey: "cornerRadius")
    self.layer.cornerRadius = to
  }
}

let v = UIView(frame: CGRect(x: 0, y: 0, width: 350, height: 450))
PlaygroundPage.current.liveView = v

let squareView = UIView(frame: CGRect(x: 10, y: 10, width: 100, height: 100))
squareView.backgroundColor = .red
squareView.layer.masksToBounds = true
v.addSubview(squareView)
var cornerRadius = CGFloat(0.5*min(squareView.frame.width,
                                   squareView.frame.height))
squareView.addCornerRadiusAnimation(from: 0, to: cornerRadius, duration: 5)

let rectangleView = UIView(frame: CGRect(x: 10, y: 200, width: 100, height: 200))
rectangleView.backgroundColor = .blue
rectangleView.layer.masksToBounds = true
v.addSubview(rectangleView)
cornerRadius = CGFloat(0.5*min(rectangleView.frame.width,
                                   rectangleView.frame.height))
rectangleView.addCornerRadiusAnimation(from: 0, to: cornerRadius, duration: 5)

let diamondView = UIView(frame: CGRect(x: 200, y: 200, width: 100, height: 200))
diamondView.backgroundColor = .green
diamondView.layer.masksToBounds = true
v.addSubview(diamondView)
cornerRadius = CGFloat(0.5*max(diamondView.frame.width,
                               diamondView.frame.height))
diamondView.addCornerRadiusAnimation(from: 0, to: cornerRadius, duration: 5)

Playground output


视图是一个正方形。问题似乎不在于正方形,而是当代码运行以将其圆形化时,在生命周期中运行的时间点。它在约束完全布局视图之前运行,因此圆度不正确。 - steventnorris
@steventnorris 因为你应该在视图完成布局之后计算cornerRadius(这在我的答案中有提到)。一个快速的解决方案是子类化UIView并实现该方法。 - FranMowinckel
是的,这可能是我必须做的。我试图以一种适用于所有UIView的方式来实现这一点。如果我子类化,就没有办法再次子类化了。例如,假设我将UIView子类化为MyRoundView。现在,如果我想要一个UILabel,我必须决定是否子类化UILabel并复制该代码,或者子类化MyRoundView并且没有UILabel带来的方法和参数。 - steventnorris
你可以在你的扩展中添加一些 makeRound 方法,并在你的视图控制器中调用它(例如在 viewDidLayoutSubviews() 中)。或者你可以循环遍历你的视图和子视图,寻找 circleView 属性。或者你可以通过一个方法来扩展你的视图控制器,使其能够处理圆形子视图。 - FranMowinckel

0

您提到宽度和高度为185.5,但您将宽度约束保持为350。


问题不在于约束条件。这应该适用于非静态宽度/高度约束,但是根据它在生命周期中运行的位置,它并没有起作用,我开始思考了。 - steventnorris

0
如果仍然失败,在Xcode中删除所有(全部)限制,并在单元格类中添加以下内容:
override func awakeFromNib() {
    super.awakeFromNib()        
    userImageView.layoutIfNeeded()
    userImageView.layer.cornerRadius = userImageView.frame.size.width / 2
    userImageView.layer.masksToBounds = true
    userImageView.clipsToBounds = true

}


0

如果您不想在viewDidLayout上移动,只需在设置圆角之前添加.layoutIfNeeded()即可轻松完成技巧。

yourCircleView.layoutIfNeeded()
yourCircleView.layer.cornerRadius = yourCircleView.frame.size.width/2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接