#ios #swift #asynchronous #dispatchsemaphore
Вопрос:
Я хотел оживить альфу uiviews и, похоже, не могу этого сделать. Он ведет себя странно и сообщает об ошибке, что запуск изменений пользовательского интерфейса не рекомендуется запускать в фоновом потоке, но я не знаю, как заставить его работать в основном потоке. Кто-нибудь может мне помочь? Я считаю, что он пропускает первый блок UIView.animate и выполняет то, что во втором, без какой-либо анимации вообще.
функция аниматесемафора() {
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()
}
}
}
Ответ №1:
Вам нужно вставить любой код, связанный с пользовательским интерфейсом/анимацией, в основной поток, а не в фоновую очередь
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
}
}
Комментарии:
1. Я понимаю вашу точку зрения, но я хотел, чтобы это было как-то правильно написано. Нет ли другого способа не записывать все в блок завершения других частей кода? Когда это написано таким образом, это просто ошеломляет и не так легко читается.
2. Из предыдущей анимации. Один завершается, второй запускает 5-секундный таймер, а затем воспроизводится.
3. Я попробовал это, но он пропускает строку 43 до строки 59, а затем 50.
4. Я понимаю, но могу ли я как-то избежать вставки всего в обработчики завершения, или другого пути нет?
Ответ №2:
Вы также можете создать конфигурацию для создания анимации (черновик можно найти здесь). 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()