Swift에서 애니메이션을 연속으로 실행하는 이야기

13179 단어 SwiftUIView
읽었다여기 기사.. 이렇게 말하면UIView 애니메이션도 보통 연속으로 실행해야 한다면completion의 회호지옥아, 회호지옥을 하지 않는 연속 실행 애니메이션을 실행해 봤다.
방법은 매우 간단하다. 애니메이션 블록을 하나씩 꺼내서 애니메이션이 끝난 후에 자신의 API를 호출하기만 하면 된다.결과는 다음과 같은 Playground 실행 가능한 소스 코드로 작성할 수 있습니다.
Playground.swift
let base = UIView(frame: CGRect(x: 0, y: 0, width: 400, height: 400))
base.backgroundColor = .white
PlaygroundPage.current.liveView = base

let view = UIView(frame: CGRect(x: 100, y: 100, width: 200, height: 200))
view.backgroundColor = .blue
base.addSubview(view)

// アニメーションブロック
UIView.animate(eachBlockDuration: 1, eachBlockDelay: 0, eachBlockOptions: .curveEaseInOut, animationBlocks:
    { view.frame.origin = .zero },
    { view.frame.size.width = 400 },
    { view.frame.size.height = 400 }
) { (finished) in
    print(finished)
}
플레이그라운드 콘서트에서 이걸 보면 이런 느낌이에요.

그렇다면 이animate(eachBlock...)의 실현은 바로 이런 느낌이다.
UIView.swift
private extension ArraySlice {

    var startItem: Element {
        return self[self.startIndex]
    }

}

extension UIView {

    private typealias `Self` = UIView

    private static func animate(eachBlockDuration duration: TimeInterval, eachBlockDelay delay: TimeInterval, eachBlockOptions options: UIViewAnimationOptions, animationArraySlice: ArraySlice<() -> Void>, completion: ((_ finished: Bool) -> Void)?) {

        let animation = animationArraySlice.startItem

        UIView.animate(withDuration: duration, delay: delay, options: options, animations: animation) { (finished) in

            let remainedAnimations = animationArraySlice.dropFirst()

            if remainedAnimations.isEmpty {
                completion?(finished)

            } else {
                Self.animate(eachBlockDuration: duration, eachBlockDelay: delay, eachBlockOptions: options, animationArraySlice: remainedAnimations, completion: completion)
            }

        }

    }

    public static func animate(eachBlockDuration duration: TimeInterval, eachBlockDelay delay: TimeInterval = 0, eachBlockOptions options: UIViewAnimationOptions = .curveEaseInOut, animationBlocks: (() -> Void)..., completion: ((_ finished: Bool) -> Void)? = nil) {

        let isFinished = animationBlocks.isEmpty

        guard isFinished == false else {
            completion?(isFinished)
            return
        }

        let animationArraySlice = ArraySlice(animationBlocks)

        Self.animate(eachBlockDuration: duration, eachBlockDelay: delay, eachBlockOptions: options, animationArraySlice: animationArraySlice, completion: completion)

    }

}
두 가지 아주 비슷한 방법이 있는데 하나는 public, 하나는 private이다. 그런데 왜 두 가지가 있을까? 가변 길이 파라미터를 다른 함수에 직접 전달할 수 없기 때문이다.실행 효율성이나 프로그램 내부의 용이성Array보다는 ArraySlice높기 때문에(재생성 기간.dropFirst() 없이 그렇게 사용) 매개변수를 ArraySlice<() -> Void>형으로 설정하는 방법을 준비했습니다.그러나ArraySlice의 경우 사용하는 쪽으로서 가변 길이 파라미터에 비해 역시 미묘한 군배가 비교적 낮고 사용하는 쪽은 가변 길이 파라미터, 내부는ArraySlice로 구분하여 사용한다.또한 public 측에서 매개 변수의 길이가 1 이상이어야 한다면first?도 자화자찬할 필요가 없다.마지막으로 private 방법ArraySlice을 비워 두면 전달된 애니메이션 블록을 하나씩 실행할 수 있습니다.편리한 방법˘ω˘)한동안
홍보:올해야말로!그래서 저는 iOSDC에 상담을 많이 신청했습니다!관심 있는 이야기가 있으면 RT 꼭 해주세요!
유행의 이니셜
ADDD(API-Design-Driven Development)
AVAudioEngine의 오디오 반복 재생 사용
Swift4.0 대처하려면 참담할 것 같아요.
책임 범위를 의식한 이야기
UIView 속성 및 방법

좋은 웹페이지 즐겨찾기