6

Hãy tưởng tượng một số CAGradientLayer.Tạo hiệu ứng một thuộc tính lớp đơn giản là thay đổi các thuộc tính khác?

Rất dễ dàng để tạo ảnh động .startPoint.endPoint.

Bây giờ hãy tưởng tượng một phao spinLike đơn giản là đặt cả hai số cùng một lúc.

{Vì vậy, thay vì có hai hình ảnh động khác nhau, bạn có thể chỉ đơn giản là động spinLike.}

Vì vậy, một cái gì đó giống như ..

class CustomGradientLayer: CAGradientLayer { 

    @objc var spinLike: CGFloat = 0 { 

     didSet { 

      startPoint = CGPoint(...) 
      endPoint = CGPoint(...) 
      setNeedsDisplay() 
     } 
    } 
} 

Để động spinLike ...

class Test: UIView { 

    ... 
    g = CustomGradientLayer() 
    a = CABasicAnimation(keyPath: "spinLike") 
    ... 
    g.add(a, forKey: nil) 
    ... 

Nhưng.

Nó không hoạt động, startPointendPoint hoàn toàn không được di chuyển.

Điều gì là sai?


Note - bi thảm có vẻ như bạn không thể @NSManaged một tài sản trong đó có một didSet ...

enter image description here


Lưu ý - nó đủ dễ dàng để làm của riêng hoạt hình tùy chỉnh của bạn chỉ bằng ghi đè vòng lặp vẽ.

Có nhiều ví dụ về điều này xung quanh. Đây là cách bạn làm điều đó:

class CircleProgressLayer: CALayer { 

    @NSManaged var progress: CGFloat 

    override class func needsDisplayForKey(key: String) -> Bool { 

     if key == "progress" { 
      return true 
     } 
     return super.needsDisplayForKey(key) 
    } 

    override func draw(in ctx: CGContext) { 

     path.fill() etc etc... your usual drawing code 
    } 
} 

Đáng tiếc là câu hỏi của tôi ở đây là

Không liên quan đến bản vẽ thực tế:

Bằng cách hoạt ảnh tài sản spinLike,

tôi chỉ đơn giản muốn thay đổi mỗi frame hiện các thuộc tính có thể hoạt hình thông thường (trong ví dụ, .startPoint.endPoint)

Làm thế nào để bạn làm điều này?

Lưu ý! Bạn không thể thay đổi .startPoint.endPoint trong drawInContext - bạn muốn được attempting to modify read-only layer

+0

Bạn nên ghi đè 'vẽ (trong ngữ cảnh: CGContext) 'và không' drawInContext (ctx: CGContext)'. – clemens

+0

(Hmm, đó không phải là @clemens - Tôi đã làm trong thực tế kiểm tra 'vẽ # in' - Tôi chỉ cần gõ cũ ra khỏi thói quen ... :)) – Fattie

+0

Bạn đã thực hiện bất kỳ tiến bộ về vấn đề của bạn? Nếu 'startPoint' và' endPoint' luôn liên quan đến 'spinLike', bạn có thể thả các thuộc tính này và tính các giá trị của chúng trong' draw (in:) '. – clemens

Trả lời

1

Để động thuộc tính tùy chỉnh, bạn nên đánh dấu chúng với @NSManaged. Bạn không nên bắt buộc vẽ lại khi gán giá trị mới. Thay vào đó, bạn nên ghi đè lên needsDisplay(forKey:).

class CustomedGradLayer: CAGradientLayer { 
    @NSManaged var spinLike: CGFloat 

    class func needsDisplay(forKey key: String) -> Bool { 
     return key == "spinLike" || super.needsDisplay(forKey: key) 
    } 

    class func defaultValue(forKey key: String) -> Any? { 
     return key == "spinLike" ? CGFloat(0) : super.defaultValue(forKey: key) 
    } 
} 

Cuối cùng, bạn nên triển khai bản vẽ của lớp theo Apple documentation.

Tôi đã viết một dự án nhỏ trong Swift vài tháng trước. Nó thể hiện các hoạt hình lớp tùy chỉnh với độ sâu của đường cong Koch.

Đây là mã của lớp lớp:

class KochLayer: CALayer { 
    fileprivate let kPI = CGFloat(Double.pi) 
    @NSManaged var depth : CGFloat 
    var midPoint: CGPoint { 
     get { 
      let theBounds = self.bounds 

      return CGPoint(x: theBounds.midX, y: theBounds.midY) 
     } 
    } 
    var color: CGColor! 

    override class func defaultValue(forKey inKey: String) -> Any? { 
     return inKey == kDepthKey ? 0.0 : super.defaultValue(forKey: inKey) 
    } 

    override class func needsDisplay(forKey inKey: String) -> Bool { 
     if inKey == kDepthKey { 
      return true 
     } 
     else { 
      return super.needsDisplay(forKey: inKey) 
     } 
    } 

    override init() { 
     super.init() 
    } 

    override init(layer inLayer: Any) { 
     super.init(layer: inLayer) 
     if let theLayer = inLayer as? KochLayer { 
      depth = theLayer.depth 
      color = theLayer.color 
     } 
    } 

    required init(coder inCoder: NSCoder) { 
     fatalError("init(coder:) has not been implemented") 
    } 

    func pointWithRadius(_ inRadius: CGFloat, angle inAngle: CGFloat) -> CGPoint { 
     let theCenter = midPoint 

     return CGPoint(x: theCenter.x + inRadius * sin(inAngle), 
      y: theCenter.y - inRadius * cos(inAngle)); 
    } 

    override func draw(in inContext: CGContext) { 
     let theBounds = self.bounds 
     let theRadius = fmin(theBounds.width, theBounds.height)/2.0 
     let thePoints: [CGPoint] = [ 
      pointWithRadius(theRadius, angle:0.0), 
      pointWithRadius(theRadius, angle:2 * kPI/3.0), 
      pointWithRadius(theRadius, angle:4 * kPI/3.0) 
     ] 
     let thePath = CGMutablePath() 

     inContext.setLineWidth(0.5) 
     inContext.setLineCap(.round) 
     inContext.setLineJoin(.round) 
     inContext.setFillColor(color) 
     thePath.move(to: thePoints[0]) 
     for i in 0..<3 { 
      addPointsToPath(thePath, fromPoint:thePoints[i], toPoint:thePoints[(i + 1) % 3], withDepth:self.depth) 
     } 
     inContext.addPath(thePath) 
     inContext.fillPath() 
    } 

    func addPointsToPath(_ inoutPath: CGMutablePath, fromPoint inFromPoint: CGPoint, toPoint inToPoint: CGPoint, withDepth inDepth: CGFloat) { 
     var thePoints = Array<CGPoint>(repeating: inFromPoint, count: 5) 

     thePoints[4] = inToPoint; 
     if inDepth <= 1.0 { 
      curveWithWeight(inDepth, points:&thePoints) 
      for i in 1..<5 { 
       inoutPath.addLine(to: thePoints[i]) 
      } 
     } 
     else { 
      let theDepth = inDepth - 1; 

      curveWithWeight(1.0, points:&thePoints) 
      for i in 0..<4 { 
       addPointsToPath(inoutPath, fromPoint:thePoints[i], toPoint:thePoints[i + 1], withDepth:theDepth) 
      } 
     } 
    } 

    func curveWithWeight(_ inWeight: CGFloat, points inoutPoints: inout [CGPoint]) { 
     let theFromPoint = inoutPoints[0] 
     let theToPoint = inoutPoints[4] 
     let theFactor = inWeight/(2 * sqrt(3)) 
     let theDelta = CGSize(width: theToPoint.x - theFromPoint.x, height: theToPoint.y - theFromPoint.y); 

     inoutPoints[1] = CGPoint(x: theFromPoint.x + theDelta.width/3, 
      y: theFromPoint.y + theDelta.height/3) 
     inoutPoints[2] = CGPoint(x: theFromPoint.x + theDelta.width/2 + theFactor * theDelta.height, 
      y: theFromPoint.y + theDelta.height/2 - theFactor * theDelta.width); 
     inoutPoints[3] = CGPoint(x: theToPoint.x - theDelta.width/3, 
      y: theToPoint.y - theDelta.height/3) 
    } 
} 
+0

'CGFloat' được dựa trên' double' và không phải trên một lớp trong mục tiêu C. Vì vậy, bạn không thể sử dụng tùy chọn cho điều này. Bạn nên khai báo nó theo ví dụ trong câu trả lời của tôi mà không có dấu chấm hỏi. – clemens

+1

Một lưu ý nhỏ (không quan trọng) ở đây ... Có lẽ 'dynamic' sẽ là một ứng viên tốt hơn' @ NSManaged' ở đây, kể từ khi Apple liên kết rõ ràng với CoreData ('Ngôn ngữ lập trình Swift/Swift 4/Page 874) – Alladinian

+1

@Alladinian mã Objective-C tương ứng sẽ là '@ dynamic' khác với từ khóa động trong Swift, vì vậy không may' dynamic' sẽ không hoạt động ở đây. –

Các vấn đề liên quan