스토리 보드에 인터페이스 설정이있는 Swift의 UIViewController에 대한 사용자 정의 초기화
UIViewController의 하위 클래스에 대한 사용자 지정 초기화를 작성하는 데 문제가 있습니다. 기본적으로 속성을 직접 설정하는 대신 viewController의 init 메서드를 통해 종속성을 전달하고 싶습니다. viewControllerB.property = value
그래서 내 viewController에 대한 사용자 정의 초기화를 만들고 슈퍼 지정 초기화를 호출했습니다.
init(meme: Meme?) {
self.meme = meme
super.init(nibName: nil, bundle: nil)
}
뷰 컨트롤러 인터페이스는 스토리 보드에 있으며 사용자 지정 클래스의 인터페이스도 뷰 컨트롤러로 만들었습니다. 그리고 Swift는이 메소드 내에서 아무것도하지 않더라도이 init 메소드를 호출해야합니다. 그렇지 않으면 컴파일러가 불평합니다 ...
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
문제는 MyViewController(meme: meme)
내 viewController에서 속성을 초기화하지 않는 사용자 지정 초기화를 호출하려고 할 때입니다 .
디버깅을 시도하고 있었는데, viewController에서 발견하고 init(coder aDecoder: NSCoder)
먼저 호출 한 다음 나중에 사용자 지정 초기화를 호출했습니다. 그러나이 두 init 메서드는 다른 self
메모리 주소를 반환 합니다.
내 viewController의 init에 문제가 있다고 의심되며 항상 구현이없는으로 반환 self
됩니다 init?(coder aDecoder: NSCoder)
.
누구든지 viewController에 대한 사용자 정의 초기화를 올바르게 만드는 방법을 알고 있습니까? 참고 : 내 viewController의 인터페이스는 스토리 보드에 설정되어 있습니다.
다음은 내 viewController 코드입니다.
class MemeDetailVC : UIViewController {
var meme : Meme!
@IBOutlet weak var editedImage: UIImageView!
// TODO: incorrect init
init(meme: Meme?) {
self.meme = meme
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func viewDidLoad() {
/// setup nav title
title = "Detail Meme"
super.viewDidLoad()
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
editedImage = UIImageView(image: meme.editedImage)
}
}
위의 답변 중 하나에서 지정 했으므로 사용자 정의 초기화 방법과 스토리 보드를 모두 사용할 수 없습니다. 그러나 여전히 정적 메서드를 사용하여 스토리 보드에서 ViewController를 인스턴스화하고 추가 설정을 수행 할 수 있습니다. 다음과 같이 표시됩니다.
class MemeDetailVC : UIViewController {
var meme : Meme!
static func makeMemeDetailVC(meme: Meme) -> MemeDetailVC {
let newViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("IdentifierOfYouViewController") as! MemeDetailVC
newViewController.meme = meme
return newViewController
}
}
스토리 보드에서 뷰 컨트롤러 식별자로 IdentifierOfYouViewController를 지정하는 것을 잊지 마십시오. 위 코드에서 스토리 보드의 이름을 변경해야 할 수도 있습니다.
스토리 보드에서 초기화 할 때 사용자 지정 이니셜 라이저를 사용할 수 없습니다.를 사용 init?(coder aDecoder: NSCoder)
하여 컨트롤러를 초기화하기 위해 스토리 보드를 디자인했습니다. 그러나 데이터를 UIViewController
.
뷰 컨트롤러의 이름이 포함 detail
되어 있으므로 다른 컨트롤러에서 가져 왔다고 가정합니다. 이 경우 prepareForSegue
메서드를 사용 하여 세부 정보로 데이터를 보낼 수 있습니다 (Swift 3입니다).
override func prepare(for segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "identifier" {
if let controller = segue.destinationViewController as? MemeDetailVC {
controller.meme = "Meme"
}
}
}
테스트 목적 String
대신 유형의 속성을 사용했습니다 Meme
. 또한 올바른 segue 식별자를 전달했는지 확인하십시오 ( "identifier"
자리 표시 자일 뿐임 ).
이 작업을 수행 한 한 가지 방법은 편의 이니셜 라이저를 사용하는 것입니다.
class MemeDetailVC : UIViewController {
convenience init(meme: Meme) {
self.init()
self.meme = meme
}
}
그런 다음 MemeDetailVC를 다음과 같이 초기화합니다. let memeDetailVC = MemeDetailVC(theMeme)
이니셜 라이저에 대한 Apple의 문서 는 꽤 좋지만 개인적으로 가장 좋아하는 것은 Ray Wenderlich : Initialization in Depth 튜토리얼 시리즈입니다. 다양한 init 옵션과 작업을 수행하는 "적절한"방법에 대한 많은 설명 / 예제를 제공해야합니다.
편집 : 사용자 정의 뷰 컨트롤러에서 편의 이니셜 라이저를 사용할 수 있지만 스토리 보드 또는 스토리 보드 segue를 통해 초기화 할 때 사용자 정의 이니셜 라이저를 사용할 수 없다고 말하는 모든 사람이 맞습니다.
If your interface is set up in the storyboard and you're creating the controller completely programmatically, then a convenience initializer is probably the easiest way to do what you're trying to do since you don't have to deal with the required init with the NSCoder (which I still don't really understand).
If you're getting your view controller via the storyboard though, then you will need to follow @Caleb Kleveter's answer and cast the view controller into your desired subclass then set the property manually.
As @Caleb Kleveter has pointed out, we can't use a custom initializer while initialising from a Storyboard.
But, we can solve the problem by using factory/class method which instantiate view controller object from Storyboard and return view controller object. I think this is a pretty cool way.
Note: This is not an exact answer to question rather a workaround to solve the problem.
Make class method, in MemeDetailVC class, as follows:
// Considering your view controller resides in Main.storyboard and it's identifier is set to "MemeDetailVC"
class func `init`(meme: Meme) -> MemeDetailVC {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "MemeDetailVC") as? MemeDetailVC
vc.meme = meme
return vc
}
Usage:
let memeDetailVC = MemeDetailVC.init(meme: Meme())
There were originally a couple of answers, which were cow voted and deleted even though they were basically correct. The answer is, you can't.
When working from a storyboard definition your view controller instances are all archived. So, to init them it's required that init?(coder...
be used. The coder
is where all the settings / view information comes from.
So, in this case, it's not possible to also call some other init function with a custom parameter. It should either be set as a property when preparing the segue, or you could ditch segues and load the instances directly from the storyboard and configure them (basically a factory pattern using a storyboard).
In all cases you use the SDK required init function and pass additional parameters afterwards.
UIViewController
class conform to NSCoding
protocol which is defined as:
public protocol NSCoding {
public func encode(with aCoder: NSCoder)
public init?(coder aDecoder: NSCoder) // NS_DESIGNATED_INITIALIZER
}
So UIViewController
has two designated initializer init?(coder aDecoder: NSCoder)
and init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?)
.
Storyborad calls init?(coder aDecoder: NSCoder)
directly to init UIViewController
and UIView
,There is no room for you to pass parameters.
One cumbersome workaround is to use an temporary cache:
class TempCache{
static let sharedInstance = TempCache()
var meme: Meme?
}
TempCache.sharedInstance.meme = meme // call this before init your ViewController
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder);
self.meme = TempCache.sharedInstance.meme
}
Swift 5
You can write custom initializer for only Optional
type properties.
class MyFooClass: UIViewController {
var foo: Foo?
init(with foo: Foo) {
self.foo = foo
super.init(nibName: nil, bundle: nil)
}
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.foo = nil
}
}
// View controller is in Main.storyboard and it has identifier set
Class B
class func customInit(carType:String) -> BViewController
{
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let objClassB = storyboard.instantiateViewController(withIdentifier: "BViewController") as? BViewController
print(carType)
return objClassB!
}
Class A
let objB = customInit(carType:"Any String")
navigationController?.pushViewController(objB,animated: true)
Swift 5 - Stored Property Initialization in UIViewController Subclass
class SomeClass: UIViewController {
var storedProperty: String
required init?(coder aDecoder: NSCoder) {
self.storedProperty = "Stored Prop"
super.init(coder: aDecoder);
}
}
'Development Tip' 카테고리의 다른 글
Play 프레임 워크에서 선택적 쿼리 매개 변수를 처리하는 방법 (0) | 2020.11.11 |
---|---|
StackOverflowException은 어떻게 감지됩니까? (0) | 2020.11.11 |
Visual Studio Code에서 올바른 들여 쓰기로 붙여 넣기 복사 설정 (0) | 2020.11.11 |
WPF 응용 프로그램에서 사용자 설정을 저장하는 방법? (0) | 2020.11.11 |
소켓 연결에 대한 추가 데이터 보내기 (0) | 2020.11.11 |