programing

스토리보드에서 인터페이스를 설정하는 Swift의 UIViewController에 대한 사용자 정의 init

padding 2023. 8. 11. 21:35
반응형

스토리보드에서 인터페이스를 설정하는 Swift의 UIViewController에 대한 사용자 정의 init

하위 에 대해 데 . 으로 UIViewController처럼 을 설정하는 . 기본적으로 속성을 다음과 같이 직접 설정하는 대신 viewController에 대한 init 메서드를 통해 종속성을 전달하고 싶습니다.viewControllerB.property = value

그래서 나는 내 뷰에 대한 사용자 정의 init를 만들었고 컨트롤러와 super designated init를 호출했습니다.

init(meme: Meme?) {
        self.meme = meme
        super.init(nibName: nil, bundle: nil)
    }

뷰 컨트롤러 인터페이스는 스토리보드에 상주하며, 사용자 지정 클래스를 위한 인터페이스도 내 뷰 컨트롤러로 만들었습니다.그리고 스위프트는 당신이 이 방법 내에서 아무것도 하지 않더라도 이를 init 메소드라고 부르도록 요구합니다.그렇지 않으면 컴파일러가 불평할 것입니다.

required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

문제는 내가 내 고객을 호출하려고 할 때입니다.MyViewController(meme: meme)내 보기에 컨트롤러에서 속성이 전혀 시작되지 않습니다.

하려고 제 라는 컨트롤러가 있습니다.init(coder aDecoder: NSCoder)먼저 호출되고 나중에 내 사용자 정의 init가 호출됩니다. 이 두 다른 그나이두가지방법다다서은릅니로러▁different▁return▁however를 반환합니다.self메모리 주소

내 보기에 대한 init에 문제가 있는 것 같습니다. 컨트롤러가 항상 반환됩니다.selfinit?(coder aDecoder: NSCoder)구현이 없는 것입니다.

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)
    }

}

위의 답변 중 하나에 명시된 대로 및 사용자 정의 init 메서드와 스토리보드를 모두 사용할 수 없습니다.

하지만 정적 방법을 사용하여 인스턴스화할 수 있습니다.ViewController스토리보드에서 추가 설정을 수행합니다.

다음과 같이 표시됩니다.

class MemeDetailVC : UIViewController {
    
    var meme : Meme!
    
    static func makeMemeDetailVC(meme: Meme) -> MemeDetailVC {
        let newViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "IdentifierOfYouViewController") as! MemeDetailVC
        
        newViewController.meme = meme
        
        return newViewController
    }
}

스토리보드에서 IdentifierOfYouViewController를 뷰 컨트롤러 식별자로 지정하는 것을 잊지 마십시오.위의 코드에서 스토리보드의 이름을 변경해야 할 수도 있습니다.

수 .init?(coder aDecoder: NSCoder)Apple이 컨트롤러를 초기화하기 위해 스토리보드를 설계한 방법입니다.를 그나데전송방있법습다니이는하로 보내는 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"
        }
    }
}

유형성속을사용다니습했▁of▁property다▁type▁a니습▁used▁i▁just용사 타입의 속성을 사용했습니다.StringMeme또한segue identifier)."identifier"자리 표시자일 뿐입니다.)

@Caleb Kleverter가 지적했듯이 Storyboard에서 초기화하는 동안에는 맞춤형 초기화 프로그램을 사용할 수 없습니다.

그러나 Storyboard에서 뷰 컨트롤러 객체를 인스턴스화하고 뷰 컨트롤러 객체를 반환하는 팩토리/클래스 방식을 사용하여 문제를 해결할 수 있습니다.저는 이것이 꽤 멋진 방법이라고 생각합니다.

참고: 이는 질문에 대한 정확한 대답이 아니라 문제를 해결하기 위한 해결 방법입니다.

MemeDetailVC 클래스의 클래스 메소드를 다음과 같이 만듭니다.

// 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
}

용도:

let memeDetailVC = MemeDetailVC.init(meme: Meme())

한 가지 방법은 편의성 이니셜라이저를 사용하는 것입니다.

class MemeDetailVC : UIViewController {

    convenience init(meme: Meme) {
        self.init()
        self.meme = meme
    }
}

"MemeDetail VC"를 사용하여 합니다.let memeDetailVC = MemeDetailVC(theMeme)

이니셜라이저에 대한 애플의 문서는 꽤 괜찮지만, 제가 개인적으로 가장 좋아하는 것은 레이 웬더리치입니다. 다양한 init 옵션과 "적절한" 작업 방법에 대한 충분한 설명/예시를 제공하는 Depth 튜토리얼 시리즈의 초기화.


편집: 사용자 정의 뷰 컨트롤러에서 편리한 이니셜라이저를 사용할 수 있지만 스토리보드에서 또는 스토리보드 세그를 통해 초기화할 때 사용자 정의 이니셜라이저를 사용할 수 없다는 것은 모두가 옳습니다.

인터페이스가 스토리보드에 설정되어 있고 완전히 프로그래밍 방식으로 컨트롤러를 만드는 경우에는 NS Coder를 사용하여 필요한 인터페이스를 처리할 필요가 없기 때문에 편의성 초기화 프로그램이 가장 쉽게 수행할 수 있는 방법일 것입니다(아직 잘 이해되지 않습니다).

스토리보드를 통해 뷰 컨트롤러를 얻는 경우 @Caleb Kleveter의 대답을 따라 원하는 하위 클래스에 뷰 컨트롤러를 캐스팅한 다음 속성을 수동으로 설정해야 합니다.

원래 몇 가지 답변이 있었는데, 소가 투표를 해서 기본적으로 맞았는데도 삭제했습니다.대답은, 할 수 없다는 것입니다.

스토리보드 정의에서 작업할 때 뷰 컨트롤러 인스턴스는 모두 아카이브됩니다.그래서, 그것들을 시작하기 위해서는.init?(coder...사용됩니다.coder는 모든 설정/보기 정보의 출처입니다.

따라서 이 경우에는 사용자 정의 매개 변수를 사용하여 다른 init 함수를 호출할 수 없습니다.segue를 준비할 때 속성으로 설정하거나, segue를 버리고 스토리보드에서 직접 인스턴스를 로드하여 구성할 수 있습니다(기본적으로 스토리보드를 사용하는 공장 패턴).

모든 경우 SDK 필수 init 기능을 사용하고 이후에 추가 매개 변수를 전달합니다.

스위프트 5

커스텀 이니셜라이저는 이렇게 작성할 수 있습니다 ->

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
    }
}

UIViewController는 에부하계급는에 적합합니다.NSCoding과 같이:

public protocol NSCoding {

   public func encode(with aCoder: NSCoder)

   public init?(coder aDecoder: NSCoder) // NS_DESIGNATED_INITIALIZER
}    

그렇게UIViewController에는 두 이니셜라이저가 .init?(coder aDecoder: NSCoder)그리고.init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?).

스토리보드 호출init?(coder aDecoder: NSCoder)바로 그 안에.UIViewController그리고.UIView매개 변수를 전달할 공간이 없습니다.

한 가지 번거로운 해결 방법은 임시 캐시를 사용하는 것입니다.

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
}

부터는 다음을뷰할 수 있습니다.instantiateViewController(identifier:creator:)에 있는 UIStoryboard사례.

튜토리얼: https://sarunw.com/posts/better-dependency-injection-for-storyboards-in-ios13/

에 대해 지정 를 할 수 ,instantiateInitialViewController(creator:)그리고 관계와 쇼를 포함한 seggues를 위하여.

이 기능은 Xcode 11에 추가되었으며 다음은 Xcode 11 릴리스 노트에서 발췌한 것입니다.

view controller method에 이 달린 view .@IBSegueAction속성을 사용하여 필요한 값이 있는 사용자 지정 이니셜라이저를 사용하여 코드에서 segue의 대상 뷰 컨트롤러를 만들 수 있습니다.이를 통해 스토리보드에서 옵션이 아닌 초기화 요구사항이 있는 뷰 컨트롤러를 사용할 수 있습니다.에서 segue로의 .@IBSegueAction메서드가 소스 뷰 컨트롤러에 있습니다.은 SegueActions라는 이름의 OS가 .destinationViewController에서 전된달 segue 객의로 prepareForSegue:sender:@IBSegueAction할 수 , 를 통해 segue 수 .prepareForSegue:sender:. (47091566)

안 안IBSegueActionmethod는 최대 세 개의 매개 변수, 즉 코더, 송신자 및 세그의 식별자를 사용합니다.첫 번째 매개 변수가 필요하며, 원하는 경우 메서드의 서명에서 다른 매개 변수를 생략할 수 있습니다.NSCoder스토리보드에 구성된 값으로 사용자 지정하려면 대상 뷰 컨트롤러의 이니셜라이저로 전달해야 합니다.하는 뷰 합니다.nil으로 합니다.init(coder:)방법.만약 당신이 돌아올 필요가 없다는 것을 안다면.nil반환 형식은 선택 사항이 아닐 수 있습니다.

를 추가합니다.@IBSegueAction속성:

@IBSegueAction
func makeDogController(coder: NSCoder, sender: Any?, segueIdentifier: String?) -> ViewController? {
    PetController(
        coder: coder,
        petName:  self.selectedPetName, type: .dog
    )
}

에-C서를 합니다.IBSegueAction반환 유형 앞:

- (IBSegueAction ViewController *)makeDogController:(NSCoder *)coder
               sender:(id)sender
      segueIdentifier:(NSString *)segueIdentifier
{
   return [PetController initWithCoder:coder
                               petName:self.selectedPetName
                                  type:@"dog"];
}

XCode 11/iOS13에서 사용할 수 있습니다.instantiateViewController(identifier:creator:)를 사용하지않는 경우도 . segue는 다음과 같습니다.

    let vc = UIStoryboard(name: "StoryBoardName", bundle: nil).instantiateViewController(identifier: "YourViewControllerIdentifier", creator: {
        (coder) -> YourViewController? in
        return YourViewController(coder: coder, customParameter: "whatever")
    })
    present(vc, animated: true, completion: nil)

은 사용자 도 ▁the▁this▁▁be▁without▁initializers다board▁solution▁a▁shows▁but니▁still이▁custom▁using▁use▁have를▁to▁to▁way보줍여▁able방솔루을법있는션을 사용하지 않고 Storyboard를 사용할 수 있는 방법을 보여줍니다.self.init(nib: nil, bundle: nil)기능.

그것을 사용할 수 있도록 하기 위해, 먼저 우리의 것을 조정해 보겠습니다.MemeDetailsVC 사용자 초기화 해당 을 "" "" "" "" "" "" ""에 위임합니다.super.init(coder:)하는 nibName nibName을 합니다.

class MemeDetailVC : UIViewController {
    var meme : Meme!
    @IBOutlet weak var editedImage: UIImageView!

    init?(meme: Meme, coder: NSCoder) {
        self.meme = meme
        super.init(coder: aDecoder)
    }
    @available(*, unavailable, renamed: "init(product:coder:)")
        required init?(coder: NSCoder) {
            fatalError("Invalid way of decoding this class")
        }

    override func viewDidLoad() {
        title = "Detail Meme"
        super.viewDidLoad()
    }

    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)
        editedImage = UIImageView(image: meme.editedImage)
    }
}

그런 다음 View 컨트롤러를 다음과 같이 인스턴스화하고 표시합니다.

guard let viewController = storyboard?.instantiateViewController(
        identifier: "MemeDetailVC",
        creator: { coder in
            MemeDetailVC(meme: meme, coder: coder)
        }
    ) else {
        fatalError("Failed to create Product Details VC")
    }
//Then you do what you want with the view controller.
    present(viewController, sender: self)

고지 사항:저는 이것을 옹호하지 않고 그것의 복원력을 철저히 테스트하지 않았지만, 그것은 제가 장난을 치다가 발견한 잠재적인 해결책입니다.

는 두 컨트롤러 를 통해 스토리보드 할 수 . 정의 기술로사정처두뷰컨를음러로여트다스롤으하니있습수달구토할드성성리보인화유터이스적페를기서면지하초으번기는화용자의초▁your▁the▁techn▁contro:▁via▁viewboard▁time▁first다▁by▁custom▁initial▁initial니▁twiceller-습▁custom▁be있▁while▁achieved▁story수▁can기.init 두 는 리고두번안에서째그안서▁and 안에.loadView()스토리보드에서 경치를 볼 수 있는 곳.

final class CustomViewController: UIViewController {
  @IBOutlet private weak var label: UILabel!
  @IBOutlet private weak var textField: UITextField!

  private let foo: Foo!

  init(someParameter: Foo) {
    self.foo = someParameter
    super.init(nibName: nil, bundle: nil)
  }

  override func loadView() {
    //Only proceed if we are not the storyboard instance
    guard self.nibName == nil else { return super.loadView() }

    //Initialize from storyboard
    let storyboard = UIStoryboard(name: "Main", bundle: nil)
    let storyboardInstance = storyboard.instantiateViewController(withIdentifier: "CustomVC") as! CustomViewController

    //Remove view from storyboard instance before assigning to us
    let storyboardView = storyboardInstance.view
    storyboardInstance.view.removeFromSuperview()
    storyboardInstance.view = nil
    self.view = storyboardView

    //Receive outlet references from storyboard instance
    self.label = storyboardInstance.label
    self.textField = storyboardInstance.textField
  }

  required init?(coder: NSCoder) {
    //Must set all properties intended for custom init to nil here (or make them `var`s)
    self.foo = nil
    //Storyboard initialization requires the super implementation
    super.init(coder: coder)
  }
}

를 이제앱다곳다음같과이사정다용있수호습니출할를이저이라니셜의의자른에서▁your▁like▁initial▁now다izer▁you▁call있▁custom니습이▁in▁can와 같은 사용자 정의 이니셜라이저로 부를 수 있습니다.CustomViewController(someParameter: foo)스토리보드에서 보기 구성을 계속 수신할 수 있습니다.

저는 몇 가지 이유로 이것이 좋은 해결책이라고 생각하지 않습니다.

  • 모든 사전 초기화 속성을 포함하여 개체 초기화가 복제되었습니다.
  • 정의 에 변수입니다.init.
  • 배출구/특성이 변경될 때 유지관리해야 하는 보일러 플레이트 추가

아마도 당신은 이러한 절충안을 받아들일 수 있지만, 당신의 위험을 감수하고 사용할 수 있습니다.

올바른 흐름은 지정된 이니셜라이저를 호출하는 것입니다. 이 경우에는 nibName을 사용하는 init입니다.

init(tap: UITapGestureRecognizer)
{
    // Initialise the variables here


    // Call the designated init of ViewController
    super.init(nibName: nil, bundle: nil)

    // Call your Viewcontroller custom methods here

}

View 컨트롤러는 메인 스토리보드에 있으며 식별자 세트가 있습니다.

클래스 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!
}

클래스 A

let objB = customInit(carType:"Any String")

 navigationController?.pushViewController(objB,animated: true)

언급URL : https://stackoverflow.com/questions/35315404/custom-init-for-uiviewcontroller-in-swift-with-interface-setup-in-storyboard

반응형