Swift 12주차 - 내비게이션 컨트롤러 이용해 화면 전환하기

2024. 5. 25. 15:05swift

Swift 12주차 - 내비게이션 컨트롤러 이용해 화면 전환하기

 

이번 주차에서는 네비게이션 컨트롤러를 사용해 화면을 전환해 보고 화면 전환과 동시에 데이터도 전달 되도록 구현해 볼 예정이다.

 

 

목차

     

     

    네비게이션 컨트롤러 앱을 위한 기본 환경 구성하기

     

    먼저 스토리보드에서 네비게이션 컨트롤러를 추가해보자.

     

    IPhone 모양의 뷰 컨트롤러를 클릭한 후 메뉴에서 Editor-> Embed in-> Navigation Controller 를 선택하자.

     

     

     

    그러면 처음에는 없었던 네비게이션 컨트롤러가 추가된 모습을 볼 수 있다.

     

     

     

    뷰 컨트롤러에 제목을 추가해보자.

     

    메인 뷰 컨트롤러의 상단부분을 클릭한 후 오른쪽 인스펙터 영역에서 Attributes inspector 버튼을 클릭하자.

     

     

     

    아래 Title에 '메인화면' 이라고 입력하면 아래처럼 제목이 표시되게 된다.

     

     

     

     

    뷰 추가하고 바 버튼으로 뷰 전환하기

     

    + 버튼을 클릭해 뷰 컨트롤러(View Controller)를 찾아서 메인화면 오른쪽의 빈 공간에 추가하자.

     

     

     

    그다음 바 버튼 아이템(Bar Button Item)을 메인화면의 내비게이션 바 오른쪽에 추가하자.

     

     

     

    오른쪽 인스펙터 영역에서 시스템 아이템을 Edit로 변경하자.

     

    그러면 아래 사진처럼 버튼 글자가 Edit로 변경될 것이다.

     

     

     

    이 Edit 바 버튼을 마우스 오른쪽 버튼으로 클릭한 채 오른쪽의 뷰 컨트롤러에 갖다 놓으면 아래와 같은 검은 창이 나타나게 되는데, 여기서 Action Segue을 Show로 선택하자.

     

    그렇게 하면 메인화면에서 서브 화면으로 갔다가 돌아오는 형태를 취하게 된다.

     

     

     

     

    세그웨이가 생성된 모습을 볼 수 있다. 세그웨이가 생성되면 코딩을 하지 않아도 화면전환을 할 수 있다.

     

     

    서브 화면의 Title을 '수정화면'으로 바꾸자.

     

     

     

    시뮬레이터를 실행하여 Edit 버튼을 클릭하면 수정화면으로 이동하고, 메인화면 버튼을 클릭하면 다시 메인화면으로 돌아오는 것을 확인할 수 있다.

     

     

     

    버튼을 활용해 뷰 전환하기

     

    먼저 아래 사진처럼 수정화면에 레이블과 완료 버튼을 추가하자.

     

    그 다음 메인화면에서 수정 버튼을 추가하자.

     

     

     

    스토리보드 중앙에 있는 세그웨이를 선택하고 Attributes inspector 버튼을 클릭한 후 Identifier를 editBarButton으로 수정하자.

     

     

     

    아까와 같이 수정 버튼을 마우스 오른쪽 클릭한 채로 수정화면의 뷰 컨트롤러로 갖다 놓고 Action Segue을 Show로 선택하자.

     

     

     

     

    그러면 아래처럼 세그웨이가 2개가 된 모습을 볼 수 있다.

     

    아래 세그웨이를 선택한 후 Identifier를 editButton으로 수정하자.

     

     

     

     

    뷰 전환 구분하기

     

    지금까지는 뷰 전환만 이루어질 뿐 아무런 동작도 수행하지 않는다.

     

    이제 뷰가 전환될 때 바 버튼을 눌러 전환하였는지 일반 버튼을 눌러 전환하였는지 구분하고 이를 화면에 나타내 보자.

     

     

    수정화면의 뷰 컨트롤러는 만들었지만 뷰 컨트롤러의 클래스 파일이 없으므로 추가해주자.

     

    상단 메뉴에서 File -> New -> File... 을 선택하자.

     

     

     

     

    iOS 항목의 Source 탭에서 Cocoa Touch Class 를 선택하고 Next 버튼을 클릭하자.

     

     

     

    Class는 'EditViewController'로 수정하고 Subclass of는 UIViewController로 수정한 후 Next 버튼을 클릭하여 지정 폴더에 저장하자.

     

     

     

    그러면 EditViewController.swift 파일이 추가된 것을 확인할 수 있다.

     

     

     

    수정화면의 뷰 컨트롤러를 선택한 후 Class에서 EditViewController를 선택하자.

     

    이렇게 해서 스토리보드의 뷰 컨트롤러와 EditViewController.swift 파일이 제대로 연결되었다.

     

     

     

     

    이제 수정화면의 아웃렛 변수를 추가해보자.

     

    레이블을 연결한 후 Name은 lblWay로 설정하자.

     

     

     

    이번엔 완료 버튼의 액션 함수를 추가하자.

     

    Name은 btnDone으로 설정하고 Type은 UIButton으로 설정하자.

     

     

     

     

    ViewController.swift 파일로 넘어오자.

     

    세그웨이를 이용하여 화면을 전환하기 위해 prepare 함수를 사용하자.

     

    prepare만 쳐도 자동 완성 기능을 이용하여 쉽게 함수를 추가할 수 있다.

     

     

     

    세그웨이의 도착 컨트롤러를 EditViewController 형태를 가지는 segue.destination ViewController로 선언하자.

     

    그리고 세그웨이의 아이디에 따라 다르게 동작하도록 if문을 추가하자.

     

     

     

     

    이제 뷰 컨트롤러에서 각 세그웨이 아이디별로 다른 문자열을 받아서 표시하기 위해 EditViewController.swift 파일을 수정해보자.

     

    레이블의 텍스트를 직접 제어할 수 없기 때문에 문자열 변수 textWayValue를 만들자.

     

    그리고 viewDidLoad 함수에서 변수 textWayValue 값을 레이블의 텍스트로 대입하여 레이블에 출력되게 하자.

     

     

     

    다시 ViewController.swift 파일로 돌아와서 prepare 함수를 마저 작성하자.

     

    세그웨이 아이디가 editButton 일 경우 'segue: use button' 문자열을 전송하고, 세그웨이 아이디가 editBarButton일 경우 'segue: use bar button' 문자열을 전송하도록 한다.

     

     

     

     

    다시 EditViewController.swift 파일을 선택하여 수정화면의 완료 버튼을 클릭하면 메인화면으로 이동하도록 코드를 추가하자.

     

    뷰를 전환하기 위해 세그웨이를 추가할 때 Action Segue를 Show 형태로 했기 때문에 돌아갈 때는 pop의 형태로 해야 한다.

     

     

     

    시뮬레이터를 실행하여 Edit 버튼과 수정 버튼을 클릭해보면 각각 수정화면으로 이동함과 동시에 'segue: use bar button', 'segue: use button' 이 표시되는 것을 볼 수있다.

     

    또한 수정화면에서 완료 버튼을 클릭해도 메인화면으로 이동한다.

     

     

     

     

    뷰 전환과 함께 메세지 전달하기

     

    이번에는 수정화면에서 메세지를 입력하고 완료 버튼을 클릭하면 메인화면에서 그 메세지가 전달되도록 구현해보자.

     

    반대로 메인화면에서 수정화면으로 메세지를 전달하는 작업도 진행해보자.

     

     

    메인화면에 레이블을 추가한 후 Message라고 수정하고, 텍스트 필드를 추가하자.

     

    그리고 해당 레이블과 텍스트 필드롤 복사하여 수정화면에 붙여넣어주자.

     

     

     

    메인화면의 텍스트 필드를 아웃렛 변수로 추가해주자.

     

    이름은 txMessage로 설정하자.

     

     

     

    같은 방법으로 수정화면에서 텍스트 필드를 아웃렛 변수로 추가하자.

     

     

     

     

    메세지를 전달하기 위해 코드를 추가하자.

     

     

     

    import문과 클래스 문 사이에 프로토콜 형태의 델리게이트를 추가하자.

     

     

     

     

    ViewController.swift 파일을 열고 프로토콜로 작성한 EditDelegate를 뷰 컨트롤러 클래스 선언문 안에 넣어 상속받자.

     

     

    프로토콜을 위임받으면 프로토콜에서 정의한 함수를 무조건 만들어야 한다. 함수를 만들지 않으면 위처럼 에러가 발생한다.

     

     

    에러 알림을 클릭한 후 Fix 버튼을 클릭하면 자동으로 함수를 생성시켜준다.

     

     

     

    함수를 클래스 하단부로 옮기고 함수 안에 코드를 아래와 같이 추가하자.

     

     

     

     

    다시 EditViewController.swift 파일로 가서 delegate 변수를 생성하자.

     

     

     

     

    이번엔 btnDone 함수를 수정해보자.

     

    수정화면의 btnDone 함수에서 didMessageEidtDone을 호출하면서 수정화면의 텍스트 필드 내용을 메세지 문자열로 전달한다.

     

    다시말해 수정화면의 텍스트 필드의 내용을 메인화면으로 전달한다.

     

     

     

    마지막으로 ViewController.swift 파일로 들어가서 prepare 함수의 마지막 부분에 다음 코드를 추가하자.

     

     

     

     

    시뮬레이터를 실행한 후 수정화면에서 메세지 창에 '수정완료'를 입력한 후 완료 버튼을 클릭하면 메인화면에 동일한 내용이 나타나는 것을 확인할 수 있다.

     

     

    이제 메인화면에서 메세지를 입력한 후 수정 버튼을 클릭하면 메인화면의 메세지가 수정화면의 메세지로 이동하도록 코드를 추가하자.

     

    ViewController.swift 파일에서 prepare 함수에 아래처럼 코드를 추가하자.

     

     

     

    다시 시뮬레이션을 실행시켜 메인화면에서 '수정해주세요' 라고 메세지를 입력하면 수정화면의 메세지 창에 '수정해주세요' 가 나타나는 것을 확인할 수 있다.

     

     

     

     

    수정화면에서 메인화면의 전구 제어하기

     

    메인화면에 이미지 뷰(Image View)를 추가하고, 수정화면에는 '켜기' 레이블과 스위치(Switch)를 추가하자.

     

     

     

     

    메인화면의 이미지 뷰를 아웃렛 변수로 추가하자.

     

    이름은 imgView로 설정하자.

     

     

     

    같은 방법으로 수정화면의 스위치를 아웃벳 변수로 추가하자.

     

    이름은 swisOn으로 설정하자.

     

     

     

     

    전구 이미지인 'lamp_on.png' 파일과 'lamp_off.png' 파일을 왼쪽 내비게이터 영역에 추가하자.

     

     

     

    전구 초기 화면을 세팅하기 위해 변수를 초기화하자.

     

     

     

     

    다시 EditViewController.swift 파일을 열고 아래처럼 isOn 변수를 만들고 viewDidLoad 함수에서 스위치값에 출력되게 하자.

     

     

     

     

    메인화면 전구의 온/오프 상태를 수정화면에서 그대로 인식해 표시되도록 수정하자.

     

    ViewController.swift 파일을 열어 아래처럼 코드를 추가하자.

     

     

    수정화면의 isOn에 메인화면의 상태를 전달한다.

     

     

    시뮬레이터를 실행하여 수정 버튼을 클릭해 수정화면으로 이동해 보면 스위치가 켜져있는 것을 확인할 수 있다.

     

     

     

    이제 스위치에 대한 액션 함수를 추가해보자.

     

    이름은 swImageOnOff로 수정하고 타입은 UISwitch로 수정하자.

     

     

    이제 전구를 제어하기 위한 코딩 작업을 해보자.

     

    방금 추가한 swImageOnOff 함수를 다음과 같이 수정하자.

     

    스위치 상태를 확인하여 isOn 변수에 true 또는 false를 설정한다.

     

     

     

    수정화면의 스위치 상태를 메인화면으로 보내기 위해 델리게이트에 didImageOnOffDone 함수를 추가하자.

     

     

     

    ViewController.swift 파일을 선택해보면 이번에도 프로토콜에서 정의한 함수를 만들지 않았기 때문에 에러가 발생했다.

     

    에러 알림을 클릭한 후 Fix를 클릭하여 함수를 추가한 후 클래스의 아래쪽으로 이동시키자.

     

     

     

    스위치 값에 따라 전구를 켜고 끄기 위하여 if문과 else문을 사용하여 함수를 수정하자.

     

     

     

     

    EditViewController.swift 파일에서 btnDone 함수를 수정하자.

     

    수정화면의 btnDone 함수에서 didImageOnOffDone을 호출하면서 수정화면의 스위치 상태를 isOn으로 전달한다.

     

    즉, 수정화면의 스위치 상태를 메인화면으로 전달한다.

     

     

     

     

    시뮬레이터를 실행시켜 스위치가 정상적으로 작동하는 것을 확인할 수 있다.

     

     

     

     

    수정화면에서 전구 확대/축소 기능 추가하기

     

    먼저 수정화면에 확대/ 축소 기능을 위한 버튼을 추가하자.

     

     

     

    해당 버튼의 아웃렛 변수를 추가하자.

     

    이름은 btnResize로 설정하자.

     

     

     

     

    다음으로 액션 함수를 추가하자.

     

    이름은 btnResizeImag으로 설정하고 타입은 UIButton으로 설정하자.

     

     

     

     

    이미지 확대/축소를 위해 ViewController.swift 파일에 변수를 추가하자.

     

    여기서 isZoom은 수정화면에서 사용자가 설정한 확대 상태를 나타내고, orgZoom은 현재 이미지의 확대 상태를 나타낸다.

     

     

     

    EditViewController.swift 파일도 와서 아래처럼 코드를 추가하자.

     

     

     

    ViewController.swift 파일로 이동해 메인화면의 이미지 상태를 수정화면에 전달하기 위한 코드를 추가하자.

     

     

     

    그리고 다시 EditViewController.swift 파일로 이동해 btnResizeImage 함수에 아래처럼 코드를 추가하자.

     

    isZoom이 true라면 isZoom을 false로 바꾸고 타이틀을 축소로 바꾸고, isZoom이 false라면 isZoom을 true로 바꾸고 타이틀을 확대로 바꾼다.

     

     

     

    수정화면의 버튼 상태를 메인화면으로 보내기 위해 델리게이트에 didImageZoomDone 함수를 추가하자.

     

     

     

     

    ViewController.swift 파일로 이동하여 didImageZoomDone 함수에 아래와 같이 코드를 추가하자.

     

    사용자가 확대를 누른 경우 이미 확대 상태인지 확인하고 확대 상태가 아니라면 확대하고 orgZoom을 true로 설정하고,

    사용하가 축소를 누른 경우 이미 축소 상태인지 확인하고 축소 상태가 아니라면 축소하고 orgZoom을 false로 설정한다.

     

     

     

     

    마지막으로 EditViewController.swift 파일로 이동해 수정화면의 버튼 상태를 메인화면으로 전달하기 위한 코드를 추가하자.

     

     

     

     

    이제 시뮬레이터를 실행해보면 모든 기능들이 정상적으로 실행되는 것을 확인할 수 있다.

     

     

     

     

     

     

    내비게이션 컨트롤러 앱 전체 소스 코드

     

    ViewController.swift

     

    import UIKit
    
    class ViewController: UIViewController, EditDelegate {
        
        let imgOn = UIImage(named: "lamp_on.png")
        let imgOff = UIImage(named: "lamp_off.png")
        
        var isOn = true
        var isZoom = false
        var orgZoom = false
    
        @IBOutlet var txMessage: UITextField!
        @IBOutlet var imgView: UIImageView!
        
        override func viewDidLoad() {
            super.viewDidLoad()
            // Do any additional setup after loading the view.
            imgView.image = imgOn
        }
        
        override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
            let editViewController = segue.destination as! EditViewController
            if segue.identifier == "editButton" {
                editViewController.textWayValue = "segue: use button"
            } else if segue.identifier == "editBarButton" {
                editViewController.textWayValue = "segue: use bar button"
            }
            
            editViewController.textMessage = txMessage.text!    
            editViewController.isOn = isOn
            editViewController.isZoom = orgZoom
            editViewController.delegate = self
        }
    
        func didMessageEditDone(_ controller: EditViewController, message: String) {
            txMessage.text = message
        }
        
        func didImageOnOffDone(_ controller: EditViewController, isOn: Bool) {
            if isOn {
                imgView.image = imgOn
                self.isOn = true
            } else {
                imgView.image = imgOff
                self.isOn = false
            }
        }
        
        func didImageZoomDone(_ controller: EditViewController, isZoom: Bool) {
            let scale: CGFloat = 2.0
            var newWidth: CGFloat, newHeight: CGFloat
    
            if isZoom {
                if !orgZoom {
                    newWidth = imgView.frame.width * scale
                    newHeight = imgView.frame.height * scale
                    imgView.frame.size = CGSize(width: newWidth, height: newHeight)
                    orgZoom = true
                }
            } else {
                if orgZoom {
                    newWidth = imgView.frame.width / scale
                    newHeight = imgView.frame.height / scale
                    imgView.frame.size = CGSize(width: newWidth, height: newHeight)
                    orgZoom = false
                }
            }
        }
    }

     

     

     

    EditViewController.swift

     

    import UIKit
    
    protocol EditDelegate {
        func didMessageEditDone(_ controller: EditViewController, message: String)
        func didImageOnOffDone(_ controller: EditViewController, isOn: Bool)
        func didImageZoomDone(_ controller: EditViewController, isZoom: Bool)
    }
    
    class EditViewController: UIViewController {
        
        var textWayValue: String = ""
        var textMessage: String = ""
        var delegate: EditDelegate?
        var isOn = false
        var isZoom = false
        
        @IBOutlet var lblWay: UILabel!
        @IBOutlet var txMessage: UITextField!
        @IBOutlet var swisOn: UISwitch!
        @IBOutlet var btnResize: UIButton!
        
        override func viewDidLoad() {
            super.viewDidLoad()
    
            // Do any additional setup after loading the view.
            lblWay.text = textWayValue
            txMessage.text = textMessage
            swisOn.isOn = isOn
            if isZoom {
                btnResize.setTitle("확대", for: UIControl.State())
            } else {
                btnResize.setTitle("축소", for: UIControl.State())
            }
        }
        
        @IBAction func btnDone(_ sender: UIButton) {
            if delegate != nil {
                delegate?.didMessageEditDone(self, message: txMessage.text!)
                delegate?.didImageOnOffDone(self, isOn: isOn)
                delegate?.didImageZoomDone(self, isZoom: isZoom)
            }
            _ = navigationController?.popViewController(animated: true)
        }
        
        @IBAction func swImageOnOff(_ sender: UISwitch) {
            if sender.isOn {
                isOn = true
            } else {
                isOn = false
            }
        }
        
        @IBAction func btnResizeImage(_ sender: UIButton) {
            if isZoom {
                isZoom = false
                btnResize.setTitle("축소", for: UIControl.State())
            } else {
                isZoom = true
                btnResize.setTitle("확대", for: UIControl.State())
            }
        }   
    }