관리 메뉴

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

[iOS - swift] custom loading 구현 방법 (UIWindow 사용) 본문

iOS 기본 (swift)

[iOS - swift] custom loading 구현 방법 (UIWindow 사용)

jake-kim 2022. 5. 22. 16:51

구현된 로딩

Custom Loading 구현 아이디어

  • 최상위 UIWindow는 UIView를 서브클래싱한 인스턴스이므로, window.addSubview하여 커스텀 로딩 화면을 넣어주면 UIWindow하위에 존재하는 현재 화면위에 로딩이 뜨도록 구현
  • Loading 애니메이션은 Lottie 사용

사용한 프레임워크

  • Lottie -로딩 애니메이션에 사용
  • SnapKit - 코드 베이스로 레이아웃 구현에 편의를 위해 사용
  pod 'lottie-ios'
  pod 'SnapKit'

UIWindow 개념

  • UIWindow는 사용자에게 보여지는 화면을 담고 있는 최상위 ContainerView
  • 실제로도 UIWindow는 UIView의 서브클래스
  • 이 밖에도 UI 이벤트 처리도 같이 담당

https://developer.apple.com/library/archive/documentation/WindowsViews/Conceptual/ViewPG_iPhoneOS/WindowsandViews/WindowsandViews.html#//apple_ref/doc/uid/TP40009503-CH2-SW1

  • 싱글톤인, AppDelegate로 window에 접근하여 사용할 수 있도록 AppDelegate에 아래 코드 추가
static var window: UIWindow { (UIApplication.shared.delegate?.window!)! }

Custom Loading 뷰 구현

  • 사용하는쪽에서 로딩 뷰의 auto layout을 화면에 꽉차게 해놓고 사용
  • 커스텀 로딩 뷰 UI 구성
    • 원래 가지고 있는 view - 아래 깔려있는 뷰들을 눌러도 interaction이 동작하지 않게끔 가려지는 역할
    • contentView - 이 뷰의 addSubview되게하고, 혹시 다른 인터렉션이 필요한 경우 이 위에다 UI를 배치
    • loadingView - Lottie 프레임워크에서 지원해주는 AnimationView이며, 애니메이션 재생에 사용
import UIKit
import Lottie
import SnapKit

final class MyLoadingView: UIView {
  private let contentView: UIView = {
    let view = UIView()
    view.backgroundColor = .white
    view.alpha = 0
    return view
  }()
  private let loadingView: AnimationView = {
    let view = AnimationView(name: "loading_ball")
    view.loopMode = .loop
    return view
  }()
}
  • 레이아웃
override init(frame: CGRect) {
  super.init(frame: frame)
  self.backgroundColor = .black.withAlphaComponent(0.3)
  
  self.addSubview(self.contentView)
  self.contentView.addSubview(self.loadingView)
  
  self.contentView.snp.makeConstraints {
    $0.center.equalTo(self.safeAreaLayoutGuide)
  }
  self.loadingView.snp.makeConstraints {
    $0.center.equalTo(self.safeAreaLayoutGuide)
    $0.size.equalTo(300)
  }
}
  • show와 hide 메소드 구현
    • show 메소드에서는 window에 넣는것임을 주의
    • 로딩 화면이 서서히 보여지는 애니메이션을 주기 위해서 alpha값을 이용
  func show() {
    guard !AppDelegate.window.subviews.contains(where: { $0 is MyLoadingView }) else { return }
    AppDelegate.window.addSubview(self)
    self.snp.makeConstraints {
      $0.edges.equalToSuperview()
    }
    self.layoutIfNeeded()
    
    self.loadingView.play()
    UIView.animate(
      withDuration: 0.7,
      animations: { self.contentView.alpha = 1 }
    )
  }
  func hide(completion: @escaping () -> () = {}) {
    self.loadingView.stop()
    self.removeFromSuperview()
    completion()
  }

로딩뷰를 사용하는 쪽

  • 인스턴스를 만들어서 show()
  // ViewController.swift
  
  @objc private func didTapButton() {
    let loadingView = MyLoadingView()
    loadingView.show()
    
    DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
      loadingView.hide {
        self.loadingButton.setTitle("로딩 종료!", for: .normal)
      }
    }
  }
  • 위와같이 사용해도 괜찮지만, 위 인스턴스는 단순히 show(), hide()메소드만 호출하므로 싱글톤으로 만들어서 사용하면 깔끔한 코드 완성
// MyLoadingView.swift
final class MyLoadingView: UIView {
  static let shared = MyLoadingView()
  ...
  
  private init() {
    ...
  }
  
  ...
}

// ViewController.swift
@objc private func didTapButton() {
  MyLoadingView.shared.show()
  
  DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
    MyLoadingView.shared.hide() {
      self.loadingButton.setTitle("로딩 종료!", for: .normal)
    }
  }
}

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

 

* 참고

https://developer.apple.com/documentation/uikit/uiwindow

https://developer.apple.com/library/archive/documentation/WindowsViews/Conceptual/ViewPG_iPhoneOS/WindowsandViews/WindowsandViews.html#//apple_ref/doc/uid/TP40009503-CH2-SW1

Comments