2012-05-30 36 views
10

Tôi đang cố tạo một màn hình tùy chỉnh UIView trên màn hình cho 5s khi có thông báo từ xa.Làm thế nào để thực hiện hoạt ảnh 'nối tiếp' với GCD?

Mã như thế này:

//customView.alpha = 1.0 here 
[UIView animateWithDuration:1 animations:^{ 
            customView.alpha = 0.3; 
           } 
           completion:^(BOOL finished){ 
            // remove customView from super view. 
           }]; 

Vấn đề và Những gì tôi cần

Nhưng có trường hợp đang là một vài thông báo có thể đến ở khoảng thời gian ngắn, trong đó một số customView thể hoạt hình cùng một lúc và một người có thể bao gồm người khác.

Tôi muốn các hoạt ảnh này thực hiện từng cái một, để chúng không xung đột.

Giả nhưng không thành công

//(dispatch_queue_t)queue was created in other parts of the code 
dispatch_sync(queue, ^{ 
    [UIView animationWithDuration:animations:...]; 
}); 

Sau khi thực hiện các hình ảnh động trong GCD hàng đợi, tôi nhận được kết quả tương tự như mã nguồn gốc tôi sử dụng, mà không sử dụng GCD. Ảnh động vẫn còn xung đột.

BTW, tôi nghe nói rằng hình ảnh động hoặc nhiệm vụ liên quan đến giao diện người dùng nên luôn luôn được chạy trên thread chính, nhưng trong mã thứ hai của tôi hoạt hình dường như làm smoothy. Tại sao?

Trả lời

4

Nếu đó là hình động tương tự chạy mỗi lần bạn có thể lưu trữ số lần hoạt ảnh sẽ chạy (không giống như thuộc tính đếm lặp của hoạt ảnh).

Khi bạn nhận được thông báo từ xa, bạn tăng bộ đếm và gọi phương thức hoạt ảnh nếu bộ đếm chính xác. Sau đó, trong methodThatAnimates bạn đệ quy gọi cho mình trong khối hoàn thành trong khi giảm truy cập mỗi lần.Nó sẽ giống như thế này (với tên phương pháp giả):

- (void)methodThatIsRunWhenTheNotificationIsReceived { 
    // Do other stuff here I assume... 
    self.numberOfTimesToRunAnimation = self.numberOfTimesToRunAnimation + 1; 
    if ([self.numberOfTimesToRunAnimation == 1]) { 
     [self methodThatAnimates]; 
    } 
} 

- (void)methodThatAnimates { 
    if (self.numberOfTimesToRunAnimation > 0) { 
     // Animation preparations ... 
     [UIView animateWithDuration:1 
         animations:^{ 
            customView.alpha = 0.3; 
         } 
         completion:^(BOOL finished){ 
            // Animation clean up ... 
            self.numberOfTimesToRunAnimation = self.numberOfTimesToRunAnimation - 1; 
            [self methodThatAnimates]; 
         }]; 
    } 
} 
+1

Bạn có ý tưởng tương tự như @Ducan. Cảm ơn mã của bạn. Và bạn có nghĩ rằng chúng ta nên khóa 'self.numberOfTimesToRunAnimation'? – studyro

+0

Có. Bằng cách không xác định thuộc tính là "nonatomic" và không bao giờ truy cập trực tiếp biến (luôn sử dụng thuộc tính), hệ thống sẽ khóa biến cho bạn sao cho hai luồng không đọc/ghi cùng một lúc. –

+0

Rất đẹp. Tôi đã sử dụng điều này để kiểm soát giao diện người dùng SegedControl. Bằng cách đặt 'selectedIndex' vào thuộc tính và đặt nó thành' NSNotFound' khi hoàn thành, tôi không phải tắt điều khiển trong khi hoạt ảnh. Cảm ơn! –

0

Tôi khuyên bạn nên gửi thư trong khối hoàn thành cho bất kỳ đối tượng nào đang kích hoạt hoạt ảnh. Sau đó, bạn có thể có đối tượng đó xếp hàng các thông báo và bắt đầu thông báo tiếp theo mỗi khi nhận được thông báo.

1

Bạn có thể sử dụng một (không) đồng thời NSOperationQueue để thực hiện các hình ảnh động "từng bước"

Lớp NSOperationQueue quy định thực hiện một tập các đối tượng NSOperation. Sau khi được thêm vào hàng đợi, một thao tác vẫn nằm trong hàng đợi đó cho đến khi nó được hủy bỏ một cách rõ ràng hoặc kết thúc thực hiện nhiệm vụ của nó. Các hoạt động trong hàng đợi (nhưng chưa thực thi) được sắp xếp theo mức độ ưu tiên và phụ thuộc đối tượng liên kết và được thực hiện tương ứng. Một ứng dụng có thể tạo ra nhiều hàng đợi hoạt động và gửi các hoạt động tới bất kỳ hàng đợi nào.

Phụ thuộc liên hoạt cung cấp thứ tự thực thi tuyệt đối cho các hoạt động , ngay cả khi các hoạt động đó nằm trong các hàng đợi hoạt động khác nhau . Một đối tượng hoạt động không được coi là sẵn sàng để thực hiện cho đến khi tất cả các hoạt động phụ thuộc của nó đã hoàn thành việc thực thi. Đối với các hoạt động đã sẵn sàng để thực hiện, hàng đợi vận hành luôn thực thi một ưu tiên cao nhất liên quan đến các hoạt động sẵn sàng khác .

+1

animateWithDuration: ... không đồng bộ. Điều đó sẽ giải quyết vấn đề như thế nào? Mỗi thao tác sẽ kết thúc ngay lập tức, trước khi hoạt ảnh hoàn tất. –

+0

đặt thuộc tính isFinished trong khối hoàn thành trên hoạt ảnh. Từ các tài liệu: "Đường dẫn khóa isFinished cho phép khách hàng biết rằng một hoạt động đã hoàn thành nhiệm vụ của nó thành công hoặc đã bị hủy bỏ và đang thoát." – CarlJ

4

Sử dụng hàng đợi để nộp hình ảnh động trong chuỗi sẽ không làm việc, bởi vì phương pháp bắt đầu trở về hình ảnh động ngay lập tức, và các hình ảnh động được thêm vào phim hoạt hình cây sẽ được thực hiện sau. Mỗi mục trong hàng đợi của bạn sẽ hoàn thành trong một phần nhỏ của một giây.

Nếu mỗi hoạt ảnh của bạn hoạt động trên cùng một chế độ xem thì theo mặc định, hệ thống sẽ cho phép mỗi hoạt ảnh kết thúc chạy trước khi hoạt ảnh tiếp theo bắt đầu.

Để trích dẫn tài liệu cho giá trị tùy chọn UIViewAnimationOptionBeginFromCurrentState:

UIViewAnimationOptionBeginFromCurrentState

Khởi động hoạt hình từ các thiết lập hiện tại gắn liền với một hình ảnh động đã trên máy bay. Nếu phím này không có, mọi hoạt ảnh trong chuyến bay đều được phép kết thúc trước khi bắt đầu hoạt ảnh mới. Nếu hoạt ảnh khác là không hoạt động, khóa này không có hiệu lực.

Nếu bạn muốn chuỗi một loạt các hình ảnh động, đây là những gì tôi sẽ làm:

Tạo một mảng có thể thay đổi các khối hình ảnh động. (các khối mã là các đối tượng và có thể được thêm vào một mảng.) Viết một phương thức để kéo khối hoạt hình đầu ra khỏi mảng (và loại bỏ nó khỏi mảng) và gửi nó bằng animateWithDuration: animations: completion, nơi phương thức hoàn thành đơn giản gọi lại phương thức. Làm cho mã xác nhận một khóa trước khi kéo một mục ra khỏi mảng, và giải phóng khóa sau khi xóa mục.

Sau đó, bạn có thể viết mã phản hồi thông báo đến bằng cách xác nhận khóa mảng hoạt ảnh, thêm khối hình động vào khóa và nhả khóa.

+0

Cảm ơn! Tôi nghĩ đây là một cách phù hợp để làm những gì tôi cần. – studyro

0

ProcedureKit (dựa trên NSOperation) là ví dụ về giải pháp làm sẵn, nhưng nó khá nặng để chỉ sử dụng cho hoạt ảnh.

My Operation lớp con mà tôi sử dụng để xếp hàng hoạt hình pop-up và các công cụ khác:

class SerialAsyncOperation: Operation { 

    private var _started = false 

    private var _finished = false { 
     willSet { 
      guard _started, newValue != _finished else { 
       return 
      } 
      willChangeValue(forKey: "isFinished") 
     } 
     didSet { 
      guard _started, oldValue != _finished else { 
       return 
      } 
      didChangeValue(forKey: "isFinished") 
     } 
    } 

    private var _executing = false { 
     willSet { 
      guard newValue != _executing else { 
       return 
      } 
      willChangeValue(forKey: "isExecuting") 
     } 
     didSet { 
      guard oldValue != _executing else { 
       return 
      } 
      didChangeValue(forKey: "isExecuting") 
     } 
    } 

    override var isAsynchronous: Bool { 
     return true 
    } 

    override var isFinished: Bool { 
     return _finished 
    } 

    override var isExecuting: Bool { 
     return _executing 
    } 

    override func start() { 
     guard !isCancelled else { 
      return 
     } 
     _executing = true 
     _started = true 
     main() 
    } 

    func finish() { 
     _executing = false 
     _finished = true 
    } 

    override func cancel() { 
     _executing = false 
     _finished = true 
     super.cancel() 
    } 
} 

Ví dụ về sử dụng:

// Setup a serial queue 
private lazy var serialQueue: OperationQueue = { 
    let queue = OperationQueue() 
    queue.maxConcurrentOperationCount = 1 
    queue.name = String(describing: type(of: self)) 
    return queue 
}() 

// subclass SerialAsyncOperation 
private class MessageOperation: SerialAsyncOperation { 

    // ... 

    override func main() { 
     DispatchQueue.main.async { [weak self] in 
      // do UI stuff 

      self?.present(completion: { 
       self?.finish() 
      }) 
     } 
    } 

    func present(completion: @escaping() -> Void) { 
     // do async animated presentation, calling completion() in its completion 
    } 

    func dismiss(completion: @escaping() -> Void) { 
     // do async animated dismissal, calling completion() in its completion 
    } 

    // animated cancellation support 
    override func cancel() { 
     if isExecuting { 
      dismiss(completion: { 
       super.cancel() 
      }) 
     } else { 
      super.cancel() 
     } 
    } 
} 

Về cơ bản, chỉ cần thêm hoạt động này cho một hàng đợi sê-ri, và hãy nhớ gọi số finish() khi bạn hoàn thành công việc không đồng bộ của mình. Ngoài ra bạn có thể hủy bỏ tất cả các hoạt động trên một hàng đợi nối tiếp với một cuộc gọi, và chúng sẽ được loại bỏ một cách duyên dáng.

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