Lined Notebook

화면 전환과 데이터 이동

by 사슴비행기

 

화면 전환과 데이터 이동

 

Basic Knowlege

* 화면전환만? 데이터도?

iOS에서 화면전환을 하는 방법은 여러가지가 있다.

1. storyboard와 class와 coding을 이용한 방법 - 화면이동 & 데이터전달

2. segue를 이용한 방법 - 화면이동 & 데이터전달(하위 View에서 상위 View로 단반향만 전달 가능)

그래서 용도에 맞게 어떤 코드를 사용할지 생각해야 한다.

 

1. storyboard와 class와 coding을 이용한 방법 - 화면이동 & 데이터전달

 * 안드로이드는 액티비티를 만들면 레이아웃 파일과 소스파일이 같이 만들어진다. 하지만 iOS는 따로 만들어진다.

 * 대신에 디자인은 한 화면에서 할 수 있기 때문에 UI 구조파악이 쉽다는 장점이 있다.

 * 레이아웃과 소스파일을 따로 만들어야 하기 때문에 클래스(소스파일)로 객체생성을 했을 때 이 객체가 어느 View(레이아웃)의 객체인지 연결을 해주어야 한다.

 * 그래서 각 뷰에 연결된 class와 storyboard ID를 설정해 주어야 한다.

 

 1) 상위 ViewController의 참조 가져오기

  * 다른 프로그래밍 언어에서는 하위 ViewController가 상위 ViewController에게 데이터를 넘기려면 상위 ViewController의 참조를 하위 ViewController에게 넘겨주고 하위 ViewController에서 접근을 해야 한다.

  * iOS에서는 자신을 출력한 상위 ViewController에 대한 참조를 처음부터 제공한다. (presentingViewContoller)

  * 이 참조는 화면을 이동할 때에도 쓰이고 데이터를 전달할 때도 쓰인다.

  * 이 코드로 화면전환을 하면 Stack구조로 화면이 쌓인다고 생각하면 된다. 즉, 상위 ViewController 위에 하위 ViewController가 올라오는 것. 그래서 다시 상위 ViewController로 이동할 때는 하위 ViewController를 제거하면 된다. 하위 ViewController가 올라온 상태에서 상위 ViewController는 잠시 숨겨진 상태인 것이다.

  * 이 때 앱의 생명주기에 따른 메소드 실행 순서를 잘 알아야 한다. 앱이 처음 구동될 때와 다시 화면이 보여질 때 구동되는 메소드가 약간씩 변동되거나 다르기 때문이다.

 

 2) 화면을 전환하면서 데이터를 전달하려면 구조를 파악해야 한다.

  (1) 종속적인 경우

   * 다음 화면 객체를 직접 생성한 구조

   * 하위 컨트롤러에 프로퍼티를 만들고 상위 컨트롤러가 하위 컨트롤러의 인스턴스를 생성한 후, 하위 프로퍼티에 데이터를 대입한다.

  (2) 종속적이지 않은 경우

   * 다음 화면 객체를 직접 생성하지 않은 경우의 구조

   * 공통 영역을 이용해야 하는데 iOS는 AppDelegate가 공통 영역이 될 수 있다.

 

 

 


1. storyboard와 class와 coding을 이용한 방법 - 화면이동 & 데이터전달

 

Want It ①

※ 처음 화면(ViewController-기본제공)을 1번이라 하고 화면 전환 할 화면(SecondViewController)을 2번이라 함.

1. 2번을 컨트롤 할 클래스 생성 (만들 형식의 클래스를 상속받아야 함)

2. 스토리보드에서 만들 형식의 클래스를 하나 배치하고 class 속성과 storyboard ID 속성을 1.에서 만든 클래스 이름으로 설정한다.

3. 1번에서 2번으로 화면을 전환하려면 2번의 인스턴스가 필요하다. 그리고 2번 인스턴스를 만드려면 스토리보드 인스턴스가 필요하다.

 1) 스토리보드 인스턴스를 생성한다.

 2) 2번 인스턴스를 생성한다.

 3) 화면을 전환한다.

 

클래스 생성

화면 디자인

클래스와 화면 연결

ViewController.swift

import UIKit

class ViewController: UIViewController {

    //storyboard 연결 변수
    @IBOutlet weak var lblFirstView: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    @IBAction func moveSecondView(_ sender: Any) {
    	//스토리보드 인스턴스 생성
        let storyboard = UIStoryboard.init(name: "Main", bundle: Bundle.main)
        //2번 인스턴스 생성
        let secondView = storyboard.instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController
        //화면전환
        self.present(secondView, animated: true)
    }

}

SecondViewController.swift

import UIKit

class SecondViewController: UIViewController {

    //storyboard 연결 변수
    @IBOutlet weak var lblSecondView: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    @IBAction func movePrev(_ sender: Any) {
        //현재 화면을 제거(상위 참조를 이용해서 내 자신을 삭제하기)
        self.presentingViewController?.dismiss(animated: true)
    }

 

Result

 


Want It ②

※ ①을 베이스로 한다.

1. 처음 앱을 구동하면 1번 View에는 FirstView라는 문구가 뜬다.

2. 1번 View에서 버튼을 클릭하면 2번 View로 이동한다.(1번 View는 숨김 상태)

3. 2번 View의 라벨은 1번 View에서 넘긴 데이터(String)가 뜬다.

4. 2번 View에서 버튼을 클릭하면 2번 뷰가 삭제되고 다시 1번 View가 뜬다.

5. 1번 View의 라벤에는 2번 VIew가 넘긴 데이터(String)이 뜬다.

 

SecondViewController.swift

import UIKit

class SecondViewController: UIViewController {

    //storyboard 연결 변수
    @IBOutlet weak var lblSecondView: UILabel!
    
    //상위 ViewController로부터 데이터를 받을 프로퍼티
    var data : String!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        //상위 ViewController로부터 받은 데이터를 출력
        lblSecondView.text = data
    }
    
    @IBAction func movePrev(_ sender: Any) {
        //상위 ViewController의 참조 가져오기
        let parent = self.presentingViewController as! ViewController
        
        //상위 ViewController에 데이터 전달
        parent.name = "역시 원두는 케냐AA"
        
        //현재 화면을 제거
        parent.dismiss(animated: true)
        
        //self.presentingViewController?.dismiss(animated: true)
    }

}

ViewController.swift

import UIKit

class ViewController: UIViewController {
    
    //storyboard 연결 변수
    @IBOutlet weak var lblFirstView: UILabel!
    
    //하위 SecondViewController로부터 데이터를 받을 프로퍼티
    var name : String = ""
    
    //DidLoad는 메모리에 올리는 것
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    //WillAppear는 화면에 그리는 것
    //앱 처음 구동 시에는 DidLoad와 WillAppear가 둘다 실행되고
    //화면이 다시 보여질 때는 WillAppear만 재실행된다.
    override func viewWillAppear(_ animated: Bool){
        super.viewWillAppear(animated)
        
        //이 조건문을 달지 않으면 처음 앱이 구동되었을 때 라벨에 아무것도 보이지 않는다.
        if name.count > 0{
        lblFirstView.text = name
        }
    }

    @IBAction func moveSecondView(_ sender: Any) {
        let storyboard = UIStoryboard.init(name: "Main", bundle: Bundle.main)
        
        let secondView = storyboard.instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController
        
        //하위 SecondViewController에 데이터 전달
        secondView.data = "시원한 아메리카노"
        
        self.present(secondView, animated: true)
    }
    
}

2. 코드 작성 없이 segue를 이용한 방법 - 화면이동만 가능(데이터 전달 못함)

 

* 이벤트를 처리할 수 있는 객체를 다른 ViewController에 드래그해서 전환할 수 있도록 iOS에서 추가한 화면전환 방법

ViewController에서 다른 ViewController로 segue를 이용해서 화면전환을 시키게 되면 ViewController의 prepare라는 메소드가 호출된다.

* 다른 ViewController에서 ViewController로 돌아올 때는 코드로 작성해도 되고 segue exit으로 연결해서 돌아올 수도 있다.

* 데이터를 넘기는 것만 아니라면 iOS에서는 segue와 segue exit를 이용해, 코드를 몇 줄 안 치고 화면 전환을 할 수도 있는 것.

* 단, 데이터를 하위에서 상위 View로 넘기는 것은 코드를 사용해야만 한다.

ViewController에서 다른 ViewController로 segue를 이용해서 이동하고자 할 땐, 이벤트를 받을 수 있는 객체를 선택하고 출력하고자 하는 ViewController로 드래그 하면 된다.

* 이벤트를 받을 수 있는 객체 : UIResponder로부터 상속받은 클래스 객체 : UIViewController, UIView, AppDelegate

 

 

Want It

1. 각 뷰에 버튼을 하나씩 더 배치해서 이전 버튼과 데이터 이동이 어떻게 다른지 확인할 수 있다.

 

 

각 뷰에 버튼 생성

 

 

 

 

 

 

 

 

 

 

 

1번 버튼을 클릭한 상태에서 control을 누리고 2번으로 드래그.

present modaly를 선택

 

 

 

 

 

 

 

 

반대로 돌아오기 위한 작업을 하려면 1번에 UIStoryboardSegue를 매개변수로 갖는 @IBAction 메소드가 있어야 한다.

ViewController.swift에 @IBAction func 메소드이름(segue:UIStoryboardSegue){} 생성

import UIKit

class ViewController: UIViewController {
    @IBOutlet weak var lblFirstView: UILabel!
    var name : String = ""
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    override func viewWillAppear(_ animated: Bool){
        super.viewWillAppear(animated)
        if name.count > 0{
        lblFirstView.text = name
        }
    }

    @IBAction func moveSecondView(_ sender: Any) {
        let storyboard = UIStoryboard.init(name: "Main", bundle: Bundle.main)
        let secondView = storyboard.instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController
        secondView.data = "시원한 아메리카노"
        self.present(secondView, animated: true)
    }
    //추가된 부분
    @IBAction func moveSegue(segue:UIStoryboardSegue){
        
    }
}

※ exit는 왼쪽 사진이 가르키는 쪽입니다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

2번의 버튼(이벤트를 받을 수 있는 객체)을 클릭한 상태에서 control을 누른 채, 상단의 exit로 드래그하고 만들어 놓은 메소드 선택

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

segue에 애니메이션 적용

 

 

 

 

 

 

 

 

애니메이션 종류 선택

 

 

 

 

 

Result

 

 

Want It

1. 하위 ViewController에서 새로 만든 버튼을 클릭해도 데이터가 전달된다.

2. 버튼을 교차해가며 비교해 본다.

 

segue로 이동할 때는 func prepare(for segue:UIStoryboardSegue, sender:Any?) 메소드가 호출되도록 되어 있다.

이 메소드는 정해져 있어서 외우는 것이 좋다.

segue의 destination 프로퍼티가 목적지 뷰 컨트롤러에 대한 참조이다. 단, 형변환을 사용해서 사용해야 한다.

이 메소드는 2번에서 1번으로 돌아올 때 호출되는데, 어쨌든간에 1번에 추가하는 코드라서 데이터를 2번에서 1번으로 가져올 수 없다.

그래서 다른 방법으로, 처음 했던 예제처럼 코드를 작성하거나 돌아가기 전에 공유 영역에 변수를 수정하는 형태로 데이터를 넘겨주어야 한다.

 

ViewController에 func prepare(for segue:UIStoryboardSegue, sender:Any?) 메소드 오버라이딩

import UIKit

class ViewController: UIViewController {
    @IBOutlet weak var lblFirstView: UILabel!
    var name : String = ""
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    override func viewWillAppear(_ animated: Bool){
        super.viewWillAppear(animated)
        if name.count > 0{
        lblFirstView.text = name
        }
    }

    @IBAction func moveSecondView(_ sender: Any) {
        let storyboard = UIStoryboard.init(name: "Main", bundle: Bundle.main)
        let secondView = storyboard.instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController
        secondView.data = "시원한 아메리카노"
        self.present(secondView, animated: true)
    }
    
    @IBAction func moveSegue(segue:UIStoryboardSegue){
    }
    
    //세그웨이를 이용해서 이동할 때 호출되는 메소드
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        //이동할 ViewController의 참조를 생성
        let second = segue.destination as! SecondViewController
        second.data = "졸립다."
    }
 
}

Result

 

 

Want It

2번에서 공유자원 사용해서 1번에 데이터 전달 해보기

-- AppDelegate를 사용했는데 2번에서 대입을 하고 1번으로 이동하면 초기값이 계속 뜨는 문제가 발생. 보완 필요.

 

--해결

let appDelegate = AppDelegate()

let appDelegate : AppDelegate?

let appDelegate : AppDelegate!

나는 바보인 것인가....1시간 동안 왜 이러고 앉아 있었지 ㅠㅠ

내 핸드폰의 AppDelegate를 가져오려면 위에처럼 선언하면 안 됨.

let appDelegate = UIApplication.shared.delegate as! AppDelegate

이게 바른 코드임...

이제야 잘 됨.

 

ViewController.swift

import UIKit

class ViewController: UIViewController {

//property//

    @IBOutlet weak var lblFirstView: UILabel!
    var name : String = ""

//view//

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func viewWillAppear(_ animated: Bool){
        super.viewWillAppear(animated)
        if name.count > 0{
        lblFirstView.text = name
        }
    }

//button click//

    @IBAction func moveSecondView(_ sender: Any) {
        let storyboard = UIStoryboard.init(name: "Main", bundle: Bundle.main)
        let secondView = storyboard.instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController
        secondView.data = "시원한 아메리카노"
        self.present(secondView, animated: true)
    }
    
    
//segue//

    @IBAction func moveSegue(segue:UIStoryboardSegue){
    	//돌아올 때 불러와지는 듯 - 더 알아봐야 할 것 같다.
        let appDelegate = UIApplication.shared.delegate as! AppDelegate
        name = appDelegate.shareData
    }
    
    //세그웨이를 이용해서 이동할 때 호출되는 메소드
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        //이동할 ViewController의 참조를 생성
        let second = segue.destination as! SecondViewController
        second.data = "졸립다."
    }
}

SecondViewController.swift

import UIKit

class SecondViewController: UIViewController {

//property//

    @IBOutlet weak var lblSecondView: UILabel!
    var data : String?

//view//

    override func viewDidLoad() {
        super.viewDidLoad()
        lblSecondView.text = data
        
        //공유 데이터 변경
        let appDelegate = UIApplication.shared.delegate as! AppDelegate
        appDelegate.shareData = "그럼 자야지"
        print(appDelegate)
    }

//button click//

    @IBAction func movePrev(_ sender: Any) {
        let parent = self.presentingViewController as! ViewController
        parent.name = "역시 원두는 케냐AA"
        parent.dismiss(animated: true)
        //self.presentingViewController?.dismiss(animated: true)
    }
}

Result

 

 

그리고 navigation을 이용한 화면 전환도 있는데 너무 늦어서... 다음에 해보는 것으루!

블로그의 정보

Beautiful Coding

사슴비행기

활동하기