source

Swift에서 뷰 컨트롤러와 다른 개체 간에 데이터를 어떻게 공유합니까?

itover 2023. 8. 5. 22:34
반응형

Swift에서 뷰 컨트롤러와 다른 개체 간에 데이터를 어떻게 공유합니까?

예를 들어 Swift 앱에 여러 뷰 컨트롤러가 있는데 이들 컨트롤러 간에 데이터를 전달할 수 있어야 합니다.보기 컨트롤러 스택에서 여러 단계 아래에 있는 경우 다른 보기 컨트롤러로 데이터를 전달하려면 어떻게 해야 합니까?또는 탭 모음 뷰 컨트롤러의 탭 사이?

(참고로 이 질문은 "링거"입니다.)너무 많은 질문을 받아서 저는 그 주제에 대한 튜토리얼을 쓰기로 했습니다.아래 제 답변을 참조하십시오.

당신의 질문은 매우 광범위합니다.모든 시나리오에 대한 간단한 캐치올 솔루션이 있다고 제안하는 것은 약간 순진합니다.이제 이러한 시나리오를 몇 가지 살펴보겠습니다.


제 경험상 스택 오버플로에 대해 묻는 가장 일반적인 시나리오는 한 뷰 컨트롤러에서 다음 뷰 컨트롤러로 정보를 전달하는 것입니다.

스토리보드를 사용하는 경우 첫 번째 뷰 컨트롤러가 다음을 무시할 수 있습니다.prepareForSegue그게 바로 그것을 위한 것입니다. 가.UIStoryboardSegue개체는 이 메서드가 호출될 때 전달되며 대상 뷰 컨트롤러에 대한 참조를 포함합니다.여기서는 전달할 값을 설정할 수 있습니다.

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "MySegueID" {
        if let destination = segue.destination as? SecondController {
            destination.myInformation = self.myInformation
        }
    }
}

또는 스토리보드를 사용하지 않는 경우에는 니브에서 뷰 컨트롤러를 로드합니다.우리의 코드는 조금 더 단순합니다.

func showNextController() {
    let destination = SecondController(nibName: "SecondController", bundle: nil)
    destination.myInformation = self.myInformation
    show(destination, sender: self)
}

모두, 두경우모두,,myInformation는 각 뷰 컨트롤러의 속성으로, 한 뷰 컨트롤러에서 다른 뷰 컨트롤러로 전달해야 하는 모든 데이터를 보유합니다.각 컨트롤러의 이름이 같을 필요는 없습니다.


▁a▁tabs의 탭 간에 정보를 공유하고 싶을 수도 .UITabBarController.

이 경우에는 훨씬 더 단순할 수 있습니다.

먼다음하클생성다니합를래의 를 만들어 UITabBarController다양한 탭 간에 공유할 정보에 대한 속성을 제공합니다.

class MyCustomTabController: UITabBarController {
    var myInformation: [String: AnyObject]?
}

에서 앱을 , 인 스리보드앱구는경축하우됩탭클니다변기면경서하에본값를토래스의컨러트롤바서에을▁the▁our▁change▁from▁now▁bar됩▁class다니▁we▁from'▁default▁simply▁controller▁tabs▁we▁if▁our,스▁the면,board▁story're▁building변▁app에서 변경하기만 하면 됩니다.UITabBarControllerMyCustomTabController스토리보드를 사용하지 않는 경우 기본값이 아닌 이 사용자 지정 클래스의 인스턴스를 인스턴스화합니다.UITabBarController클래스를 지정하고 여기에 뷰 컨트롤러를 추가합니다.

이제 탭 모음 컨트롤러 내의 모든 뷰 컨트롤러는 다음과 같이 이 속성에 액세스할 수 있습니다.

if let tbc = self.tabBarController as? MyCustomTabController {
    // do something with tbc.myInformation
}

그리고 하위 분류에 의해.UINavigationController동일한 방식으로 전체 탐색 스택에서 데이터를 공유할 수 있습니다.

if let nc = self.navigationController as? MyCustomNavController {
    // do something with nc.myInformation
}

몇 가지 다른 시나리오가 있습니다.이 대답이 그들 모두를 포함하는 것은 아닙니다.

이 질문은 항상 나옵니다.

한 가지 제안은 데이터 컨테이너 싱글톤을 만드는 것입니다.응용 프로그램의 수명 동안 한 번만 생성되고 응용 프로그램의 수명 동안 지속되는 개체입니다.

이 접근 방식은 앱의 여러 클래스에서 사용 가능하거나 수정할 수 있어야 하는 글로벌 앱 데이터가 있는 상황에 적합합니다.

보기 컨트롤러 간에 단방향 또는 양방향 링크를 설정하는 등의 다른 접근 방식은 보기 컨트롤러 간에 정보/메시지를 직접 전달하는 상황에 더 적합합니다.

(다른 대안에 대해서는 아래의 nhgrif의 답변을 참조하십시오.

데이터 컨테이너 싱글톤을 사용하면 싱글톤에 대한 참조를 저장하는 속성을 클래스에 추가한 다음 액세스가 필요할 때마다 해당 속성을 사용할 수 있습니다.

시작할 때마다 앱 상태가 유지되도록 싱글톤이 디스크에 내용을 저장하도록 설정할 수 있습니다.

저는 GitHub에서 당신이 어떻게 이것을 할 수 있는지 보여주는 데모 프로젝트를 만들었습니다.링크는 다음과 같습니다.

GitHub의 SwiftDataContainerSingleton 프로젝트 다음은 해당 프로젝트의 README입니다.

SwiftData 컨테이너 싱글톤

데이터 컨테이너 싱글톤을 사용하여 애플리케이션 상태를 저장하고 개체 간에 공유하는 방법을 시연합니다.

DataContainerSingleton클래스는 실제 싱글톤입니다.

상수 정적상사니다용합를을 합니다.sharedDataContainer싱글톤에 대한 참조를 저장합니다.

싱글톤에 액세스하려면 구문을 사용합니다.

DataContainerSingleton.sharedDataContainer

샘플 프로젝트는 데이터 컨테이너에 다음과 같은 세 가지 속성을 정의합니다.

  var someString: String?
  var someOtherString: String?
  var someInt: Int?

로드방을 someInt데이터 컨테이너의 속성은 다음과 같은 코드를 사용합니다.

let theInt = DataContainerSingleton.sharedDataContainer.someInt

일부에 값을 저장하는 방법Int에서는 다음과 같은 구문을 사용합니다.

DataContainerSingleton.sharedDataContainer.someInt = 3

Singleton의 컨테이너init에 대한 합니다.UIApplicationDidEnterBackgroundNotification다음과 .

goToBackgroundObserver = NSNotificationCenter.defaultCenter().addObserverForName(
  UIApplicationDidEnterBackgroundNotification,
  object: nil,
  queue: nil)
  {
    (note: NSNotification!) -> Void in
    let defaults = NSUserDefaults.standardUserDefaults()
    //-----------------------------------------------------------------------------
    //This code saves the singleton's properties to NSUserDefaults.
    //edit this code to save your custom properties
    defaults.setObject( self.someString, forKey: DefaultsKeys.someString)
    defaults.setObject( self.someOtherString, forKey: DefaultsKeys.someOtherString)
    defaults.setObject( self.someInt, forKey: DefaultsKeys.someInt)
    //-----------------------------------------------------------------------------

    //Tell NSUserDefaults to save to disk now.
    defaults.synchronize()
}

컨테이너의 을 관자코드서데컨이속저의다니장합성을너에 합니다.NSUserDefaults사용할 수도 있습니다.NSCoding핵심 데이터 또는 상태 데이터를 저장하기 위한 다양한 다른 방법.

Singleton의 컨테이너init메서드는 해당 속성에 대해 저장된 값도 로드하려고 합니다.

init 메서드의 해당 부분은 다음과 같습니다.

let defaults = NSUserDefaults.standardUserDefaults()
//-----------------------------------------------------------------------------
//This code reads the singleton's properties from NSUserDefaults.
//edit this code to load your custom properties
someString = defaults.objectForKey(DefaultsKeys.someString) as! String?
someOtherString = defaults.objectForKey(DefaultsKeys.someOtherString) as! String?
someInt = defaults.objectForKey(DefaultsKeys.someInt) as! Int?
//-----------------------------------------------------------------------------

NSUserDefaults의 됩니다.DefaultsKeys다음과 같이 정의됩니다.

struct DefaultsKeys
{
  static let someString  = "someString"
  static let someOtherString  = "someOtherString"
  static let someInt  = "someInt"
}

다음과 같은 상수 중 하나를 참조합니다.

DefaultsKeys.someInt

데이터 컨테이너 싱글톤 사용:

이 샘플 응용 프로그램은 데이터 컨테이너 싱글톤을 시험적으로 사용합니다.

두 개의 보기 컨트롤러가 있습니다. 지정 입니다.ViewController의 사용자 지정 입니다.SecondVC.

뷰 모두 두 뷰 컨트롤러 모두 두 뷰 컨 롤 러 텍 의 트 가 있 컨 러 lton 롤 로 니 다 합 드 값 을 너 서 에 이 테 터 컨 이 데 단 모 일 트 두 뷰 트 두 으 며 스 필 모 드 두 ▁a ▁from ▁value▁both▁the▁container▁contro▁loadl▁single두llers▁on▁a니다합로ton드▁have▁and▁bothton▁field▁data의▁them▁view▁text'뷰을값컨,someInt을 성을텍필입 니다합력드에의 합니다.viewWillAppear 둘 다의 현재 값을 다시 'some , 필리다둘텍썸드현'로 저장합니다.데이터 컨테이너의 Int'입니다.

는 텍트필값로코는에 .viewWillAppear:방법:

override func viewWillAppear(animated: Bool)
{
  //Load the value "someInt" from our shared ata container singleton
  let value = DataContainerSingleton.sharedDataContainer.someInt ?? 0
  
  //Install the value into the text field.
  textField.text =  "\(value)"
}

"View"에 있습니다.textFieldShouldEndEditing메서드:

 func textFieldShouldEndEditing(textField: UITextField) -> Bool
 {
   //Save the changed value back to our data container singleton
   DataContainerSingleton.sharedDataContainer.someInt = textField.text!.toInt()
   return true
 }

보기 컨트롤러가 표시될 때마다 UI가 업데이트되도록 viewDidLoad가 아닌 viewWillAppear의 사용자 인터페이스에 값을 로드해야 합니다.

또 다른 방법은 알림 센터(NSNotificationCenter)를 사용하여 알림을 게시하는 것입니다.그것은 매우 느슨한 커플링입니다.통지를 보낸 사람은 누가 듣고 있는지 알거나 신경 쓸 필요가 없습니다.그것은 단지 알림을 올리고 그것에 대해 잊어버립니다.

알림은 지정된 메시지를 수신하는 임의의 수의 관찰자가 있을 수 있으므로 일대일 메시지 전달에 유용합니다.

스위프트 4

데이터를 빠르게 전달하는 방법은 매우 많습니다.여기에 몇 가지 가장 좋은 접근법을 추가합니다.

StoryBoard Segue 사용

Storyboard segue는 소스 뷰 컨트롤러와 대상 뷰 컨트롤러 간에 데이터를 전달하거나 그 반대의 경우에도 매우 유용합니다.

// If you want to pass data from ViewControllerB to ViewControllerA while user tap on back button of ViewControllerB.
        @IBAction func unWindSeague (_ sender : UIStoryboardSegue) {
            if sender.source is ViewControllerB  {
                if let _ = sender.source as? ViewControllerB {
                    self.textLabel.text = "Came from B = B->A , B exited"
                }
            }
        }

// If you want to send data from ViewControllerA to ViewControllerB
        override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
            if  segue.destination is ViewControllerB {
                if let vc = segue.destination as? ViewControllerB {
                    vc.dataStr = "Comming from A View Controller"
                }
            }
        }

위임 방법 사용

뷰 컨트롤러 D

//Make the Delegate protocol in Child View Controller (Make the protocol in Class from You want to Send Data)
    protocol  SendDataFromDelegate {
        func sendData(data : String)
    }

    import UIKit

    class ViewControllerD: UIViewController {

        @IBOutlet weak var textLabelD: UILabel!

        var delegate : SendDataFromDelegate?  //Create Delegate Variable for Registering it to pass the data

        override func viewDidLoad() {
            super.viewDidLoad()
            // Do any additional setup after loading the view.
            textLabelD.text = "Child View Controller"
        }

        @IBAction func btnDismissTapped (_ sender : UIButton) {
            textLabelD.text = "Data Sent Successfully to View Controller C using Delegate Approach"
            self.delegate?.sendData(data:textLabelD.text! )
            _ = self.dismiss(animated: true, completion:nil)
        }
    }

뷰 컨트롤러 C

    import UIKit

    class ViewControllerC: UIViewController , SendDataFromDelegate {

        @IBOutlet weak var textLabelC: UILabel!

        override func viewDidLoad() {
            super.viewDidLoad()
            // Do any additional setup after loading the view.
        }

        @IBAction func btnPushToViewControllerDTapped( _ sender : UIButton) {
            if let vcD = self.storyboard?.instantiateViewController(withIdentifier: "ViewControllerD") as?  ViewControllerD  {
                vcD.delegate = self // Registring Delegate (When View Conteoller D gets Dismiss It can call sendData method
    //            vcD.textLabelD.text = "This is Data Passing by Referenceing View Controller D Text Label." //Data Passing Between View Controllers using Data Passing
                self.present(vcD, animated: true, completion: nil)
            }
        }

        //This Method will called when when viewcontrollerD will dismiss. (You can also say it is a implementation of Protocol Method)
        func sendData(data: String) {
            self.textLabelC.text = data
        }

    }

데이터 컨트롤러를 싱글톤으로 만드는 대신 데이터 컨트롤러 인스턴스를 만들어 전달하는 것이 좋습니다.의존성 주입을 지원하기 위해 먼저 다음을 작성합니다.DataController프로토콜:

protocol DataController {
    var someInt : Int {get set} 
    var someString : String {get set}
}

저는 그면제만것다입니들가러▁a다를 만들 입니다.SpecificDataController class:(으)로 표시됨:

class SpecificDataController : DataController {
   var someInt : Int = 5
   var someString : String = "Hello data" 
}

ViewController그러면 클래스는 다음을 보유할 필드를 가져야 합니다.dataController은 다유형시주오십하의에음의 입니다.dataController입니다.DataController이렇게 하면 데이터 컨트롤러 구현을 쉽게 전환할 수 있습니다.

class ViewController : UIViewController {
   var dataController : DataController?
   ...
}

AppDelegate할 수 .dataController:

 func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    if let viewController = self.window?.rootViewController as? ViewController {
        viewController.dataController =  SpecificDataController()
    }   
    return true
}

다른 뷰로 이동하면 컨트롤러를 통과할 수 있습니다.dataController표시 위치:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    ...   
}

이제 하고자 할 이 할 수 .AppDelegate데이터 컨트롤러를 사용하는 다른 코드를 변경할 필요가 없습니다.

만약 우리가 단순히 하나의 값을 전달하고 싶다면, 이것은 당연히 과잉 살상입니다.이런 경우에는 nhgrif의 대답을 따르는 것이 가장 좋습니다.

이 접근 방식을 사용하면 논리 부분에서 뷰를 분리할 수 있습니다.

@nhgrif가 훌륭한 답변에서 지적했듯이 VC(뷰 컨트롤러)와 다른 개체가 서로 통신할 수 있는 방법은 매우 다양합니다.

제가 첫 번째 답변에서 요약한 데이터 싱글톤은 직접적인 의사소통보다는 세계적인 상태를 공유하고 저장하는 것에 관한 것입니다.

nhif의 응답을 통해 소스에서 대상 VC로 직접 정보를 보낼 수 있습니다.답변에서 언급했듯이, 메시지를 수신처에서 소스로 다시 보내는 것도 가능합니다.

실제로 서로 다른 뷰 컨트롤러 간에 활성 단방향 또는 양방향 채널을 설정할 수 있습니다.보기 컨트롤러가 스토리보드 세그먼트를 통해 연결된 경우 링크를 설정하는 시간은 prepareForSegue 메서드에 있습니다.

저는 Github에 부모 뷰 컨트롤러를 사용하여 두 개의 다른 테이블 뷰를 자식으로 호스팅하는 샘플 프로젝트를 가지고 있습니다.하위 뷰 컨트롤러는 임베드 세그를 사용하여 연결되고 상위 뷰 컨트롤러는 prepareForSegue 메서드에서 각 뷰 컨트롤러와 양방향 링크를 연결합니다.

그 프로젝트는 github(link)에서 찾을있습니다.하지만 나는 그것을 Objective-C에 썼고 Swift로 변환하지 않았기 때문에 만약 당신이 Objective-C에 불편하다면 그것을 따르기가 조금 어려울 수도 있습니다.

SWIFT 3:

segue가 식별된 스토리보드가 있는 경우 다음을 사용합니다.

func prepare(for segue: UIStoryboardSegue, sender: Any?)

서로 다른 UIViewController 간의 탐색을 포함하여 프로그래밍 방식으로 모든 작업을 수행하는 경우에는 다음 방법을 사용합니다.

func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool)

참고: UINavigationController를 만드는 데 필요한 두 번째 방법을 사용하려면 UIViewController를 밀어넣어야 합니다. 즉, 위임자가 UINavigationControllerDelegate 프로토콜을 준수해야 합니다.

   class MyNavigationController: UINavigationController, UINavigationControllerDelegate {

    override func viewDidLoad() {
        self.delegate = self
    }

    func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {

     // do what ever you need before going to the next UIViewController or back
     //this method will be always called when you are pushing or popping the ViewController

    }
}

그것은 당신이 언제 데이터를 얻고 싶은지에 따라 다릅니다.

원하는 때마다 데이터를 가져오려면 싱글톤 패턴을 사용할 수 있습니다.패턴 클래스는 앱 런타임 중에 활성화됩니다.여기 싱글톤 패턴의 예가 있습니다.

class AppSession: NSObject {

    static let shared = SessionManager()
    var username = "Duncan"
}

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        print(AppSession.shared.username)
    }
}

작업 후 데이터를 가져오려는 경우 Notification 센터를 사용할 수 있습니다.

extension Notification.Name {
    static let loggedOut = Notification.Name("loggedOut")
}

@IBAction func logoutAction(_ sender: Any) {
    NotificationCenter.default.post(name: .loggedOut, object: nil)
}

NotificationCenter.default.addObserver(forName: .loggedOut, object: nil, queue: OperationQueue.main) { (notify) in
    print("User logged out")
}

뷰 컨트롤러 간에 데이터를 전달하는 대신 변수를 글로벌하게 선언합니다.여러분은 기능으로도 이것을 할 수 있습니다!

예:

var a = "a"
func abc() {
   print("abc")
}
class ViewController: UIViewController {

}

언급URL : https://stackoverflow.com/questions/29734954/how-do-you-share-data-between-view-controllers-and-other-objects-in-swift

반응형