英文:
UIBezierPath, Draw a curved view in swift
问题
我试图创建一个类似于iPhone刘海的曲线。到目前为止,我尝试绘制了曲线,但对结果不满意。
这是代码:
class CurveView: UIView {
    
    override func draw(_ rect: CGRect) {
        addShape()
    }
    
    private var shapeLayer: CALayer?
    private func addShape() {
        let shapeLayer = CAShapeLayer()
        shapeLayer.path = createPath()
        shapeLayer.strokeColor = UIColor.lightGray.cgColor
        shapeLayer.fillColor = UIColor.red.cgColor
        shapeLayer.lineWidth = 1
        
        shapeLayer.shadowOffset = CGSize(width: 0, height: 0)
        shapeLayer.shadowRadius = 5
        shapeLayer.shadowColor = UIColor.gray.cgColor
        shapeLayer.shadowOpacity = 0.3
        
        if let oldShapeLayer = self.shapeLayer {
            self.layer.replaceSublayer(oldShapeLayer, with: shapeLayer)
        } else {
            self.layer.insertSublayer(shapeLayer, at: 0)
        }
        self.shapeLayer = shapeLayer
    }
    
    func createPath() -> CGPath {
        
        let height: CGFloat = 20
        let path = UIBezierPath()
        let centerWidth = self.frame.width / 2
        
        path.move(to: CGPoint(x: 0, y: 0))
        path.addLine(to: CGPoint(x: (centerWidth - height * 4), y: 0))
        
        path.addCurve(to: CGPoint(x: centerWidth, y: height),
                      controlPoint1: CGPoint(x: (centerWidth - 44), y: 0), controlPoint2: CGPoint(x: centerWidth - 105, y: height))
        
        path.addCurve(to: CGPoint(x: (centerWidth + height * 4), y: 0),
                      controlPoint1: CGPoint(x: centerWidth + 105, y: height), controlPoint2: CGPoint(x: (centerWidth + 44), y: 0))
        
        path.addLine(to: CGPoint(x: self.frame.width, y: 0))
        path.addLine(to: CGPoint(x: self.frame.width, y: self.frame.height))
        path.addLine(to: CGPoint(x: 0, y: self.frame.height))
        path.close()
        
        return path.cgPath
    }
    
}
我希望中间部分是一条直线而不是圆形形状。此外,中间部分应根据内容的大小动态调整。
英文:
I am trying to create a curve that resembles the iPhone notch. So far, I have attempted to draw the curve, but I am not satisfied with the result.
Here is the code:
class CurveView: UIView {
    
    override func draw(_ rect: CGRect) {
        addShape()
    }
    
    private var shapeLayer: CALayer?
    private func addShape() {
        let shapeLayer = CAShapeLayer()
        shapeLayer.path = createPath()
        shapeLayer.strokeColor = UIColor.lightGray.cgColor
        shapeLayer.fillColor = UIColor.red.cgColor
        shapeLayer.lineWidth = 1
        
        shapeLayer.shadowOffset = CGSize(width:0, height:0)
        shapeLayer.shadowRadius = 5
        shapeLayer.shadowColor = UIColor.gray.cgColor
        shapeLayer.shadowOpacity = 0.3
        
        if let oldShapeLayer = self.shapeLayer {
            self.layer.replaceSublayer(oldShapeLayer, with: shapeLayer)
        } else {
            self.layer.insertSublayer(shapeLayer, at: 0)
        }
        self.shapeLayer = shapeLayer
    }
    
    func createPath() -> CGPath {
        
        let height: CGFloat = 20
        let path = UIBezierPath()
        let centerWidth = self.frame.width / 2
        
        path.move(to: CGPoint(x: 0, y: 0))
        path.addLine(to: CGPoint(x: (centerWidth - height * 4), y: 0))
        
        path.addCurve(to: CGPoint(x: centerWidth, y: height),
                      controlPoint1: CGPoint(x: (centerWidth - 44), y: 0), controlPoint2: CGPoint(x: centerWidth - 105, y: height))
        
        path.addCurve(to: CGPoint(x: (centerWidth + height * 4), y: 0),
                      controlPoint1: CGPoint(x: centerWidth + 105, y: height), controlPoint2: CGPoint(x: (centerWidth + 44), y: 0))
        
        path.addLine(to: CGPoint(x: self.frame.width, y: 0))
        path.addLine(to: CGPoint(x: self.frame.width, y: self.frame.height))
        path.addLine(to: CGPoint(x: 0, y: self.frame.height))
        path.close()
        
        return path.cgPath
    }
    
}
I would like the center to be a straight line instead of a circular shape. Additionally, the center should adjust dynamically based on the size of my content.
What I want:
答案1
得分: 1
这段代码是用Swift编写的,它创建了一个自定义的UIView子类NotchView,以及一个演示控制器NotchVC。NotchView是一个带有特殊形状的视图,该形状是通过使用CGMutablePath和一系列的弧线来创建的,具体的形状和尺寸由一些属性控制,如cornerRadius、notchRadius和notchWidthPercent。
这段代码中包含了一些视图和布局的操作,以及演示控制器的设置。你可以根据需要修改属性来改变形状和颜色。
如果你需要更多详细信息或有其他问题,请随时提出。
英文:
It's easier to create that shape by using a CGMutablePath and a series of arcs with .addArc(tangent1End: pt1, tangent2End: pt2, radius: r).
Quick example:
class NotchView: UIView {
	public var cornerRadius: CGFloat = 24.0 { didSet { setNeedsLayout() } }
	public var notchRadius: CGFloat = 12.0 { didSet { setNeedsLayout() } }
	public var notchWidthPercent: CGFloat = 1.0 / 3.0 { didSet { setNeedsLayout() } }
	public var color: UIColor = .red { didSet { shapeLayer.fillColor = color.cgColor } }
	// this allows us to use the "base" layer as a shape layer
	//	instead of adding a sublayer
	lazy var shapeLayer: CAShapeLayer = self.layer as! CAShapeLayer
	override class var layerClass: AnyClass {
		return CAShapeLayer.self
	}
	
	override init(frame: CGRect) {
		super.init(frame: frame)
		commonInit()
	}
	required init?(coder: NSCoder) {
		super.init(coder: coder)
		commonInit()
	}
	func commonInit() {
		shapeLayer.fillColor = UIColor.red.cgColor
	}
	
	override func layoutSubviews() {
		super.layoutSubviews()
		
		var pth = CGMutablePath()
		
		let w: CGFloat = bounds.width * (1.0 - notchWidthPercent) * 0.5
		
		var pt1: CGPoint = .zero
		var pt2: CGPoint = .zero
		pth = CGMutablePath()
		
		// start on left side, midY
		pt1 = .init(x: bounds.minX, y: bounds.midY)
		
		pth.move(to: pt1)
		
		// top-left corner
		pt1 = .init(x: bounds.minX, y: bounds.minY)
		pt2 = .init(x: w, y: bounds.minY)
		
		pth.addArc(tangent1End: pt1, tangent2End: pt2, radius: cornerRadius)
		
		// top-left notch
		pt1 = pt2
		pt2.x += notchRadius
		pt2.y += notchRadius
		
		pth.addArc(tangent1End: pt1, tangent2End: pt2, radius: notchRadius)
		// bottom-left notch
		pt1 = pt2
		pt2.x = bounds.maxX - (w + notchRadius)
		
		pth.addArc(tangent1End: pt1, tangent2End: pt2, radius: notchRadius)
		// bottom-right notch
		pt1 = pt2
		pt2.x += notchRadius
		pt2.y -= notchRadius
		
		pth.addArc(tangent1End: pt1, tangent2End: pt2, radius: notchRadius)
		// top-right notch
		pt1 = pt2
		pt2.x = bounds.maxX
		
		pth.addArc(tangent1End: pt1, tangent2End: pt2, radius: notchRadius)
		// top-right corner
		pt1 = pt2
		pt2.y = bounds.maxY
		
		pth.addArc(tangent1End: pt1, tangent2End: pt2, radius: cornerRadius)
		// bottom-right corner
		pt1 = pt2
		pt2.x = bounds.minX
		
		pth.addArc(tangent1End: pt1, tangent2End: pt2, radius: cornerRadius)
		// bottom-left corner
		pt1 = pt2
		pt2.y = cornerRadius
		
		pth.addArc(tangent1End: pt1, tangent2End: pt2, radius: cornerRadius)
		// close the path
		pth.closeSubpath()
		
		shapeLayer.path = pth
	}
	
}
and a demo controller:
class NotchVC: UIViewController {
	
	override func viewDidLoad() {
		super.viewDidLoad()
		
		view.backgroundColor = .lightGray
		
		let notchView = NotchView()
		notchView.translatesAutoresizingMaskIntoConstraints = false
		view.addSubview(notchView)
		let g = view.safeAreaLayoutGuide
		NSLayoutConstraint.activate([
			notchView.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
			notchView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
			notchView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
			notchView.heightAnchor.constraint(equalToConstant: 120.0),
		])
		// you can change these values from their defaults
		//	examples:
		//notchView.color = .blue
		//notchView.cornerRadius = 32
		//notchView.notchRadius = 8
		//notchView.notchWidthPercent = 0.6
	}
}
To get this output:
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。




评论