角度半径图像Swift

3

我正在尝试制作这个圆角图片...但它的形状与原图不完全一致...有没有更简单的方法而非尝试不同的宽度和高度值? 谢谢!

在此输入图片描述

 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

前往wolframalpha.com并输入“center of circle through points (3, 0), (0, 3), and (0,-3)” ,它会告诉您圆的半径和中心。当然,您可以替换为自己的点。 - vacawama
2个回答

4

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

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

@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 200 图片视图,圆角设置为 40 的结果:

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)

这个答案最初发布已经有一段时间了,所以有一些更改:

  • 直接继承UIImageView - 不需要用嵌入的UIImageView来制作一个UIView
  • 添加一个布尔变量roundTop
    • 如果设置为False(默认值),我们将圆角应用在底部
    • 如果设置为True,则我们将圆角应用在顶部
  • 重新排序和“命名”路径点以增加清晰度

所以,基本原理是:

enter image description here

我们创建一个UIBezierPath并:

  • 移动到pt1
  • 添加一条线到pt2
  • 添加一条线到pt3
  • 使用controlPoint添加一个二次曲线到pt4
  • 关闭路径
  • 使用该路径进行CAShapeLayer遮罩

结果如下:

enter image description here

如果我们想要圆角应用在顶部,在关闭路径后,我们可以应用一个scale变换,使用-1作为y值进行垂直镜像。因为该变换在“y-zero”处镜像它,所以我们还要应用一个translate变换将其移回到原位。

这给了我们:

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
    }
    
}

请问,我在Storyboard中拖动了一个ImageView,并将其类设置为“RoundedBottomImageView”,但在模拟器中它显示为红色图像,与代码中的红色背景相符。您能否解释一下这段代码? - ema so
这是一个 UIView 的子类,而不是 UIImageView - DonMag
@DonMag,您能否告诉我顶部的情况? - Mitesh Dobareeya
@MiteshDobareeya - 这对你来说真的是一个很好的学习练习,而不仅仅是要求更改。请查看我的答案中的编辑,这应该会让你更容易理解正在发生的事情。我鼓励你阅读描述并检查点说明,并尝试自己进行更改...然后检查我的更新类。 - DonMag
@DonMag 感谢您添加了详细的描述。我没有使用比例变换,而是通过在addLine中更改Y轴值来实现。 - Mitesh Dobareeya
@MiteshDobareeya - 当然是一个有效的方法!很高兴你解决了它。 - DonMag

0
您可以尝试使用 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
    }
}

在像 viewWillAppear 这样的方法中使用它,以获取 UIImageView 的实际框架。

用法:

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

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