구성
Swift에서 화면을 그리는 방식에는 여러가지로 크게 Storyboard, 코드 작성 방식, Swift UI가 있다.
가장 기본이 되는 것은 Storyboard이며 이를 기준으로 배워보고자 한다.
Storyboard는 하나의 파일로 여러 개의 View를 가질 수 있으며 1개의 View는 1개의 Controller를 가질 수 있다.
Xcode의 View 속성에서 Controller를 설정할 수 있다.
(Storyboard에 여러 개의 View가 포함되어 있으면 협업시 충돌이 발생하기에 나중에는 Storyboard Reference라는 것을 사용한다)
Controller는 View에서 일어나는 모든 interaction들을 관리하는 주체로 생명 주기를 비롯해 포함된 요소들의 onClick과 같은 기본적인 화면과 관련된 행위들을 처리해주는 부분이다. 일반적으로 하나의 View당 하나를 할당하기때문에 중복되는 코드가 발생하는 경우는 상속, 프로토콜 등 여러가지 기술들을 사용해 최적화 한다. Storyboard에 생성한 컴포넌트에서 드래그 앤 드롭으로 Controller의 코드내에 연결시키면 간단하게 컴포넌트에 대한 액션들을 생성하고 매치 시킬 수 있다.
첫 번째 사진에서 사용된 Navigation Controller를 비롯해 Controller는 여러가지가 기본으로 제공된다.
화면 전환이 가능한 AppBar를 사용하고 싶다면 Navigation Controller를 root로 배치해주면 이후 View들에서 간단하게 적용할 수 있고,
BottomTabBar와 View도 제공되는 것들로 쉽게 구현할 수 있다. Controller를 배치하면 View가 함께 생성된다. 단독으로 존재할 수는 없다.
이 두가지를 기본으로 패턴 채택에 따라 Model이나 ViewModel 등을 추가해 비즈니스 로직을 생성하고 앱을 만들어 나갈 수 있다.
상태 변화
Swift에는 선언형 프레임워크들과는 다르게 state가 별도로 존재하지 않으며,
useState나 setState와 같은 것 없이 ViewController 내부에 변수로 선언한 컴포넌트를 곧바로 참조해서 속성을 변경한다.
다른 View의 컴포넌트를 갱신하고 싶을 때에는 다른 ViewController를 참조해 속성을 변경해준다.
아래는 화면 전환과 동시에 다른 ViewController에게 데이터를 전달하는 간단한 예제이다.
React나 Flutter의 계층 형과는 확연하게 다른 구조를 가지고 있으며 기본적인 화면간 데이터 전송, 화면 갱신을 Controller에 의존하고 있다.
//데이터를 변경(전송)하는 Controller
class EditViewController: UIViewController {
@IBOutlet weak var textField: UITextField!
//Segue를 사용해 화면 전환시 동작하는 함수
//segue의 동작 시 다음 View를 위해 준비할 수 있다.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
//전환할 ViewController를 선언한다.
//guard는 조건에 실패하면 else를 반환합니다. as?로 캐스팅 했을 때의 예외 처리
//let = const
guard let nextViewController = segue.destination as? ViewController else {return}
//전환할 ViewController의 title을 변경한다.
nextViewController.title = "Segue로 이동 성공"
}
//수정 버튼과 연결된 액션 함수
@IBAction func onClickPopBtn(_ sender: Any) {
//Navigation Controller로 이동하는 경우
//NavigationController에 쌓인 이전 화면의 Controller 선언
guard let vc = self.navigationController?.viewControllers[0] as? ViewController else{return}
//Title 및 해당 Controller의 변수를 textField 값으로 변경
vc.title = "수정됨"
vc.receivedMessage = textField.text!
//Navigation pop
self.navigationController?.popViewController(animated: true)
}
}
//=======================================================================
//데이터가 변경(전달받은)된 Controller
class ViewController: UIViewController {
var receivedMessage : String?
@IBOutlet weak var messageLabel: UILabel!
override func viewWillAppear(_ animated: Bool) {
//다른 View가 변경한 receiveMessage로 화면 갱신
messageLabel.text = receivedMessage
super.viewWillAppear(animated)
}
}
위는 Segue로 아래는 NavigationController를 사용했고 이들은 화면을 전환하는 기법 중 하나이다.
데이터를 넘겨주기 위해 다른 View의 Controller를 참조하는 방식이 이렇다 라는 것만 짚고 넘어가면 되겠다.
화면 이동 (Segue)
Swift에서는 화면 이동은기본적으로 Segue(Segueway)를 통해 할 수 있다. 방법은 매우 간단하다. 버튼과 같은 컴포넌트에Control를 누른채로 크릭 드래그해서 다른 View와 연결하면 된다. 그럼 아래와 같이 View간에 Segue 라인이 연결되고 어떤 컴포넌트에 해당하는 액션인지 알 수 있다. NavigationController나 TabViewController 등의 제공되는 Controller들을 사용해서 화면을 전환할 수도 있다.
Controller의 생명 주기
여느 프론트엔드 프레임워크와 같이 Swift에도 생명주기가 있다.
모든 단계들은 UIViewController에서 함수로 구현되어 있어 아래와 같이 override하여 사용한다.
class ViewController: UIViewController {
//Optional type, nil(null)을 가질 수 있음
var receivedMessage : String?
//Storyboard의 객체 선언
//변수의 메모리 할당에는 strong, weak, unowned의 타입이 존재한다.
//아래는 weak 타입
@IBOutlet weak var messageLabel: UILabel!
//===================lifecycle functions====================
//outlet(컴포넌트)들과 action이 생성 및 연결되는 부분
//일반적인 경우라면 개발자의 영역에서 사용하지 않음
override func loadView() {
super.loadView()
}
//해당 Controller가 생성될 때, 메모리 부족이 아니라면 1번만 실행 됨.
//컴포넌트들의 속성을 초기화할 때, 이 곳에 넣음.
override func viewDidLoad() {
super.viewDidLoad()
}
//View가 모두 load된 후 렌더링 되기 바로 직전
//이전 View로부터 받아온 것들 포함
override func viewWillAppear(_ animated: Bool) {
//이전 View가 변경한 receiveMessage를 컴포넌트에 적용
messageLabel.text = receivedMessage
super.viewWillAppear(animated)
}
//View가 렌더링 된 이후 실행
//애니메이션이나 API를 호출하는 영역
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
}
//View가 remove되기 직전, 직후
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
}
}
정리
- 각각의 View에는 1개의 Controller가 연결되고, View 및 컴포넌트들의 모든 액션들은 Controller에서 처리된다.
- 컴포넌트들은 Controller 내부에 변수형으로 선언될 수 있고, 이를 참조해 컴포넌트의 속성을 바꿀 수 있다.
- 별도의 state가 존재하지 않는다.
- 다른 View의 내용을 참조하거나 화면을 갱신하고자 할 때 해당 View의 Controller를 참조한다.
'Programming' 카테고리의 다른 글
[Git] Git 파해치기 (엘리스SW트랙 1주차) (0) | 2022.04.16 |
---|