최근에 조금 시간이 나서 이전부터 개선 하고 싶었던 Toast 다시 개발하게 되었습니다.
개선하고 싶었던 것은
그럼 먼저 UIWindow에 대해 살짝 알아보죠.
The backdrop for your app’s user interface and the object that dispatches events to your views
설명하자면 UIWindow는 UIView의 서브 클래스로, 앱의 UI를 담는 컨테이너이며, view에 이벤트를 전달하는 객체입니다. UIWindow는 뷰 컨트롤러와 함께 이벤트를 처리하고 앱의 기본적인 작업을 수행합니다. UIKit이 대부분의 창 관련 상호작용을 처리합니다
UIWindow 자체는 시각적인 화면이 없으며, 하나 이상의 View를 호스팅합니다. 이 view는 window의 rootViewController에 의해 관리됩니다.
Window를 통해 z축을 조절할 수 있다는 점 + 인앱 메시지를 보낼 수 있는 라이브러리에서도 Window를 새로 만들어서 사용하는 것을 보고 Toast용 Window를 만들면 어떨까 생각했습니다.
// sceneDelegate에서 windowScene을 받아서 사용
let window = UIWindow(windowScene: windowScene)
window.windowLevel = .alert // 최상단으로 사용. 기본은 normal
window.backgroudColor = .clear
window.isUserInteractionEnabled = false
window.isHidden = true
이렇게 만든 window를 Toast를 띄우는데 사용했습니다.
이전 Toast는 해당 ViewController View의 최상단에 띄웠었습니다.
여기서 발생하는 문제는 새로운 viewController가 push 되거나 화면 이동이 발생하면 Toast가 가려지게 된다는 점입니다.
keyWindow를 찾아 최상단에 Toast를 띄워준다 하더라도 위와 같이 modal을 새롭게 present하게 되면, 뷰 계층 순서가 바뀌어서 가려지게 됩니다.
이렇게 별도로 만든 Toast용 Window는 main Window보다 z축 레벨이 더 높기 때문에 영향을 받지 않는 다는 것을 확인할 수 있습니다.
Serial Queue의 들어온 작업은 하나의 작업이 끝나야 다음 작업을 시작합니다.
...생략...
private let serialQueue = DispatchQueue(label: "toaster.queue")
private var toastQueue: [Toast] = []
private var isShowing: Bool = false
func showToast(_ toast: Toast) {
serialQueue.async { [weak self] in
self?.toastQueue.append(toast)
self?.showNextToast()
}
}
private func showNextToast() {
serialQueue.async { [weak self] in
guard let self,
!self.isShowing,
!self.toastQueue.isEmpty,
let window else { return }
self.isShowing = true
DispatchQueue.main.async {
let toast = self.toastQueue.removeFirst()
self.toastView.text = toast
UIView.animate(withDuration: 0.3, delay: .zero, options: .curveEaseIn, animations: {
window.alpha = 1
}) { _ in
UIView.animate(withDuration: 0.3, delay: 2, animations: {
window.alpha = 0
}) { _ in
self.serialQueue.async {
self.isShowing = false
self.showNextToast()
}
}
}
}
}
}
showToast를 통해 Toast를 Queue에 넣어주고 Serial Queue를 사용해 하나씩 순서대로 보여지도록 설정하였습니다.