2013-01-07 40 views
19

Trên UIView bạn có thể thay đổi backgroundColour hoạt ảnh. Và trên UISlideView bạn có thể thay đổi giá trị hoạt ảnh.Tạo thuộc tính có thể tùy chỉnh tùy chỉnh

Bạn có thể thêm thuộc tính tùy chỉnh vào lớp con UIView riêng để có thể hoạt ảnh không?

Nếu tôi có CGPath trong số UIView thì tôi có thể tạo hiệu ứng bản vẽ của nó bằng cách thay đổi phần trăm được vẽ của đường dẫn.

Tôi có thể đóng gói hoạt ảnh đó vào lớp con không.

tức là tôi có UIView với CGPath tạo vòng kết nối.

Nếu hình tròn không ở đó, nó đại diện cho 0%. Nếu vòng tròn đầy, nó thể hiện 100%. Tôi có thể vẽ bất kỳ giá trị nào bằng cách thay đổi phần trăm được vẽ của đường dẫn. Tôi cũng có thể tạo hiệu ứng thay đổi (trong phân lớp UIView) bằng cách làm động phần trăm của CGPath và vẽ lại đường dẫn.

Tôi có thể đặt một số thuộc tính (tức là tỷ lệ phần trăm) trên UIView để tôi có thể dán thay đổi vào khối UIView animateWithDuration và nó tạo hiệu ứng thay đổi tỷ lệ phần trăm của đường dẫn không?

Tôi hy vọng tôi đã giải thích những gì tôi muốn làm tốt.

Về cơ bản, tất cả tôi muốn làm là một cái gì đó giống như ...

[UIView animateWithDuration:1.0 
animations:^{ 
    myCircleView.percentage = 0.7; 
} 
completion:nil]; 

và con đường vòng tròn động với tỷ lệ nhất định.

Trả lời

22

Nếu bạn mở rộng CALayer và thực hiện các tùy chỉnh của bạn

- (void) drawInContext:(CGContextRef) context 

Bạn có thể làm cho một tài sản animatable bằng cách ghi đè needsDisplayForKey (trong lớp CALayer tùy chỉnh của bạn) như thế này:

+ (BOOL) needsDisplayForKey:(NSString *) key { 
    if ([key isEqualToString:@"percentage"]) { 
     return YES; 
    } 
    return [super needsDisplayForKey:key]; 
} 

Tất nhiên, bạn cũng cần có một số @property gọi là percentage. Từ bây giờ, bạn có thể tạo hiệu ứng cho thuộc tính phần trăm bằng cách sử dụng hoạt ảnh lõi. Tôi cũng không kiểm tra xem nó có hoạt động hay không bằng cách sử dụng cuộc gọi [UIView animateWithDuration...]. Có thể đấy. Nhưng điều này đã làm việc cho tôi:

CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"percentage"]; 
animation.duration = 1.0; 
animation.fromValue = [NSNumber numberWithDouble:0]; 
animation.toValue = [NSNumber numberWithDouble:100]; 

[myCustomLayer addAnimation:animation forKey:@"animatePercentage"]; 

Oh và sử dụng yourCustomLayer với myCircleView, làm điều này:

[myCircleView.layer addSublayer:myCustomLayer]; 
+0

Cảm ơn bạn đã trả lời. Tối nay tôi sẽ xem. – Fogmeister

+0

@Fogmeister Xin chào, tôi đã đến muộn một chút. Đã hy vọng hỏi: làm thế nào chúng ta sẽ thực hiện '- (void) drawInContext: (CGContextRef) context'? Đây có phải là nơi một thuộc tính mới được tùy chỉnh sẽ được vẽ không? Cảm ơn trước. – Unheilig

+0

@Tom trong mẫu của bạn, bạn đặt thời lượng theo cách thủ công thành 1 ... bạn sẽ có thể tìm ra thời lượng/đường cong/độ trễ, vv ... được cung cấp bởi khối UIViewAnimation như thế nào? – Georg

0

bạn sẽ phải tự mình thực hiện phần trăm phần trăm. bạn có thể ghi đè lên mã vẽ lớp để vẽ đường dẫn của bạn accroding đến giá trị phần trăm thiết lập. kiểm tra các core animation programming guideanimation types and timing guide

+0

Cảm ơn bạn đã trả lời. Tối nay tôi sẽ xem. – Fogmeister

+0

lý do tại sao downvote? mà sau một thời gian dài như vậy? – lukya

+0

Không có ý tưởng. Không có gì để làm với tôi. – Fogmeister

3

Đối với những người cần biết thêm chi tiết về điều đó như tôi đã làm:

có một câu hỏi thú vị là example from Apple.

Ví dụ: nhờ đó tôi thấy rằng bạn không thực sự cần phải thêm lớp tùy chỉnh của bạn làm lớp con (như @Tom van Zummeren gợi ý).Thay vào đó là đủ để thêm phương thức lớp vào lớp Chế độ xem của bạn:

+ (Class)layerClass 
{ 
    return [CustomLayer class]; 
} 

Hy vọng nó sẽ giúp ai đó.

7

Hoàn Swift 3 ví dụ:

Final product

public class CircularProgressView: UIView { 

    public dynamic var progress: CGFloat = 0 { 
     didSet { 
      progressLayer.progress = progress 
     } 
    } 

    fileprivate var progressLayer: CircularProgressLayer { 
     return layer as! CircularProgressLayer 
    } 

    override public class var layerClass: AnyClass { 
     return CircularProgressLayer.self 
    } 


    override public func action(for layer: CALayer, forKey event: String) -> CAAction? { 
     if event == #keyPath(CircularProgressLayer.progress), 
      let action = action(for: layer, forKey: #keyPath(backgroundColor)) as? CAAnimation { 

      let animation = CABasicAnimation() 
      animation.keyPath = #keyPath(CircularProgressLayer.progress) 
      animation.fromValue = progressLayer.progress 
      animation.toValue = progress 
      animation.beginTime = action.beginTime 
      animation.duration = action.duration 
      animation.speed = action.speed 
      animation.timeOffset = action.timeOffset 
      animation.repeatCount = action.repeatCount 
      animation.repeatDuration = action.repeatDuration 
      animation.autoreverses = action.autoreverses 
      animation.fillMode = action.fillMode 
      animation.timingFunction = action.timingFunction 
      animation.delegate = action.delegate 
      self.layer.add(animation, forKey: #keyPath(CircularProgressLayer.progress)) 
     } 
     return super.action(for: layer, forKey: event) 
    } 

} 



/* 
* Concepts taken from: 
* https://stackoverflow.com/a/37470079 
*/ 
fileprivate class CircularProgressLayer: CALayer { 
    @NSManaged var progress: CGFloat 
    let startAngle: CGFloat = 1.5 * .pi 
    let twoPi: CGFloat = 2 * .pi 
    let halfPi: CGFloat = .pi/2 


    override class func needsDisplay(forKey key: String) -> Bool { 
     if key == #keyPath(progress) { 
      return true 
     } 
     return super.needsDisplay(forKey: key) 
    } 

    override func draw(in ctx: CGContext) { 
     super.draw(in: ctx) 

     UIGraphicsPushContext(ctx) 

     //Light Grey 
     UIColor.lightGray.setStroke() 

     let center = CGPoint(x: bounds.midX, y: bounds.midY) 
     let strokeWidth: CGFloat = 4 
     let radius = (bounds.size.width/2) - strokeWidth 
     let path = UIBezierPath(arcCenter: center, radius: radius, startAngle: 0, endAngle: twoPi, clockwise: true) 
     path.lineWidth = strokeWidth 
     path.stroke() 


     //Red 
     UIColor.red.setStroke() 

     let endAngle = (twoPi * progress) - halfPi 
     let pathProgress = UIBezierPath(arcCenter: center, radius: radius, startAngle: startAngle, endAngle: endAngle , clockwise: true) 
     pathProgress.lineWidth = strokeWidth 
     pathProgress.lineCapStyle = .round 
     pathProgress.stroke() 

     UIGraphicsPopContext() 
    } 
} 

let circularProgress = CircularProgressView(frame: CGRect(x: 0, y: 0, width: 80, height: 80)) 
UIView.animate(withDuration: 2, delay: 0, options: .curveEaseInOut, animations: { 
    circularProgress.progress = 0.76 
}, completion: nil) 

Có một objc bài viết tuyệt vời here, mà đi vào chi tiết về cách làm việc này

Cũng như một dự án objc rằng sử dụng cùng một khái niệm here:

Hành động cơ bản (đối với lớp :) sẽ được gọi khi một đối tượng đang được hoạt hình từ một khối hoạt hình, chúng ta có thể bắt đầu hoạt ảnh của chính chúng ta với các thuộc tính giống nhau (bị đánh cắp từ thuộc tính backgroundColor) và tạo các thay đổi.

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