본문 바로가기

카테고리 없음

iOS 프로그래밍 실무 12주차

화면 전환 방법 크게 2가지

 

위에 활성된 부분은 각 

네비게이션 컨트롤러 -> 네비게이션 바

뷰 컨트롤러 -> 네비게이션 아이템(백버튼)

 

네이비게이션 컨트롤러 추가시 변경 내용

 

 

 

safari에서 주소를 복사해서 사용

 

메인화면에서 영화이름을 선택시 네이버 영화설명이 tab바에서 지도를 선택시 영화관 지도가 나오게 된다.
*시험*

화면에 제일 먼저 나타나는 뷰 -> root view controller

*시험*

prepare (외부 내부 : 자료형, sender 외부내부 겸함:자료형)

->sugue가 실행되기 전에 자동으로 호출되는 매소드 

prepare(for:sender:)는 UIKit의 UIViewController 에서 제공하는 메서드로, 화면 전환(Segue)이 실행되기 직전에 데이터를 전달하거나 목적지 뷰 컨트롤러를 설정할 때 사용합니다.

기본 형태

 
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    super.prepare(for: segue, sender: sender)
}
 

언제 호출될까?

예를 들어 스토리보드에서 ViewController A → ViewController B로 Segue를 연결해 놓았다면,

  1. 사용자가 버튼을 누름
  2. Segue가 시작됨
  3. prepare(for:sender:) 호출
  4. ViewController B가 화면에 표시됨

즉, 목적지 ViewController가 나타나기 직전에 실행됩니다.


목적지 ViewController에 데이터 전달하기

ViewController B

 
class DetailViewController: UIViewController {
    var userName: String?
}
 

ViewController A

 
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "showDetail" {
        let destinationVC = segue.destination as! DetailViewController
        destinationVC.userName = "Kim"
    }
}
 

이렇게 하면 DetailViewController가 표시될 때 userName에 "Kim"이 전달됩니다.


segue.identifier를 사용하는 이유

한 ViewController에서 여러 개의 Segue를 사용할 수 있기 때문입니다.

 
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

    switch segue.identifier {
    case "showProfile":
        let vc = segue.destination as! ProfileViewController
        vc.userId = 100

    case "showSettings":
        let vc = segue.destination as! SettingsViewController
        vc.theme = "Dark"

    default:
        break
    }
}
 

sender는 무엇인가?

Segue를 발생시킨 객체가 전달됩니다.

예를 들어 버튼이 여러 개라면:

 
@IBAction func buttonTapped(_ sender: UIButton) {
    performSegue(withIdentifier: "showDetail", sender: sender)
}
 
 
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

    if let button = sender as? UIButton {
        print(button.tag)
    }
}
 

어떤 버튼이 화면 전환을 발생시켰는지 확인할 수 있습니다.


Navigation Controller가 있는 경우

목적지 컨트롤러가 Navigation Controller 안에 포함되어 있을 수 있습니다.

 
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

    if let nav = segue.destination as? UINavigationController,
       let detailVC = nav.topViewController as? DetailViewController {

        detailVC.userName = "Kim"
    }
}
 

performSegue와 함께 사용

코드로 Segue를 실행할 수도 있습니다.

 
performSegue(withIdentifier: "showDetail", sender: self)
 

그러면 자동으로:

 
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    // 여기 호출됨
}
 

이 실행된 후 화면이 전환됩니다.


전체 예제

 
class ViewController: UIViewController {

    var selectedName = "Alice"

    @IBAction func moveButtonTapped(_ sender: UIButton) {
        performSegue(withIdentifier: "showDetail", sender: self)
    }

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

        if segue.identifier == "showDetail" {
            let detailVC = segue.destination as! DetailViewController
            detailVC.userName = selectedName
        }
    }
}
 
 
class DetailViewController: UIViewController {

    var userName: String?

    override func viewDidLoad() {
        super.viewDidLoad()

        print(userName ?? "")
    }
}
 

실행 결과:

Alice
 

핵심 정리

 
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
 
  • Segue가 실행되기 직전 호출됨
  • 목적지 ViewController에 데이터를 전달할 때 사용
  • segue.identifier로 어떤 Segue인지 구분
  • segue.destination으로 목적지 ViewController 접근
  • sender로 화면 전환을 발생시킨 객체 확인 가능
  • performSegue()를 호출해도 자동으로 실행됨

Swift 개발에서는 "화면 전환 전에 데이터 전달" 이라는 용도로 가장 많이 사용되는 메서드입니다.

 

url을 가지고 특정 웹 사이트를 보여주는 소스 

a.b.com사이트를 웹뷰로 보여주는 swift 방법을 설명해줘

=>

OS에서 a.b.com 사이트를 앱 내부에서 웹뷰로 보여주려면 보통 WKWebView를 사용합니다. UIWebView는 오래전에 deprecated 되었습니다.

1. WKWebView 추가하기

코드만으로 구현

 
import UIKit
import WebKit

class WebViewController: UIViewController {

    private var webView: WKWebView!

    override func viewDidLoad() {
        super.viewDidLoad()

        webView = WKWebView(frame: view.bounds)
        webView.autoresizingMask = [.flexibleWidth, .flexibleHeight]

        view.addSubview(webView)

        if let url = URL(string: "https://a.b.com") {
            let request = URLRequest(url: url)
            webView.load(request)
        }
    }
}
 

2. Storyboard 사용하기

ViewController에 WKWebView 연결

 
import UIKit
import WebKit

class WebViewController: UIViewController {

    @IBOutlet weak var webView: WKWebView!

    override func viewDidLoad() {
        super.viewDidLoad()

        let url = URL(string: "https://a.b.com")!
        let request = URLRequest(url: url)

        webView.load(request)
    }
}
 

Storyboard에서:

  1. ViewController 추가
  2. WKWebView를 드래그해서 배치
  3. IBOutlet 연결

3. 버튼 클릭 시 웹뷰 화면으로 이동

첫 번째 화면

 
@IBAction func openWeb(_ sender: UIButton) {
    performSegue(withIdentifier: "showWeb", sender: self)
}
 

웹뷰 화면

 
class WebViewController: UIViewController {

    @IBOutlet weak var webView: WKWebView!

    var urlString: String?

    override func viewDidLoad() {
        super.viewDidLoad()

        if let urlString,
           let url = URL(string: urlString) {

            webView.load(URLRequest(url: url))
        }
    }
}
 

데이터 전달 (prepare(for:sender:))

 
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

    if segue.identifier == "showWeb" {
        let vc = segue.destination as! WebViewController
        vc.urlString = "https://a.b.com"
    }
}
 

4. 로딩 상태 표시

 
class WebViewController: UIViewController, WKNavigationDelegate {

    @IBOutlet weak var webView: WKWebView!

    override func viewDidLoad() {
        super.viewDidLoad()

        webView.navigationDelegate = self

        let url = URL(string: "https://a.b.com")!
        webView.load(URLRequest(url: url))
    }

    func webView(_ webView: WKWebView,
                 didStartProvisionalNavigation navigation: WKNavigation!) {
        print("로딩 시작")
    }

    func webView(_ webView: WKWebView,
                 didFinish navigation: WKNavigation!) {
        print("로딩 완료")
    }
}
 

5. 뒤로 가기 지원

 
@IBAction func backButtonTapped(_ sender: UIButton) {

    if webView.canGoBack {
        webView.goBack()
    }
}
 

6. 외부 브라우저 대신 앱 내부에서 열기

기본적으로:

 
UIApplication.shared.open(url)
 

를 사용하면 Safari가 열립니다.

앱 내부에서 보여주려면:

 
webView.load(URLRequest(url: url))
 

를 사용해야 합니다.


7. 특정 사이트만 허용하기

예를 들어 a.b.com 외의 사이트 이동을 막고 싶다면:

 
extension WebViewController: WKNavigationDelegate {

    func webView(
        _ webView: WKWebView,
        decidePolicyFor navigationAction: WKNavigationAction,
        decisionHandler: @escaping (WKNavigationActionPolicy) -> Void
    ) {

        if let host = navigationAction.request.url?.host,
           host.contains("a.b.com") {

            decisionHandler(.allow)
        } else {
            decisionHandler(.cancel)
        }
    }
}
 

실무에서 가장 많이 쓰는 형태

 
import UIKit
import WebKit

class WebViewController: UIViewController {

    private let webView = WKWebView()

    override func viewDidLoad() {
        super.viewDidLoad()

        view.addSubview(webView)
        webView.frame = view.bounds

        let url = URL(string: "https://a.b.com")!
        webView.load(URLRequest(url: url))
    }
}
 

이 구조를 기반으로 prepare(for:sender:)에서 URL을 전달받아 다양한 웹 페이지를 표시하는 방식이 가장 일반적입니다.

 

아규먼트를 생략하는 매소드

Swift에서는 외부 매개변수명(Argument Label)내부 매개변수명(Parameter Name) 을 구분하기 때문입니다.

WKWebView의 load 메서드 선언을 보면:

 
func load(_ request: URLRequest) -> WKNavigation?
 

여기서 _가 핵심입니다.

 
func load(_ request: URLRequest)
          ^
 

_는 외부 매개변수명을 사용하지 말라는 의미입니다.

그래서 호출할 때:

 
webView.load(request)
 

처럼 쓰고,

 
webView.load(request: request)
 

는 컴파일 에러가 발생합니다.


외부 매개변수명이 있는 경우

예를 들어:

 
func move(to position: Int) {
    print(position)
}
 

호출:

 
move(to: 10)
 

여기서

  • 외부 매개변수명: to
  • 내부 매개변수명: position

입니다.


_를 사용한 경우

 
func move(_ position: Int) {
    print(position)
}
 

호출:

 
move(10)
 

외부 매개변수명이 없어집니다.


URL 생성도 같은 원리

 
let url = URL(string: "https://a.b.com")
 

선언은:

 
init?(string: String)
 

이므로

 
URL("https://a.b.com")
 

는 안 되고,

 
URL(string: "https://a.b.com")
 

처럼 string:을 반드시 써야 합니다.


Swift API 디자인 규칙

Swift는 메서드를 영어 문장처럼 읽히게 만드는 것을 선호합니다.

예:

 
UIView.animate(withDuration: 0.3)
 

읽으면:

Animate with duration 0.3

반면

 
webView.load(request)
 

Load request

라고 자연스럽게 읽히므로 request:를 생략하도록 설계된 것입니다.

그래서 Apple API를 보면:

 
webView.load(request)
array.append(item)
view.addSubview(label)
 

처럼 목적어 역할의 첫 번째 인자는 _로 생략하는 경우가 많고,

 
present(vc, animated: true)
URL(string: ...)
animate(withDuration: ...)
 

처럼 의미를 명확히 해야 하는 경우는 외부 매개변수명을 사용합니다.

정리하면 load(request)가 가능한 이유는 load 메서드 정의가:

 
func load(_ request: URLRequest)
 

처럼 첫 번째 매개변수 앞에 _가 있기 때문입니다. _는 "호출할 때 매개변수 이름을 쓰지 마라"는 Swift 문법입니다.