UIView 添加外边框

一般来说,我们给 UIView 添加边框时都会添加内边框。然而有时也有添加外边框的需求,如裁剪框,给裁剪的view 添加外边框才能精确裁剪。


内外边框说明.png

可以看到使用外边框时更符合人的思维习惯:截取边框内部。

思路是为 view 添加一个 layer,将 layer 路径设在 view 外,这个 layer 就是外边框。

注意:

  1. layer.masksToBounds 必须为 false,否则画的外边框会被截掉
  2. 如果 view 的宽高会改变,那么 这个 layer 必须在 view 大小改变时更新路径。
 override var frame: CGRect {
     didSet {
         refreshSubLayers() // 更新路径
     }
 }
  1. 将外边框的 zPosition 设高防止遮挡

下面是完整代码

class BorderCutterView: UIView {
    
    var cutterBorderWidth: CGFloat = 1
    var cutterBorderColor: UIColor = .white
    
    var borderLayer = CALayer()
    
    override var frame: CGRect {
        didSet {
            refreshSubLayers()
        }
    }
    
    func refreshSubLayers() {
        CATransaction.begin()
        CATransaction.setDisableActions(true)
        let borderWidth = layer.borderWidth
        borderLayer.frame = CGRect(x: -borderWidth, y: -borderWidth, width: frame.size.width + borderWidth * 2, height: frame.size.height + borderWidth * 2)
        CATransaction.commit()
        
        layer.displayIfNeeded()
    }
    
    func addOutSideBorder(color: UIColor, borderWidth: CGFloat) {
        if layer.masksToBounds == true {
            fatalError("masksToBounds 必须为 false,否则外边框会被截掉")
        }
        
        borderLayer = CALayer()
        
        borderLayer.borderColor = color.cgColor
        borderLayer.borderWidth = borderWidth
        
        layer.addSublayer(borderLayer)
    }
    
       // MARK: - Lifecycle
    private func commonLoad() {
        layer.masksToBounds = false
        layer.contentsGravity = .resizeAspect
        layer.zPosition = 100
        addOutSideBorder(color: cutterBorderColor, borderWidth: cutterBorderWidth)
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonLoad()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

沿着这个思路,增加 subLayer,就可以直接画出带四个角的裁剪框


image-20220222210126075.png
class BorderCutterView: UIView {
    
    var cutterBorderWidth: CGFloat = 1
    var cutterBorderColor: UIColor = .white
    var cornerLineWidth: CGFloat = 2.5
    var cornerLineLength: CGFloat = 20
    
    var borderLayer = CALayer()
    var cornerLayer = CAShapeLayer()
    
    
    override var frame: CGRect {
        didSet {
            refreshSubLayers()
        }
    }
    
    func refreshSubLayers() {
        CATransaction.begin()
        CATransaction.setDisableActions(true)
        let borderWidth = layer.borderWidth
        borderLayer.frame = CGRect(x: -borderWidth, y: -borderWidth, width: frame.size.width + borderWidth * 2, height: frame.size.height + borderWidth * 2)
        let cgPath = cornerPath(with: cornerLineWidth, length: cornerLineLength)
        cornerLayer.path = cgPath
        CATransaction.commit()
        
        layer.displayIfNeeded()
    }
    
    
    // MARK: - Lifecycle
    private func commonLoad() {
        layer.masksToBounds = false
        layer.contentsGravity = .resizeAspect
        layer.zPosition = 100
        addOutSideBorder(color: cutterBorderColor, borderWidth: cutterBorderWidth)
        addFourCornerBorder(color: cutterBorderColor, lineWidth: cornerLineWidth)
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonLoad()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    /// 外边框
    func addOutSideBorder(color: UIColor, borderWidth: CGFloat) {
        if layer.masksToBounds == true {
            fatalError("masksToBounds 必须为 false,否则外边框会被截掉")
        }
        
        borderLayer = CALayer()
        
        borderLayer.borderColor = color.cgColor
        borderLayer.borderWidth = borderWidth
        
        layer.addSublayer(borderLayer)
    }
    
    /// 裁剪框的四个角
    /// - Parameters:
    ///   - lineWidth: 线宽
    ///   - length: 画线的长度
    func addFourCornerBorder(color: UIColor, lineWidth: CGFloat) {
        if layer.masksToBounds == true {
            fatalError("masksToBounds 必须为 false,否则外边框会被截掉")
        }
        cornerLayer.lineWidth = lineWidth
        cornerLayer.strokeColor = color.cgColor
        cornerLayer.fillColor = nil
        
        layer.addSublayer(cornerLayer)
    }
    
    
    func cornerPath(with lineWidth: CGFloat, length: CGFloat) -> CGPath {        
        let path = UIBezierPath()
        
        let x: CGFloat = -lineWidth / 2
        let y: CGFloat = -lineWidth / 2
        let maxX = frame.size.width + lineWidth / 2
        let maxY = frame.size.height + lineWidth / 2
        
        let rightBeginX = maxX - length
        let bottomBeginY = maxY - length
        
        // 左上
        path.move(to: CGPoint(x: x, y: y))
        path.addLine(to: CGPoint(x: x + length , y: y))
        path.move(to: CGPoint(x: x, y: -lineWidth))
        path.addLine(to: CGPoint(x: x, y:  -lineWidth / 2 + length))
        
        // 左下
        path.move(to: CGPoint(x: x, y: maxY))
        path.addLine(to: CGPoint(x: x + length , y: maxY))
        path.move(to: CGPoint(x: x, y: bottomBeginY))
        path.addLine(to: CGPoint(x: x, y:  lineWidth / 2 + maxY))
        
        // 右上
        path.move(to: CGPoint(x: rightBeginX, y: y))
        path.addLine(to: CGPoint(x: rightBeginX + length , y: y))
        path.move(to: CGPoint(x: maxX, y: -lineWidth))
        path.addLine(to: CGPoint(x: maxX, y:  -lineWidth / 2 + length))
        
        // 右下
        path.move(to: CGPoint(x: rightBeginX, y: maxY))
        path.addLine(to: CGPoint(x: rightBeginX + length , y: maxY))
        path.move(to: CGPoint(x: maxX, y: bottomBeginY))
        path.addLine(to: CGPoint(x: maxX, y:  lineWidth / 2 + maxY))
        
        return path.cgPath
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。