Диспетчерский семафор для пользовательского интерфейса
Я хотел оживить альфа-версию uiviews, но не могу этого сделать. Он ведет себя странно и сообщает об ошибке, что изменения пользовательского интерфейса не рекомендуется запускать в фоновом потоке, но я не знаю, как заставить его работать в основном потоке. Кто-нибудь может мне помочь? Я считаю, что он пропускает первый блок UIView.animate и выполняет то, что во втором, без какой-либо анимации.
func animateSemaphore() {
circleRed.alpha = 0.2
circleOrange.alpha = 0.2
circleGreen.alpha = 1
let dispatchSemaphore = DispatchSemaphore(value: 0)
let dispatchQueue = DispatchQueue.global(qos: .background)
dispatchQueue.async {
UIView.animate(withDuration: 0.5, delay: 5, options: .curveEaseInOut) {
self.circleOrange.alpha = 1
self.circleGreen.alpha = 0.2
} completion: { (_) in
print("1")
dispatchSemaphore.signal()
}
dispatchSemaphore.wait()
UIView.animate(withDuration: 0.5, delay: 3, options: .curveEaseInOut) {
self.circleOrange.alpha = 0.2
self.circleRed.alpha = 1
} completion: { (_) in
dispatchSemaphore.signal()
}
dispatchSemaphore.wait()
UIView.animate(withDuration: 0.5, delay: 5, options: .curveEaseInOut) {
self.circleOrange.alpha = 1
} completion: { (_) in
dispatchSemaphore.signal()
}
dispatchSemaphore.wait()
UIView.animate(withDuration: 0.5, delay: 1, options: .curveEaseInOut) {
self.circleOrange.alpha = 0.2
self.circleRed.alpha = 0.2
self.circleGreen.alpha = 1
} completion: { (_) in
self.animateSemaphore()
}
}
}
3 ответа
Вам нужно вставить любой связанный код ui / animate внутри основного потока, а не внутри фоновой очереди
func animateSemaphore() {
circleRed.alpha = 0.2
circleOrange.alpha = 0.2
circleGreen.alpha = 1
UIView.animate(withDuration: 0.5, delay: 5, options: .curveEaseInOut) {
self.circleOrange.alpha = 1
self.circleGreen.alpha = 0.2
} completion: { (_) in
UIView.animate(withDuration: 0.5, delay: 3, options: .curveEaseInOut) {
self.circleOrange.alpha = 0.2
self.circleRed.alpha = 1
} completion: { (_) in
UIView.animate(withDuration: 0.5, delay: 5, options: .curveEaseInOut) {
self.circleOrange.alpha = 1
} completion: { (_) in
UIView.animate(withDuration: 0.5, delay: 1, options: .curveEaseInOut) {
self.circleOrange.alpha = 0.2
self.circleRed.alpha = 0.2
self.circleGreen.alpha = 1
} completion: { (_) in
}
}
}
}
}
ИЛИ поиграйте с
delay
вместо анимации вложенности
func animateSemaphore() {
circleRed.alpha = 0.2
circleOrange.alpha = 0.2
circleGreen.alpha = 1
UIView.animate(withDuration: 0.5, delay: 5, options: .curveEaseInOut) {
self.circleOrange.alpha = 1
self.circleGreen.alpha = 0.2
} completion: { (_) in
print("1")
}
UIView.animate(withDuration: 0.5, delay: 8.5, options: .curveEaseInOut) {
self.circleOrange.alpha = 0.2
self.circleRed.alpha = 1
} completion: { (_) in
}
UIView.animate(withDuration: 0.5, delay: 14, options: .curveEaseInOut) {
self.circleOrange.alpha = 1
} completion: { (_) in
}
UIView.animate(withDuration: 0.5, delay: 15.5, options: .curveEaseInOut) {
self.circleOrange.alpha = 0.2
self.circleRed.alpha = 0.2
self.circleGreen.alpha = 1
} completion: { (_) in
}
}
Вы также можете создать конфигурацию для создания анимации (черновик можно найти здесь)https://gist.github.com/maximkrouk/76163b3f2775dafc73c5633d155368cb И добавить немного сглаживания
public struct UIViewAnimation {
private init(
provider: UIViewAnimatiorProvider?,
animations: @escaping () -> Void,
completion: ((Bool) -> Void)? = nil
) {
self.provider = provider
self.animations = animations
self.completion = completion
}
public init(
config: UIViewAnimatiorProvider,
animations: @escaping () -> Void,
completion: ((Bool) -> Void)? = nil
) {
self.init(
provider: config,
animations: animations,
completion: completion
)
}
let provider: UIViewAnimatiorProvider?
let animations: () -> Void
let completion: ((Bool) -> Void)?
public func run() {
if let provider = provider {
provider.makeAnimator(
for: animations,
completion: completion ?? { _ in }
).animate()
} else {
animations()
completion?(true)
}
}
public func appendingCompletion(_ completion: @escaping (Bool) -> Void) -> UIViewAnimation {
UIViewAnimation(
provider: provider,
animations: animations,
completion: { isFinished in
self.completion?(isFinished)
completion(isFinished)
}
)
}
public static let empty: UIViewAnimation = .init(
provider: nil,
animations: {},
completion: nil
)
}
extension UIViewAnimation {
public static func sequence(_ animations: UIViewAnimation...) -> UIViewAnimation {
guard var animation = animations.last else { return .empty }
animations.dropLast().reversed().forEach { prevAnimation in
let animate = animation.run
animation = prevAnimation.appendingCompletion { _ in animate() }
}
return animation
}
}
И используйте это как
extension UIView {
func animateSemaphore() {
let initialBackground = backgroundColor
UIViewAnimation.sequence(
UIViewAnimation(config: .init(duration: 2)) {
self.backgroundColor = .red
},
UIViewAnimation(config: .init(duration: 2)) {
self.backgroundColor = .green
},
UIViewAnimation(config: .init(duration: 2)) {
self.backgroundColor = .red
},
UIViewAnimation(config: .init(duration: 2)) {
self.backgroundColor = .green
},
UIViewAnimation(config: .init(duration: 2)) {
self.backgroundColor = initialBackground
}
).run()
}
}
UIView().animateSemaphore()
Или используйте DispatchSemafore, но со значением 1.
if presentedViewController != nil {
let semafore = DispatchSemaphore(value: 1)
dismiss(animated: true) {
semafore.signal()
}
semafore.wait()
}