ios - example - swift navigation item title



如何在Swift中的視圖控制器和其他對象之間共享數據? (6)

假設我的Swift應用程序中有多個視圖控制器,我希望能夠在它們之間傳遞數據。 如果我在視圖控制器堆棧中處於多個級別,那麼如何將數據傳遞到另一個視圖控制器? 還是在標籤欄視圖控制器中的標籤之間?

(請注意,這個問題是個“響噹噹”。)它被問到太多了,所以我決定寫一個關於這個主題的教程。 請參閱下面的答案。


Answer #1

SWIFT 3:

如果您的情節提要具有確定的片段,請使用:

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

儘管如果您以編程方式進行所有操作(包括在不同的UIViewController之間進行導航),請使用以下方法:

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

注意:要使用第二種方法,您需要創建UINavigationController,將UIViewControllers推入一個委託,並且它必須符合協議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

    }
}

Answer #2

另一種選擇是使用通知中心(NSNotificationCenter)並發布通知。 那是非常鬆散的耦合。 通知的發件人不需要知道或關心誰在聽。 它只是發布通知,而忘記了它。

通知對於一對多消息傳遞很有用,因為可以有任意數量的觀察者在偵聽給定消息。


Answer #3

我建議不要創建一個數據控制器實例並傳遞它,而不是創建一個數據控制器singelton。 為了支持依賴注入,我將首先創建一個 DataController 協議:

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

然後,我將創建一個 SpecificDataController (或任何當前合適的名稱)類:

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

然後, ViewController 類應該具有一個字段來保存 dataController 。 注意, dataController 的類型是協議 DataController 。 這樣,很容易切換出數據控制器實現:

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

AppDelegate 我們可以設置viewController的 dataController

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

當我們移動到另一個viewController時,我們可以將 dataController 傳遞給:

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

現在,當我們希望將數據控制器切換為其他任務時,我們可以在 AppDelegate 執行此操作,而不必更改使用該數據控制器的任何其他代碼。

如果我們只想傳遞一個值,這當然是矯kill過正。 在這種情況下,最好選擇nhgrif的答案。

通過這種方法,我們可以將視圖分離為邏輯部分。


Answer #4

正如@nhgrif在他的出色回答中指出的那樣,VC(視圖控制器)和其他對象可以通過多種不同的方式相互通信。

我在第一個答案中概述的數據單例實際上更多地是關於共享和保存全局狀態,而不是直接通信。

nhrif的答案使您可以將信息直接從源發送到目標VC。 正如我在回復中提到的,也可以將消息從目標發送回源。

實際上,您可以在不同的視圖控制器之間設置活動的單向或兩向通道。 如果視圖控制器是通過情節提要Segue鏈接的,則建立鏈接的時間在prepareFor Segue方法中。

我在Github上有一個示例項目,該項目使用父視圖控制器將2個不同的表視圖作為子項承載。 子視圖控制器使用嵌入的segue進行鏈接,父視圖控制器在prepareForSegue方法中與每個視圖控制器建立2路鏈接。

您可以 在github (鏈接) 上找到該項目 。 但是,我是用Objective-C編寫的,但尚未將其轉換為Swift,因此,如果您不熟悉Objective-C,可能會有些困難


Answer #5

這取決於您何時獲取數據。

如果您想隨時獲取數據,可以使用單例模式。 模式類在應用程序運行時處於活動狀態。 這是單例模式的示例。

class AppSession: NSObject {

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

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

        print(AppSession.shared.username)
    }
}

如果要在執行任何操作後獲取數據,可以使用NotificationCenter。

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

Answer #6

斯威夫特4

快速的數據傳遞方法有很多。 在這裡,我添加了一些最佳方法。

1)使用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"
                }
            }
        }

2)使用委託方法

ViewControllerD

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

ViewControllerC

    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
        }

    }




swift