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를 호출하여 로드를 취소한다.