아이폰 개발/Swift

swift custom loading view, custom indicator

인생여희 2021. 10. 25. 23:29

swift custom loading view, custom indicator

 

만들어볼 custom indicator

 

네트워크 통신을 할때, 앱위에 로딩뷰나 인디케이터를 띄워준다. 사용자에게 무언가가 일어나고있다는 걸 보여주고, 작동중이니 걱정말라는 표시도 될 수 있다. 너무 길어지면 안되겠지만.

 

위 그림처럼 start 버튼을 누르면 이미지가 다양하게 바뀌면서 loading view 를 띄워주거나, indicator 를 띄워주는 예제를 정리해보았다.

 

 

먼저 loading view는 아래와 같이 스토리 보드로 만들어주었다. UIView type의 커스텀 클래스를 하나 만들어서 스토리 보드와 이어준다. 

 

 

loading view나 custom indicator 만드는 방법은 여러가지가 있을 것 같다. 

 

나는 custom view를 이용했고, timer를 이용했다. 

 

그리고 이미지이름 배열을 이용해서 timer 0.3초마다 이미지가 바뀌도록 처리했다. 

 

메소드는 시작 함수와 종료 함수를 만들었다. 

 

그럼 IndicatorView를 어디서 호출해주나?


import UIKit

class IndicatorView: UIView {

    var timer:Timer?
    @IBOutlet weak var loadingImage: UIImageView!
    @IBOutlet weak var loadingLabel: UILabel!
    
    
    let imageNames = ["wand.and.rays" , "wand.and.rays.inverse", "wand.and.stars", "wand.and.stars.inverse"]
    
    //MARK: 초기화
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        loadXib()
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        loadXib()
    }

    //MARK: Xib 로드
    private func loadXib(){
        
        //self 는 최상위 view - 보이지 않음
        self.tag = 100
        
        let identifier = String(describing: type(of: self))
        let nibs = Bundle.main.loadNibNamed(identifier, owner: self, options: nil)
        
        // 첫번째 Nib 중에서 첫번째 View
        guard let customView = nibs?.first as? UIView else {return}
        customView.frame = self.bounds
        customView.backgroundColor = UIColor(white: 1, alpha: 0.3)
        //customView.tag = 100
        self.addSubview(customView)
    }
    
    //MARK: 시작
    public func startLoading(){
        var i = 0
        timer = Timer.scheduledTimer(withTimeInterval: 0.3, repeats: true) { timer in
            if i >= self.imageNames.count { i = 0 }
            self.loadingImage.image = UIImage(systemName: self.imageNames[i])
            i += 1
        }
    }
    
    
    //MARK: 정지
    public func stopLoading(){
        timer?.invalidate()
    }
    
    
}

 

 

IndicatorView는 어느화면에서 꼭 필요한 화면이다. 그렇기 때문에 싱글톤 클래스를 만들어서 그 싱글톤 클래스가 indicatorVIew를 컨트롤할 수있게 해보았다. 

 

아래 처럼 Center 라는 싱글톤 클래스를 만들어주고 , indicatorView를 띄워주는 함수, 숨기는 함수를 만들었다. 그리고 indicator view, loading view를 최상위 view 위에 띄워야 되므로, 최상위 view를 구하는 함수도 만들어 주었다.

 

import Foundation
import UIKit
class Center {
    
    //MARK: 로딩뷰
    var indicatorView:IndicatorView?
    
    static let shared = Center()
    private init(){ }
    
    //MARK: 보이기
    func showLoadingPage(_view:UIViewController) {

        let width = getRootViewController(vc: _view)?.view.frame.size.width ?? 0
        let height = getRootViewController(vc: _view)?.view.frame.size.height ?? 0

        indicatorView = IndicatorView(frame: CGRect(x: 0, y: 0, width: width , height: height))
        
        if let indicatorView = indicatorView {
            indicatorView.startLoading()
            self.getRootViewController(vc:_view)?.view.addSubview(indicatorView)
        }

    }

    //MARK: 숨기기
    func hideLoadingPage(_view:UIViewController) {
        
        // loading View의 tag 는 100
        if let loadingView = self.getRootViewController(vc: _view)?.view.viewWithTag(100) {
            
            indicatorView?.stopLoading()
            loadingView.removeFromSuperview()
        
        }
    }
    
    /**
     # getRootViewController
     - Author: k
     - Date:
     - Parameters:
        - vc: rootViewController 혹은 UITapViewController
     - Returns: UIViewController?
     - Note: vc내에서 가장 최상위에 있는 뷰컨트롤러 반환
    */
    public func getRootViewController(vc:UIViewController) ->UIViewController?{

    
        ///[1] 네비게이션 컨트롤러
        if let nc = vc as? UINavigationController {

            if let vcOfnavController = nc.visibleViewController {
                return self.getRootViewController(vc: vcOfnavController)
            }
        
        ///[2] 탭뷰 컨트롤러
        }else if let tc = vc as? UITabBarController {
            
            if let tcOfnavControler = tc.selectedViewController {
                return self.getRootViewController(vc: tcOfnavControler)
            }
            
        ///[3] 뷰 컨트롤러
        }else{
            if let pvc = vc.presentedViewController{
                return self.getRootViewController(vc: pvc)
            }else {
                return vc
            }
        }
        
        return nil
    }

}

 

 

그리고 마지막으로 아래와 같이 위에서 만든 싱글톤 클래스를 사용해서 indicator 를 호출해주면 끝~

 

import UIKit

class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
    }

    @IBAction func showLoadingImag(_ sender: UIButton) {
        print("ViewController - showLoadingImag")
        Center.shared.showLoadingPage(_view: self)
        _ = Timer.scheduledTimer(withTimeInterval: 6, repeats: false) { _ in
            Center.shared.hideLoadingPage(_view: self)
        }
        
    }
    @IBAction func stopAction(_ sender: UIButton) {
        print("ViewController - stopAction")

    }
    
}

 

 

IndicatorView.swift
0.00MB
IndicatorView.xib
0.00MB
Center.swift
0.00MB
ViewController.swift
0.00MB