圆角半径图像 Swift

Corner radius image Swift

提问人:ema so 提问时间:7/20/2018 最后编辑:Ashley Millsema so 更新时间:1/3/2022 访问量:596

问:

我正在尝试制作这个圆角半径图像......它与图像的形状不完全相同。有什么简单的答案而不是尝试宽度和高度的随机数字吗? 多谢

enter image description here

 let rectShape = CAShapeLayer()
 rectShape.bounds = self.mainImg.frame
 rectShape.position = self.mainImg.center
 rectShape.path = UIBezierPath(roundedRect: self.mainImg.bounds, byRoundingCorners: [.bottomLeft , .bottomRight ], cornerRadii: CGSize(width: 50, height: 4)).cgPath
iOS iPhone Swift UIImageView

评论

0赞 vacawama 7/21/2018
转到 wolframalpha.com 并输入,它会告诉您圆的半径和中心。当然,用你自己的观点来代替。center of circle through points (3, 0), (0, 3), and (0,-3)

答:

4赞 DonMag 7/20/2018 #1

您可以使用 QuadCurve 来获得所需的设计。

下面是一个 Swift 类,它允许您在 Storyboard / Interface Builder 中指定图像和舍入的“高度”:@IBDesignable

@IBDesignable

class RoundedBottomImageView: UIView {

    var imageView: UIImageView!
    
    @IBInspectable var image: UIImage? {
        didSet { self.imageView.image = image }
    }
    
    @IBInspectable var roundingValue: CGFloat = 0.0 {
        didSet {
            self.setNeedsLayout()
        }
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        doMyInit()
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        doMyInit()
    }
    
    func doMyInit() {
        
        imageView = UIImageView()
        imageView.backgroundColor = UIColor.red
        imageView.contentMode = UIViewContentMode.scaleAspectFill
        addSubview(imageView)
        
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        imageView.frame = self.bounds

        let rect = self.bounds
        let y:CGFloat = rect.size.height - roundingValue
        let curveTo:CGFloat = rect.size.height + roundingValue
        
        let myBezier = UIBezierPath()
        myBezier.move(to: CGPoint(x: 0, y: y))
        myBezier.addQuadCurve(to: CGPoint(x: rect.width, y: y), controlPoint: CGPoint(x: rect.width / 2, y: curveTo))
        myBezier.addLine(to: CGPoint(x: rect.width, y: 0))
        myBezier.addLine(to: CGPoint(x: 0, y: 0))
        myBezier.close()
        
        let maskForPath = CAShapeLayer()
        maskForPath.path = myBezier.cgPath
        layer.mask = maskForPath
    
    }

}

图像视图的结果,四舍五入设置为:300 x 20040

enter image description here


编辑 - (3.5 年后)...

为了回答@MiteshDobareeya评论,我们可以通过转换贝塞尔路径将圆角边缘从“底部”切换到“顶部”:

let c = CGAffineTransform(scaleX: 1, y: -1).concatenating(CGAffineTransform(translationX: 0, y: bounds.size.height))
        myBezier.apply(c)

自从这个答案最初发布以来已经有一段时间了,所以有一些变化:

  • 子类 - 无需将其设置为带有嵌入式的UIImageViewUIViewUIImageView
  • 添加 Bool 变量roundTop
    • 如果设置为 False(默认值),则将 Bottom 四舍五入
    • 如果设置为 True,则将顶部四舍五入
  • 重新排序和“命名”我们的路径点,以便清晰

所以,基本原理:

enter image description here

我们创建一个和:UIBezierPath

  • 移至pt1
  • 添加一行pt2
  • 添加一行pt3
  • 将四曲线添加到pt4controlPoint
  • 关闭路径
  • 将该路径用于蒙版CAShapeLayer

结果:

enter image description here

如果我们想对顶部进行圆角,在关闭路径后,我们可以应用应用变换,将其用作垂直镜像的值。由于该变换将其镜像为“y-zero”,因此我们还应用了变换将其移回原位。scale-1ytranslate

这给了我们:

enter image description here

下面是更新后的类:

@IBDesignable
class RoundedTopBottomImageView: UIImageView {
    
    @IBInspectable var roundingValue: CGFloat = 0.0 {
        didSet {
            self.setNeedsLayout()
        }
    }
    
    @IBInspectable var roundTop: Bool = false {
        didSet {
            self.setNeedsLayout()
        }
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        
        let r = bounds
        
        let myBezier = UIBezierPath()
        
        let pt1: CGPoint = CGPoint(x: r.minX, y: r.minY)
        let pt2: CGPoint = CGPoint(x: r.maxX, y: r.minY)
        let pt3: CGPoint = CGPoint(x: r.maxX, y: r.maxY - roundingValue)
        let pt4: CGPoint = CGPoint(x: r.minX, y: r.maxY - roundingValue)
        
        let controlPoint: CGPoint = CGPoint(x: r.midX, y: r.maxY + roundingValue)
        
        myBezier.move(to: pt1)
        myBezier.addLine(to: pt2)
        myBezier.addLine(to: pt3)
        myBezier.addQuadCurve(to: pt4, controlPoint: controlPoint)
        myBezier.close()
        
        if roundTop {
            // if we want to round the Top instead of the bottom,
            //  flip the path vertically
            let c = CGAffineTransform(scaleX: 1, y: -1) //.concatenating(CGAffineTransform(translationX: 0, y: bounds.size.height))
            myBezier.apply(c)
        }
        
        let maskForPath = CAShapeLayer()
        maskForPath.path = myBezier.cgPath
        layer.mask = maskForPath
    }
    
}

评论

0赞 ema so 7/21/2018
请我把一个图像视图拖到故事板上,并制作了它的类'RoundedBottomImageView',但它根据代码中写的红色背景在模拟器中给了我一个红色图像......你能解释一下代码吗?
0赞 DonMag 7/21/2018
它是一个子类,而不是.UIViewUIImageView
0赞 Mitesh Dobareeya 1/3/2022
@DonMag 你能告诉我顶部的相同情况吗?
0赞 DonMag 1/3/2022
@MiteshDobareeya - 这对你来说真的是一个很好的学习练习,而不仅仅是要求改变。请参阅编辑我的答案,这应该使您更容易理解正在发生的事情。我鼓励您阅读说明并检查点插图,并尝试自己进行更改......然后检查我更新的类。
0赞 Mitesh Dobareeya 1/3/2022
@DonMag 感谢您添加详细说明。我没有使用比例转换,而是通过更改 addLine 中的 Y 轴值来实现。
0赞 Jack 7/22/2018 #2

您可以尝试使用扩展。如UIView

extension UIView {
    func setBottomCurve(){
        let offset = CGFloat(self.frame.size.height + self.frame.size.height/1.8)
        let bounds = self.bounds
        let rectBounds = CGRect(x: bounds.origin.x,
                                y: bounds.origin.y ,
                                width:  bounds.size.width,
                                height: bounds.size.height / 2)
        let rectPath = UIBezierPath(rect: rectBounds)


        let ovalBounds = CGRect(x: bounds.origin.x - offset / 2,
                                y: bounds.origin.y ,
                                width: bounds.size.width + offset,
                                height: bounds.size.height)

        let ovalPath = UIBezierPath(ovalIn: ovalBounds)
        rectPath.append(ovalPath)

        let maskLayer = CAShapeLayer()
        maskLayer.frame = bounds
        maskLayer.path = rectPath.cgPath
        self.layer.mask = maskLayer
    }
}

& 在类似的方法中使用它,您可以在其中获取 UIImageView 的实际帧。viewWillAppear

用法:

 override func viewWillAppear(_ animated: Bool) {
    //use it in viewWillAppear like methods where you can get actual frame of UIImageView
      myImageView.setBottomCurve()
 }