관리 메뉴

김종권의 iOS 앱 개발 알아가기

[iOS - swift] OperationQueue, Operation 사용하여 NotificationView 구현 본문

iOS 응용 (swift)

[iOS - swift] OperationQueue, Operation 사용하여 NotificationView 구현

jake-kim 2022. 3. 21. 22:04

NotificationView

OperationQueue를 사용하여 Noti 구현

  • NotificationView를 OperationQueue를 사용하여 보여지게 만들면, NotificationView 관련 작업을 cancel, suspend 할 수 있어서, 하나의 작업 단위로 Wrapping할 수 있는 장점이 존재
  • NotificationView를 사용하는 입장에서는 단순히 NotificationView를 띄우는 행위를 `하나의 기능`이라고 보고 NotificationView를 몰라도 NotificationService.show()하여 노티뷰를 띄우는 기능을 사용하도록 하기 위함
  • NotificationService 기능
protocol NotificationService {
  func show(title: String, delay: UInt32, completion: @escaping () -> Void)
  func cancel()
  func suspend(enabled: Bool)
}

* OperationQueue 상세한 개념은 이전 포스팅 글 참고

구현 아이디어

  • 노티 뷰로 사용될 NotificationView 구현
  • NotificationService 구현 (NotificationView 인스턴스를 가지고 OperationQueue에 삽입하여 사용)

NotificationView 구현

  • NotificationView 생성
    • 해당 파일에서 NotificationView를 사용하고, 사용하는 입장에서 View의 존재를 몰라도 되므로 private으로 선언
// NotificationService.swift

import UIKit

private class NotificationView: UIView {
  private lazy var titleLabel: UILabel = {
    let label = UILabel()
    label.font = .systemFont(ofSize: 22)
    label.numberOfLines = 2
    label.lineBreakMode = .byTruncatingTail
    label.textAlignment = .center
    label.translatesAutoresizingMaskIntoConstraints = false
    self.addSubview(label)
    return label
  }()
  
  init(title: String) {
    super.init(frame: .zero)
    self.backgroundColor = .systemGray3
    self.titleLabel.text = title
    NSLayoutConstraint.activate([
      self.leftAnchor.constraint(equalTo: self.titleLabel.leftAnchor, constant: 12),
      self.rightAnchor.constraint(equalTo: self.titleLabel.rightAnchor, constant: -12),
      self.bottomAnchor.constraint(equalTo: self.titleLabel.bottomAnchor, constant: -12),
      self.topAnchor.constraint(equalTo: self.titleLabel.topAnchor, constant: 12),
    ])
  }
  
  required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }
}
  • NotificationOperation 정의
    • Operation을 상속받아서 main()부를 정의하여, 여기서 뷰 인스턴스를 만들고 애니메이션을 주어 노티뷰를 띄워주는 작업
    • cancel 되었을 때, completion을 실행하지 않도록 self.isCancelled 값 이용
// NotificationService.swift

private class NotificationOperation: Operation {
  private let title: String
  private let delay: UInt32
  private let completion: () -> Void
  
  init(title: String, delay: UInt32, completion: @escaping () -> Void) {
    self.title = title
    self.delay = delay
    self.completion = completion
  }
  
  override func main() {
    sleep(self.delay)
    
    DispatchQueue.main.async {
      let notificationView = NotificationView(title: self.title)
      notificationView.alpha = 0
      notificationView.translatesAutoresizingMaskIntoConstraints = false
      guard let window = UIApplication.shared.windows.first else { return }
      
      window.addSubview(notificationView)
      NSLayoutConstraint.activate([
        notificationView.leftAnchor.constraint(equalTo: window.safeAreaLayoutGuide.leftAnchor),
        notificationView.rightAnchor.constraint(equalTo: window.safeAreaLayoutGuide.rightAnchor),
        notificationView.topAnchor.constraint(equalTo: window.safeAreaLayoutGuide.topAnchor),
        notificationView.heightAnchor.constraint(equalToConstant: 80)
      ])
      
      UIView.animate(
        withDuration: 1,
        delay: 0,
        options: .curveEaseOut,
        animations: { notificationView.alpha = 1 },
        completion: { _ in
          
          UIView.animate(
            withDuration: 1,
            delay: 1,
            options: .curveEaseIn,
            animations: { notificationView.alpha = 0 },
            completion: { _ in
              notificationView.removeFromSuperview()
              if !self.isCancelled {
                self.completion()
              }
            }
          )
        }
      )
    }
  }
}
  • NotificationService로 위에서 정의한 Operation을 사용하는 인터페이스 정의
    • show: 노티 뷰를 보여주는 작업
    • cancel: 노티 뷰 operation을 취소할 수 있어, 위에서 isCancelled를 추가하여 completion이 실행되지 않도록 하는 기능
    • suspend: 노티 뷰 OperationQueue가 실행되지 않도록 막는 기능
protocol NotificationService {
  func show(title: String, delay: UInt32, completion: @escaping () -> Void)
  func cancel()
  func suspend(enabled: Bool)
}

final class NotificationServiceImpl: NotificationService {
  private let operationQueue = OperationQueue()
  
  func show(title: String, delay: UInt32, completion: @escaping () -> Void) {
    let operation = NotificationOperation(title: title, delay: delay, completion: completion)
    self.operationQueue.addOperation(operation)
  }
  func cancel() {
    self.operationQueue.cancelAllOperations()
  }
  func suspend(enabled: Bool) {
    self.operationQueue.isSuspended = !enabled
  }
}

사용하는 쪽

// ViewController.swift

  private var notificationService: NotificationService?

  override func viewDidLoad() {
    super.viewDidLoad()
    self.notificationService = NotificationServiceImpl()  
  }
  
  @objc private func didTapNotiButton() {
    self.notificationService?.show(title: "iOS앱 개발 알아가기", delay: 1) {
      print("노티 완료")
    }
  }

* 전체 코드: https://github.com/JK0369/ExNotificationView

Comments