100 Days of Swift - Day 25

sun02·2021년 9월 13일
0

100 days of Swift 

목록 보기
22/40

UIToolbar는 사용자가 탭 할 수 있는 UIBarButtonItem 의 집합이다.
UIProgressView는 작업이 얼만큼 수행되었는지 보여주는 바이다.

UIToolbar를 사용하는 방법은 매우 간단하다. UInavigationContorller 내에서 view controller가 활성화될 때 자동으로 읽혀지는 toolbarItem이 자동으로 제공된다.


let spacer = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, targer: nil, action: nil)
let refresh = UIBarButtonItem(barButtonSystemItem: .refresh, target: webView, action: #selector(webView.reload))

toolbarItems = [spacer, refresh]
navigationController?.isToolbarHidden = false
  • spacer: 스프링처럼 작동하여 공간이 모두 사용될 때까지 다른 버튼을 한 쪽으로 밀어준다.
    • .flexibleSpace : 유연한 공간을 만드는 특수한 시스템 항목
    • 탭할 수 없기 때문에 행동이나 타겟이 필요하지 않다.
  • refresh: 만든 메서드 대신 web View의 메서드 reload()를 호출한다.
  • toolbarItems = [spacer, refresh] : 유연한 공간과 새로 고침 버튼을 포함하는 배열을 toolbarItems 배열로 설정한다.
  • navigationController.isToolbarHidden = false : toolbar가 보여지게 한다.

var progressView: UIProgressView!

progressView = UIProgressView(progressViewStyle: .default)
progressView.sizeToFit()
let progressButton = UIBarButtonItem(customView: progressView)
  • progressView = UIProgressView(progressViewStyle: .default) : UIProgressView 인스턴스를 만들어 스타일을 제공한다
    • .bar : 진행 정도를 나타낼 때 채워지지 않은 선은 보이지 않음
    • .default : 진행 정도를 나타낼 때 채워지지 않은 선 보임
  • progressView.sizeToFit() : 레이아웃이 딱 맞도록 크기 설정
  • let progressButton = UIBarButtonItem(customView: progressView) : customView 매개변수를 사용하여 새로운 UIBarButtonItme을 만든다.
    • 생성한 UIProgressView가 toolbar에 들어갈 수 있도록 UIBarButtonItem으로 래핑한다.

toolbarItems = [progressButton, spacer, refresh]
 
  • toolbarItems 배열에 들어간 순서대로 보여지게 된다.

KVO(key-value observing) : "오브젝트 Y의 프로퍼티 X가 변화할 때 마다 알려줘"

  • WKWebView는 estimatedProgress 프로퍼티를 사용하여 해당 페이지가 얼마나 로드되었는지 알려주지만, WKNavigationDelegate 는 언제 이 값이 변경되는 시점을 알려주지 안흔다. 따라서 우리는 KVO를 이용하여 알려달라고 요청한다.

webView.addObserver(self, forkeyPath: #keyPath(WKWebView.estimatedProgress),options: .new, context:nil)
  • webView.addObserver() : web view의 프로퍼티 관찰자로 스스로를 추가한다.
    • self : 관찰자가 누구인지
    • forkeyPath : 어떤 프로퍼티를 관찰하고 싶은지, 이 경우 WKWebView의 estimatedProgress
    • options : 원하는 값, 이 경우 방금 설정한 값이므로 새 값을 원함
    • context : 고유한 값을 제공하면, 값이 변화할 때마다 해당 컨텍스트가 전송된다.

override func observeValue(forkeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
	if keyPath == "estimatedProgress" {
    	progressView.progress = Float(webView.estimatedProgress) 
    }
}
 
  • KVO를 사용하여 observer를 등록한 경우, 반드시 observeValue()라는 메서드를 호출해야한다. 이것은 언제 관찰된 값이 변화하는지 알려준다.
  • keyPath == "estimatedProgress" : keyPath 매개변수가 estimatedProgress로 설정되어있는지 확인한다.
    • webView의 estimatedProgresss 값이 변화한다면 ProgressView의 progress 프로퍼티를 새로운 estimatedProgress 값으로 설정한다.
    • UIProgressView의 progress 프로퍼티는 Float 이고 estimatedProgress는 Double 이므로 새로운 Float을 생성해야한다.

func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
	let url = navigationAction.request.url
    
    if let host = url?.host {
    	for website in websites {
        	if host.contains(website) {
            	decisionHandler(.allow)
                return
            }
        }
    }
    
    decisionHandler(.cancel)
  • 이 메서드가 호출되면 decisionHandler라는 매개변수가 전달되고, 이 매개변수가 실제 함수를 보유하고 있기 때문에 매개변수를 호출해야 실제로 함수가 호출된다.
    • 클로져는 변수처럼 함수에 전달할 수 있고 나중에 실행될 수 있는 코드 덩어리이다. 이 decisionHandler는 코드 덩어리를 제공하기보다 그것을 받고 실행해야 한다.
  • decisionHandler 변수/함수를 가지는 것은 사용자에게 "이 페이지를 정말로 로드하고 싶습니까?"라는 인터페이스를 보여주고 답변이 있을 때 해당 클로져를 호출할 수 있다는 의미이다.
    • @escaping : decisionHandler 클로져를 바로 호출할 수도 있고 나중에 호출할 수도 있기 때문에 이스케이프 클로져로 간주한다. 따라서 이 클로져는 현재 메서드에서 벗어날 가능성이 있고 나중에 사용할 수도 있다.
    • URL을 평가하여 안전한지 확인한 다음 부정 또는 긍정 응답으로 decisionHandler를 호출한다.
  • let url = navigationAction.request.url : 상수 url에 navigation의 URL을 설정한다.
  • if let host = url?.host : 옵셔널인 url.host의 값을 언래핑하기 위해 if let 구문을 사용한다.
    • "이 URL에 대한 호스트(웹사이트 도메인)이 있다면 꺼내십시오."
  • host.contains(website) : website가 호스트의 이름에 포함되어 있는지 알아본다.
    • contains() : 검사할 문자열을 제공받고, 문자열이 내부에 있다면 true를 반환한다.
    • 이 때 사이트의 이름은 URL의 아무곳에나 나타날 수 있기 때문에 hasPrefix()는 적합하지 않다.
  • decisionHandler(.allow) , return : website가 존재한다면, 긍정적인 응답으로 decision Handler를 호출하여 로딩을 허용하고, 메서드를 끝낸다.
  • decisionHandler(.cancel) : host가 없거나 아무것도 발견하지 못한 경우 부정적인 응답으로 decision Handler를 호출하여 로드를 취소한다.

0개의 댓글