'NSLocationWhenInUseUsageDescription'에 해당되는 글 1건

  1. 2023.10.09 Do it! 스위프트로 아이폰 앱만들기(7~8장 웹뷰, 맵뷰 )

7장. 웹 뷰로 간단한 웹 브라우저 만들기

8장. 맵 뷰로 지도 나타내기

 

# 웹뷰로 간단한 웹 브라우저 만들기

가. 웹뷰 제작 기초(권한 설정 등)

url을 String으로 받아  URLRequest로 받은 후, Web View 객체에 로드하면 됩니다. 만약 시뮬레이터에서 작동하지 않는다면, 프로젝트에서 Info.plist 파일을 열어 인터넷 관련 권한을 추가 설명해야 합니다. App Transport Security Settings에 + 를 누르고, Allow Arbitrary Loads를 선택하고 Value를 No 에서 Yes로 변경합니다. 그리고 시뮬레이터를 재시작합니다. 

func loadWebPage(_ url:String) {
	let myUrl = URL(string: url)
    let myRequest = URLRequest(url : myUrl!)
    myWebView.load(myrequest)
}

나. 액티비티 인디케이터로 로딩보이기

로딩을 기다릴 때, 화면 가운데서 돌아가는 원 모양의 점선이 바로 '액티비티 인디케이터 뷰'입니다. library 팔레트에서 Activity Indicator View를 선택한 후, WebKit View 위에 올려둔 후, Hide When Stopped로 동작을 멈추면 보이지 않게 설정합니다. 그 이후 코딩으로 webView를 설정하여 애니메이션을 작동.중지시키고, 숨기는 등의 행동의 재정의합니다. 

class ViewController: UIViewController, WKNavigationDelegate {

    @IBOutlet var txtUrl: UITextField!
    
    @IBOutlet var myWebView: WKWebView!
    
    @IBOutlet var myActivityIndicator: UIActivityIndicatorView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        myWebView.navigationDelegate = self
        loadWebPage("http://2sam.net")
    }
     
    func webView(_ webView: WKWebView, didCommit navigation : WKNavigation!) {
        myActivityIndicator.startAnimating()
        myActivityIndicator.isHidden = false
    }
    func webView(_ webView: WKWebView, didFinish  navigation : WKNavigation!) {
        myActivityIndicator.stopAnimating()
        myActivityIndicator.isHidden = true
    }
    func webView(_ webView: WKWebView, didFail navigation : WKNavigation!) {
        myActivityIndicator.stopAnimating()
        myActivityIndicator.isHidden = true
    }
    
    func loadWebPage(_ url:String) {
        let myUrl = URL(string: url)
        let myRequest = URLRequest(url:myUrl!)
        myWebView.load(myRequest)
        
    }

    @IBAction func btnGotoUrl(_ sender: UIButton) {
        let myUrl = checkUrl(txtUrl.text!)
        loadWebPage(myUrl)
    }
     
    @IBAction func btnGoSite1(_ sender: UIButton) {
        loadWebPage("http://fallinmac.tistory.com")
    }
    
    @IBAction func btnGoSite2(_ sender: UIButton) {
        loadWebPage("http://blog.2sam.net")
    }
    
    @IBAction func btnLoadHtmlString(_ sender: UIButton) {
        let htmlString = """
            <H1> HTML String</H1>
            <P>String 변수를 이용한 웹 페이지</P>
            <p><a href= \"http://2sam.net\">2sam</a>으로 이동</p>"
            """
        myWebView.loadHTMLString(htmlString, baseURL: nil)
    }
    
    @IBAction func btnLoadHtmlFile(_ sender: UIButton) {
        let filePath = Bundle.main.path(forResource: "htmlView", ofType: "html")
        let myUrl = URL(fileURLWithPath:filePath!)
        let myRequest = URLRequest(url:myUrl)
        myWebView.load(myRequest)
    }
    
    @IBAction func btnStop(_ sender: UIBarButtonItem) {
        myWebView.stopLoading()
    }
    
    @IBAction func btnReload(_ sender: UIBarButtonItem) {
        myWebView.reload()
    }
    
    @IBAction func btnGoBack(_ sender: UIBarButtonItem) {
        myWebView.goBack()
    }
    
    @IBAction func btnGoForward(_ sender: UIBarButtonItem) {
        myWebView.goForward()
    }
    
    func checkUrl(_ url: String) -> String {
        var strUrl = url
        let flag = (strUrl.hasPrefix("http://") || strUrl.hasPrefix("https://") )
        if !flag {
            strUrl = "http://" + strUrl
            print(strUrl)
        }
        return strUrl
    }
    
}

Refresh, Stop, Forward, Backward 등의 아이콘은 속성창의 System Item에서 제공하고 있어 이용하면 됩니다. 

제공하는 아이콘(Refresh)를 이용하는 모습

다. 앱 로딩시, 웹페이지 나타나도록 하기(로딩)

 "Info.plist" 파일을 수정해야 함. 목록에서 [App Transport Security Settings]을 선택한 후, "+" 를 클릭하여, [Allow Arbitrary Load]을 선택하고, 오른쪽 Value의 값을 "NO"에서 "YES"로 변경하면, 실행시 지정한 웹페이지가 보임.

라. 액티비티 인디케이터로 로딩보이기

Delegate를 추가하고, func 3개를 추가(로딩 중, 로딩 완료, 로딩 실패)

import UIKit
import WebKit

class ViewController :UIViewController, WKNavigationDelegate {

    ..............

    // 로딩 중일때,
    func webView(_ webView: WKWebView, didCommit navigation : WKNavigation!) {
        myActivityIndicator.startAnimating()
        myActivityIndicator.isHidden = false
    }
    // 로딩 완료시
    func webView(_ webView: WKWebView, didFinish navigation:WKNavigation!) {
    	myActivityIndicator.stopAnimating()
        myAcitvityIndicator.isHidden = true
    }
    //로딩 실패시
    func webView(_ webView: WKWebView, didFail navigation : WKNavigation!, withError error : Error) {
    	myActivityIndicator.stopAnimating()
        myActivityIndicator.isHidden = true
    }
    
}

 

# 맵 뷰로 지도 나타내기

- 맵뷰로 별도로 세그먼트 컨트롤이 있는데, 기능상 버튼과 동일한데 실제 어떤 것이 선택되었는지 알 수 있어 편리함

세그먼트 컨트롤(segment control) 화면

맵뷰를 실행했는데 아래와 같은 오류가 발생했다면, 프로젝트의 Info.plist파일을 열어 Information Property List 위로 가져가 + 클릭하여 "Privacy-Location When In Use Usage Description"을 선택하고 value를 더블클릭하여 "App needs location servers for stuff"로 수정하면 됩니다. 

This app has attempted to access privacy-sensitive data without a usage description. The app's Info.plist must contain an “NSLocationWhenInUseUsageDescription” key with a string value explaining to the user how the app uses this data
[VKDefault] Missing MeshRenderables for ground mesh layer for (4/4) of ground tiles. Tile debug info: (Key: 55.24.6.255 t:34 kt:0, Has mesh errors: 0, MeshInstance count: 1, PendingMaterial count: 1, Invisible MeshInstances count: 0 | Key: 54.25.6.255 t:34 kt:0, Has mesh errors: 0, MeshInstance count: 1, PendingMaterial count: 1, Invisible MeshInstances count: 0 | Key: 55.25.6.255 t:34 kt:0, Has mesh errors: 0, MeshInstance count: 1, PendingMaterial count: 1, Invisible MeshInstances count: 0 | Key: 54.24.6.255 t:34 kt:0, Has mesh errors: 0, MeshInstance count: 1, PendingMaterial count: 1, Invisible MeshInstances count: 0)
[Font] Failed to parse font key token: hiraginosans-w6
[Font] Failed to parse font key token: hiraginosans-w6
class ViewController: UIViewController, CLLocationManagerDelegate {
    
    let locationManager = CLLocationManager()

    @IBOutlet var myMap: MKMapView!
    
    @IBOutlet var lblLocationInfo1: UILabel!
    
    @IBOutlet var lblLocationInfo2: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
        lblLocationInfo1.text = ""
        lblLocationInfo2.text = ""
        locationManager.delegate = self
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
        locationManager.requestWhenInUseAuthorization()
        locationManager.startUpdatingLocation()
        myMap.showsUserLocation = true
    }
 
    func goLocation(latitudeValue : CLLocationDegrees, longitudeValue : CLLocationDegrees, delta span : Double ) -> CLLocationCoordinate2D{
        let pLocation = CLLocationCoordinate2DMake(latitudeValue, longitudeValue)
        let spanValue = MKCoordinateSpan(latitudeDelta: span, longitudeDelta: span)
        let pRegion = MKCoordinateRegion(center:pLocation, span:spanValue)
        myMap.setRegion(pRegion, animated: true)
        return pLocation
        
    }
    
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        let pLocation = locations.last
        _ = goLocation(latitudeValue: (pLocation?.coordinate.latitude)!, longitudeValue: (pLocation?.coordinate.longitude)!, delta: 0.01)
        CLGeocoder().reverseGeocodeLocation(pLocation!, completionHandler: {
            (placemarks, error) -> Void in
            let pm = placemarks!.first
            let country = pm!.country
            var address:String = country!
            if pm!.locality != nil {
                address += " "
                address += pm!.locality!
            }
            if pm!.thoroughfare != nil {
                address += " "
                address += pm!.thoroughfare!
            }
            
            self.lblLocationInfo1.text = "현재 위치"
            self.lblLocationInfo2.text = address
        })
        
        locationManager.stopUpdatingLocation()
    }
    
    // delta는 축약정도로 0.01이면, 지도를 100배로 확대해서 보여준다. 
    // 위도와 경도로 원하는 핀 설치.
    func setAnnotation(latitudeValue : CLLocationDegrees,
                       longitudeValue : CLLocationDegrees, delta span : Double, title strTitle:String, subtitle strSubtitle:String) {
        let annotation = MKPointAnnotation()
        annotation.coordinate = goLocation(latitudeValue:latitudeValue, longitudeValue: longitudeValue, delta: span)
        annotation.title = strTitle
        annotation.subtitle = strSubtitle
        myMap.addAnnotation(annotation)
    }
    
  
    @IBAction func sgChangeLocation(_ sender: UISegmentedControl) {
        if (sender.selectedSegmentIndex == 0 ) {
            self.lblLocationInfo1.text = " "
            self.lblLocationInfo2.text = " "
            locationManager.startUpdatingLocation()
            
        } else if sender.selectedSegmentIndex == 1 {
            setAnnotation(latitudeValue: 37.751853, longitudeValue: 128.87605740000004, delta: 1, title: "한국폴리텍대학 강릉캠퍼스", subtitle: "강원도 강릉시 남산초교길 121")
            self.lblLocationInfo1.text = "보고 계신 위치"
            self.lblLocationInfo2.text = "한국폴리텍대학 강릉캠퍼스"
        } else {
            setAnnotation(latitudeValue: 37.556876, longitudeValue: 126.914066, delta: 0.1, title: "이지스빌딩", subtitle: "서울시 마포구 잔다리로 109 이지스 빌딩")
            self.lblLocationInfo1.text = "보고 계신 위치"
            self.lblLocationInfo2.text = "이지스퍼블리싱 출판"
            
        }
    }
    
}

요약
- WebView로 기본기능이 있는 브라우저를 만들수 있다. Info.plist파일에서 권한 설정을 하고, 아이콘들은 모두 설정에서 필요에 맞는 것을 찾아 바꿔준다.
- MapView는 지도 권한을 Info.plist파일에 부여하고, 즐겨찾기/특정지역도착시 알림음 등을 개발할 수 있다. 

Posted by 목표를 가지고 달린다
,