添加 Line 方法后 Swift Uibezierpath 没有响应

Posted

技术标签:

【中文标题】添加 Line 方法后 Swift Uibezierpath 没有响应【英文标题】:Swift Bezierpath doesnt response after addLine Method 【发布时间】:2020-11-02 11:50:21 【问题描述】:

我正在尝试使用 bezierPath 绘制我的视图,而我的代码是?

    class auctionCollectionView : UIView
    let gradient = CAGradientLayer()

    
private var shapeLayer: CALayer?


private func addShape() 
    let shapeLayer = CAShapeLayer()
    shapeLayer.path = mys2().cgPath
    shapeLayer.strokeColor = UIColor.lightGray.cgColor
     
    self.shapeLayer = shapeLayer
    
    let colorTop =  UIColor(hexFromString: "#101820").cgColor
    let colorBottom = UIColor(hexFromString: "#101820").cgColor
    let gradientLayer = CAGradientLayer()
    
    gradientLayer.mask = shapeLayer
    gradientLayer.colors = [colorTop, colorBottom]
    gradientLayer.locations = [0.0, 1.0]
    gradientLayer.frame = mys2().bounds
         
    self.layer.insertSublayer(gradientLayer, at:0)

override func draw(_ rect: CGRect) 
    self.addShape()
    

func mys2() -> UIBezierPath 
  
    let path = UIBezierPath()

    path.move(to: CGPoint(x: 0, y: self.frame.height/4)) // start top left
    path.addQuadCurve(to:  CGPoint(x: self.frame.width , y:self.frame.height/4), controlPoint: CGPoint(x: self.frame.width/2 , y: 0 ))
    path.addLine(to: CGPoint(x: self.frame.width, y: self.frame.height * 3/4))
    
    path.addQuadCurve(to:  CGPoint(x: 0, y: self.frame.height * 3/4), controlPoint: CGPoint(x: self.frame.width/2, y:self.frame.height / 2))
    path.addLine(to: CGPoint(x: 0, y: self.frame.height/4))
    path.close()
    return path
 

输出看起来像

我的第一个 QuadCurve 看起来不错,但是当我尝试将其添加到 maxX (self.frame.width)maxY ( self.frame.height * 3/4)minX ( 0)maxY (self.frame.height * 3/4)CGPoint(x: self.frame.width/2, y:self.frame.height / 2) 时,它的作用就像添加线

我在这里遗漏了什么?问候!

【问题讨论】:

...我将在这里暗中进行猛烈抨击,并说您的问题被否决并标记为已关闭,因为您忘记了对所需结果的描述。从代码中可以看出,但您仍应添加类似“预期结果是底部不是直线,而是类似于顶部的曲线。还应显示渐变而不是黑色。” 我明白了。我这么快就问了这个问题。我忘了解释细节,但你的回答是正确的。谢谢@MaticOblak 【参考方案1】:

您的路径似乎没问题,但我担心您的问题是您在覆盖 draw 方法时调用的问题。

您应该先添加层,然后再对其进行管理。或者您应该使用 draw rect 绘制所有内容而不使用图层。在这种情况下,我建议第二个。我不确定您的完整实现应该如何,但是从复制路径并查看渐变代码来看,它可能与以下内容类似:

@IBDesignable class DrawingView: UIView 
    
    @IBInspectable var topColor: UIColor? = .red
    @IBInspectable var bottomColor: UIColor? = .blue

    private func mys2() -> UIBezierPath 
      
        let path = UIBezierPath()

        path.move(to: CGPoint(x: 0, y: self.frame.height/4)) // start top left
        path.addQuadCurve(to:  CGPoint(x: self.frame.width , y:self.frame.height/4), controlPoint: CGPoint(x: self.frame.width/2 , y: 0 ))
        path.addLine(to: CGPoint(x: self.frame.width, y: self.frame.height * 3/4))
        
        path.addQuadCurve(to:  CGPoint(x: 0, y: self.frame.height * 3/4), controlPoint: CGPoint(x: self.frame.width/2, y:self.frame.height / 2))
        path.addLine(to: CGPoint(x: 0, y: self.frame.height/4))
        path.close()
        return path
    
    
    override func draw(_ rect: CGRect) 
        super.draw(rect)
        
        guard let context = UIGraphicsGetCurrentContext(), let topColor = topColor, let bottomColor = bottomColor else 
            return
        
        
        // Save because of clipping
        context.saveGState()
        
        // Add clipping from my path
        mys2().addClip()
        
        let locations: [CGFloat] = [0.0, 1.0]
        let colors = [topColor.cgColor, bottomColor.cgColor]
        if let gradient = CGGradient(colorsSpace: CGColorSpaceCreateDeviceRGB(), colors: colors as CFArray, locations: locations) 
            context.drawLinearGradient(gradient, start: CGPoint.zero, end: CGPoint(x: 0.0, y: frame.size.height), options: CGGradientDrawingOptions(rawValue: 0))
        
        
        context.restoreGState()
        
        
    
    

因此,我们可以使用剪切来代替遮罩,这类似于遮罩在图层中的作用。渐变代码有点不同,但仍然足够相似。

现在请注意,只要视图需要重绘,就会调用此方法。要强制重绘,您需要致电setNeedsDisplay。因此,以下内容可能很常见:

@IBInspectable var topColor: UIColor? = .red  didSet  refresh()  
@IBInspectable var bottomColor: UIColor? = .blue  didSet  refresh()  

override var frame: CGRect  didSet  refresh()  

override func layoutSubviews() 
    super.layoutSubviews()
    refresh()


private func refresh()  setNeedsDisplay() 

所以基本上你在以下情况下强制它重绘:

任何颜色都会改变 正在设置框架(不使用自动布局时的常见情况) 正在布局子视图(使用自动布局时的常见情况)

请注意,调用setNeedsDisplay 不会立即强制重新加载并调用draw 方法。它只是将组件标记为脏,并将在下一个循环中重新绘制它。结果是draw 只会被调用一次,即使您在同一个循环(堆栈、方法...)中多次调用setNeedsDisplay。所以不存在性能问题。

在您的情况下,使用图层也可以做到这一点。每当发生变化时,您都需要一个 refresh 方法。那么这个方法应该要么创建子层,要么更正子层帧,掩码......并且应该删除draw方法。所以要么使用一种方法,要么使用另一种方法,但不能同时使用。

【讨论】:

以上是关于添加 Line 方法后 Swift Uibezierpath 没有响应的主要内容,如果未能解决你的问题,请参考以下文章

Swift 自定义打印方法

Swift中自定义打印方法

Swift - 添加到数组后 tableView 不重新加载

如何以编程方式创建标签栏控制器后添加导航界面(Swift)

Swift:向按钮添加渐变后未显示按钮图像

Swift:向按钮添加渐变后未显示按钮图像