inflearn logo
강의

강의

N
챌린지

챌린지

멘토링

멘토링

N
클립

클립

로드맵

로드맵

지식공유

앨런 iOS 앱 개발 (15개의 앱을 만들면서 근본원리부터 배우는 UIKit) - MVVM까지

화면 이동과 데이터 전달 (활용 앱31강)

스토리보드로 짠 ui의 클래스의 생성자를 만들 수 없나요?

637

문희찬

작성한 질문수 28

1

class SecondViewController: UIViewController {
    @IBOutlet weak var mainLabel: UILabel!
    var someString: String?
    
    override func viewDidLoad() {
        mainLabel.text = someString
        super.viewDidLoad()
    }
    
    init(someString: String? = nil) {
        self.someString = someString
        super.init(nibName: nil, bundle: nil)
    }
    required init?(coder: NSCoder) {
        fatalError("init?(coder) error")
    }

    @IBAction func backButtonTapped(_ sender: UIButton) {
        self.dismiss(animated: true)
    }
}

스토리보드로 만든 클래스의 생성자를 만들고

storyboard?.instantiateViewController(widthIdentifier: "secondVC")를 호출하면

init?(coder: NSCoder)가 호출되어 에러가 발생하네요.

위 코드처럼 생성자를 만드는 방법은 없을까요?

swift ios

답변 2

0

앨런(Allen)

추가적인 내용이 생각나서, 추가 댓글을 답니다.

스토리보드로 만들었을때 스토리보드 생성자를 만드는 방법인데, 전달하고자하는 데이터(저장속성)를 옵셔널로 선언을 안하고도 가능한 방법이 있긴합니다.
(많이 활용하는 방법은 아니라.. 그냥 참고적으로 이런 것도 있구나 정도 생각하시면 좋을 것 같아요.)

class SecondViewController: UIViewController {
    
    @IBOutlet weak var mainLabel: UILabel!
    
    var someString: String
    
    override func viewDidLoad() {
        super.viewDidLoad()
        mainLabel.text = someString
    }
    
    init(coder: NSCoder, dataString: String) {
        self.someString = dataString
        super.init(coder: coder)!
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    @IBAction func backButtonTapped(_ sender: UIButton) {
        self.dismiss(animated: true, completion: nil)
    }
}


생성자를 만드실때, 스토리보드로 만들면, NSCoder가 들어간 생성자가 호출이 됩니다. 그래서 반드시

init(coder: NSCoder, dataString: String)

제가 생성자 만든 것처럼, 전달하고자 하는 데이터를 포함해서 생성자를 만드시면 되고
다면, 호출하는쪽.. 전화면에서 이 생성자를 호출하는 부분도 다시 구현하셔야 합니다.

@IBAction func storyboardWithCodeButtonTapped(_ sender: UIButton) {
        
        let hello = "다음화면으로 전달할 데이터 문자열"
        
        guard let secondVC = storyboard?.instantiateViewController(identifier: "secondVC", creator: { coder in
            SecondViewController(coder: coder, dataString: hello)
        }) else {
            fatalError("Failed to initialize view controller")
        }

        self.present(secondVC, animated: true, completion: nil)
    }

위처럼

instantiateViewController(identifier:creator:)

라는 메서드를 사용해서 해결할 수 있는 방법을 제공하고 있네요.
공식문서(https://developer.apple.com/documentation/uikit/uistoryboard/3213989-instantiateviewcontroller) 참고.

이렇게 하시면.. 전달하고자 하는 데이터를 옵셔널로 선언하지 않아도 생성자를 통해 스토리보드로 만든 뷰컨트롤러 인스턴스를 생성하면서 데이터 전달이 가능합니다.

0

앨런(Allen)

스토리보드로 UI를 만드신 저장속성의 값을 할당하기 위해서 다음 생성자를 구현하시려고 하시면, 뷰컨트롤러의 필수 생성자 구현에서 fatalError를 호출하시면 당연히 안되죠. 스토리보드에서 내부적으로 required init?(coder: NSCoder)생성자를 호출하도록 되어 있기 때문에 그 내부에서 fatalError를 호출하시면... 앱이 꺼집니다.

(뷰컨트롤러의 필수생성자가 어떤 의미가 있는지는.. 글이 길어질 것 같아 아래의 링크 글을 참고 부탁드립니다.)
(참고: https://babbab2.tistory.com/171 )


물론 원하시면 아래처럼 만드실 수는 있습니다.

class SecondViewController: UIViewController {
    
    @IBOutlet weak var mainLabel: UILabel!
    
    var someString: String?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        mainLabel.text = someString   // ⭐️ super.viewDidLoad 후에 호출해야함
    }
    
    init(someString: String? = nil) {
        self.someString = someString   // ⭐️ SecondViewController의 저장속성을 셋팅하기에 super.init전에
        super.init(nibName: nil, bundle: nil)
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)    // ⭐️ 스토리보드 구현시 super.init 필요
    }

    @IBAction func backButtonTapped(_ sender: UIButton) {
        self.dismiss(animated: true, completion: nil)
    }
}

위에서 제가 별표 넣어놓은 곳 호출/할당 순서 잘 주의 해주셔야 합니다. 물론 위처럼 만들수는 있으나..

다만, 위처럼 생성자를 만들면.. 실질적인 효과가 아무것도 없습니다. 왜냐하면,

SecondViewController(someString: "안녕하세요")

이런식으로 생성자를 직접호출해서 "안녕하세요"라는 문자열 데이터를 전달해야 하는데..

그것이 아니라.. 스토리보드로 만들면, 반드시 아래처럼 호출을 하거나 해야하기 때문이죠.

storyboard?.instantiateViewController(widthIdentifier: "secondVC")


그래서 스토리보드로 만들면 저장속성을 일반적으로 옵셔널로 선언하는 것입니다. 저장속성(여기서는 someString 변수)을 옵셔널로 선언한다는 것 자체가.. 굳이 생성자를 만들 필요가 없다는 것입니다. 클래스에서 저장속성의 타입을 옵셔널 타입으로 선언해주면, nil로 초기화가 되기 때문에 생성자를 굳이 재정의 하지 않아도 괜찮습니다. (이건 생성자 관련 Swift문법 내용을 찾아보시는 면 도움이 되실 것 같고요.)
(물론 스토리보드로 만들어도 저장속성을 셋팅할 수 있는 여러가지 방법들이 있긴 합니다만.. 자주 사용하지는 않습니다.)

일반적으로 코드로 UI를 작성할때는.. 1) 저장속성 타입을 옵셔널로 선언하지 않고, 2) 생성자를 만들어서

SecondViewController(someString: "안녕하세요")

이렇게 초기화를 해줄 수도 있으나.. 그럼에도 생성자를 만드는 것 자체가 귀찮고 복잡한 코드가 되기 때문에 저장 속성을 옵셔널로 선언해주는 것이 더 편한 코드라고 생각하기는 합니다.

 

감사합니다. :)

0

문희찬

주말 아침에 감사드립니다. 생성자가 있으면 코드를 관리하기가 쉽다고 생각해서 한번 만들어봤어요. 친절하게 설명해주셔서 더 많이 배워가네요. 감사합니다!!

0

앨런(Allen)

열심히 하고 계시군요!!ㅎㅎ 화이팅 :)

수강연장부탁드려도될까요..

0

45

2

Drawing Cycle 관련 질문

0

74

1

앱 제작 시 주로 코드로 작성하시는 이유가 있을까요?

0

99

2

델리게이트 패턴 관련 질문

0

71

2

수강 기간 연장 부탁드릴 수 있을까요?

0

69

1

활용 앱 57강 질문입니다.

0

57

1

활용 앱 30강 에러 질문입니다

0

67

2

기초앱 17강 Contraints 설정 질문입니다.

0

69

2

기초 앱 4강 Type Any -> UIButton

0

70

2

34강 유용한 앱 추천에 나오는 UIKitViewer에 대해서 질문이 있습니다

0

82

1

깃에 코드는 올려도 된다고 하셨는데

0

113

1

수강기간 연장신청 드려도 될까요?

0

86

1

수강 기간 연장 부탁드려도 될까요?

0

122

2

수강 기간 연장 신청 요청드립니다.

0

133

2

playground에서 상단 실행?아이콘 회전 관련하여...

0

140

1

활용앱53강에서 질문있습니다.

0

172

2

Project 명칭 변경방법이 궁금합니다.

0

144

1

override 에 대해서 궁금한 것이 있습니다.

0

127

2

섹션8] 코드 리팩토링 문의

0

104

1

xcode 에서 ios 버전 선택 및 시뮬레이터 관련 질문

0

576

2

수강 기간 연장 부탁드려도 될까요?

0

134

2

뷰모델의 상태변화 와 didSet

0

100

1

@Sendable 키워드

0

98

1

클로저

0

110

2