Katakanlah saya memiliki beberapa pengontrol tampilan di aplikasi Swift saya dan saya ingin dapat meneruskan data di antara mereka. Jika saya beberapa level di bawah dalam tumpukan pengontrol tampilan, bagaimana cara meneruskan data ke pengontrol tampilan lain? Atau di antara tab dalam pengontrol tampilan bilah tab?
(Catatan, pertanyaan ini adalah "nada dering".) Pertanyaan ini begitu banyak sehingga saya memutuskan untuk menulis tutorial tentang subjek tersebut. Lihat jawaban saya di bawah.
Jawaban:
Pertanyaan Anda sangat luas. Untuk menyarankan bahwa ada satu solusi sederhana untuk semua skenario adalah sedikit naif. Jadi, mari kita lihat beberapa skenario ini.
Skenario paling umum yang ditanyakan tentang Stack Overflow dalam pengalaman saya adalah informasi yang lewat sederhana dari satu pengontrol tampilan ke yang berikutnya.
Jika kita menggunakan storyboard, pengontrol tampilan pertama kita dapat menimpa
prepareForSegue
, untuk itulah tepatnya. SebuahUIStoryboardSegue
objek dilewatkan saat metode ini dipanggil, dan itu berisi referensi ke pengontrol tampilan tujuan kita. Di sini, kita dapat mengatur nilai yang ingin kita teruskan.override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == "MySegueID" { if let destination = segue.destination as? SecondController { destination.myInformation = self.myInformation } } }
Alternatifnya, jika kita tidak menggunakan storyboard, maka kita memuat pengontrol tampilan kita dari ujung. Kode kami sedikit lebih sederhana.
func showNextController() { let destination = SecondController(nibName: "SecondController", bundle: nil) destination.myInformation = self.myInformation show(destination, sender: self) }
Dalam kedua kasus,
myInformation
adalah properti pada setiap pengontrol tampilan yang menyimpan data apa pun yang perlu diteruskan dari satu pengontrol tampilan ke pengontrol berikutnya. Mereka jelas tidak harus memiliki nama yang sama di setiap pengontrol.Kami mungkin juga ingin berbagi informasi antar tab di file
UITabBarController
.Dalam hal ini, sebenarnya berpotensi lebih sederhana.
Pertama, mari buat subkelas
UITabBarController
, dan berikan properti untuk informasi apa pun yang ingin kita bagikan di antara berbagai tab:class MyCustomTabController: UITabBarController { var myInformation: [String: AnyObject]? }
Sekarang, jika kita membangun aplikasi kita dari storyboard, kita cukup mengubah kelas pengontrol tab bar kita dari default
UITabBarController
menjadiMyCustomTabController
. Jika kita tidak menggunakan storyboard, kita cukup membuat instance dari kelas kustom ini daripadaUITabBarController
kelas default dan menambahkan pengontrol tampilan kita ke ini.Sekarang, semua pengontrol tampilan kita dalam pengontrol bilah tab dapat mengakses properti ini seperti:
if let tbc = self.tabBarController as? MyCustomTabController { // do something with tbc.myInformation }
Dan dengan membuat subclass
UINavigationController
dengan cara yang sama, kita dapat mengambil pendekatan yang sama untuk berbagi data di seluruh tumpukan navigasi:if let nc = self.navigationController as? MyCustomNavController { // do something with nc.myInformation }
Ada beberapa skenario lainnya. Tidak berarti jawaban ini mencakup semuanya.
sumber
prepareForSegue
. Sayang sekali pengamatan yang sangat sederhana ini hilang di antara jawaban dan penyimpangan lain di sini.prepareForSegue
atau transfer informasi langsung lainnya di hampir setiap skenario dan kemudian baik-baik saja dengan para pemula ketika mereka muncul dengan skenario di mana situasi ini tidak berhasil dan kita kemudian harus mengajari mereka tentang pendekatan yang lebih global ini.Pertanyaan ini selalu muncul setiap saat.
Salah satu sarannya adalah membuat penampung data tunggal: Objek yang dibuat sekali dan hanya sekali dalam kehidupan aplikasi Anda, dan terus berlanjut selama masa aktif aplikasi Anda.
Pendekatan ini sangat sesuai untuk situasi ketika Anda memiliki data aplikasi global yang perlu tersedia / dapat dimodifikasi di berbagai kelas dalam aplikasi Anda.
Pendekatan lain seperti menyiapkan tautan satu arah atau 2 arah antara pengontrol tampilan lebih cocok untuk situasi di mana Anda menyampaikan informasi / pesan secara langsung antara pengontrol tampilan.
(Lihat jawaban nhgrif, di bawah, untuk alternatif lain.)
Dengan penampung data tunggal, Anda menambahkan properti ke kelas Anda yang menyimpan referensi ke tunggal Anda, lalu menggunakan properti itu kapan pun Anda membutuhkan akses.
Anda bisa mengatur singleton Anda sehingga ia menyimpan isinya ke disk sehingga status aplikasi Anda tetap ada di antara peluncuran.
Saya membuat proyek demo di GitHub yang mendemonstrasikan bagaimana Anda dapat melakukan ini. Ini tautannya:
Proyek SwiftDataContainerSingleton di GitHub Berikut adalah README dari proyek itu:
SwiftDataContainerSingleton
Demonstrasi menggunakan wadah data tunggal untuk menyimpan status aplikasi dan membagikannya di antara objek.
The
DataContainerSingleton
kelas adalah tunggal yang sebenarnya.Ini menggunakan konstanta statis
sharedDataContainer
untuk menyimpan referensi ke singleton.Untuk mengakses singleton, gunakan sintaks
DataContainerSingleton.sharedDataContainer
Proyek sampel mendefinisikan 3 properti dalam penampung data:
var someString: String? var someOtherString: String? var someInt: Int?
Untuk memuat
someInt
properti dari penampung data, Anda akan menggunakan kode seperti ini:let theInt = DataContainerSingleton.sharedDataContainer.someInt
Untuk menyimpan nilai ke someInt, Anda akan menggunakan sintaks:
DataContainerSingleton.sharedDataContainer.someInt = 3
Metode DataContainerSingleton
init
menambahkan pengamat untukUIApplicationDidEnterBackgroundNotification
. Kode itu terlihat seperti ini: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() }
Dalam kode pengamat itu menyimpan properti penampung data ke
NSUserDefaults
. Anda juga dapat menggunakanNSCoding
Core Data, atau berbagai metode lain untuk menyimpan data status.Metode DataContainerSingleton
init
juga mencoba memuat nilai yang disimpan untuk propertinya.Bagian dari metode init tersebut terlihat seperti ini:
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? //-----------------------------------------------------------------------------
Kunci untuk memuat dan menyimpan nilai ke NSUserDefaults disimpan sebagai konstanta string yang merupakan bagian dari sebuah struct
DefaultsKeys
, didefinisikan seperti ini:struct DefaultsKeys { static let someString = "someString" static let someOtherString = "someOtherString" static let someInt = "someInt" }
Anda mereferensikan salah satu konstanta ini seperti ini:
DefaultsKeys.someInt
Menggunakan wadah data tunggal:
Aplikasi sampel ini membuat penggunaan penampung data tunggal secara trival.
Ada dua pengontrol tampilan. Yang pertama adalah subkelas khusus UIViewController
ViewController
, dan yang kedua adalah subkelas khusus UIViewControllerSecondVC
.Kedua pengontrol tampilan memiliki bidang teks di atasnya, dan keduanya memuat nilai dari properti penampung data singlelton
someInt
ke bidang teks dalamviewWillAppear
metode mereka , dan keduanya menyimpan nilai saat ini dari bidang teks kembali ke `someInt 'penampung data.Kode untuk memuat nilai ke dalam bidang teks ada di
viewWillAppear:
metode: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)" }
Kode untuk menyimpan nilai yang diedit pengguna kembali ke penampung data ada di
textFieldShouldEndEditing
metode pengontrol tampilan :func textFieldShouldEndEditing(textField: UITextField) -> Bool { //Save the changed value back to our data container singleton DataContainerSingleton.sharedDataContainer.someInt = textField.text!.toInt() return true }
Anda harus memuat nilai ke antarmuka pengguna Anda di viewWillAppear daripada viewDidLoad sehingga UI Anda diperbarui setiap kali pengontrol tampilan ditampilkan.
sumber
Cepat 4
Ada begitu banyak pendekatan untuk pengiriman data dengan cepat. Di sini saya menambahkan beberapa pendekatan terbaik darinya.
1) Menggunakan Segue StoryBoard
Segmen papan cerita sangat berguna untuk meneruskan data di antara Pengontrol Tampilan Tujuan dan Sumber dan juga sebaliknya.
// 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) Menggunakan Metode Delegasi
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 } }
sumber
ViewControllerA
keViewControllerB
. Saya hanya menempelkan potongan kode di bagian bawah sayaViewControllerA.swift
(di manaViewControllerA.swift
sebenarnya apa pun nama file Anda, tentu saja) tepat sebelum kurung kurawal terakhir. "prepare
" sebenarnya adalah fungsi khusus yang sudah ada sebelumnya di Kelas tertentu [yang tidak melakukan apa-apa], itulah sebabnya Anda harus "override
" melakukannyaAlternatif lain adalah dengan menggunakan pusat pemberitahuan (NSNotificationCenter) dan pemberitahuan posting. Itu adalah kopling yang sangat longgar. Pengirim notifikasi tidak perlu tahu atau peduli siapa yang mendengarkan. Itu hanya memposting pemberitahuan dan melupakannya.
Notifikasi bagus untuk penyampaian pesan satu-ke-banyak, karena bisa saja terdapat sejumlah pengamat yang mendengarkan pesan tertentu.
sumber
Alih-alih membuat pengontrol data singelton, saya akan menyarankan untuk membuat contoh pengontrol data dan menyebarkannya. Untuk mendukung injeksi ketergantungan, saya pertama-tama akan membuat
DataController
protokol:protocol DataController { var someInt : Int {get set} var someString : String {get set} }
Kemudian saya akan membuat kelas
SpecificDataController
(atau nama apa pun yang saat ini sesuai):class SpecificDataController : DataController { var someInt : Int = 5 var someString : String = "Hello data" }
The
ViewController
kelas maka harus memiliki lapangan untuk memegangdataController
. Perhatikan bahwa tipedataController
adalah protokolDataController
. Dengan cara ini, mudah untuk mengganti implementasi pengontrol data:class ViewController : UIViewController { var dataController : DataController? ... }
Di dalam
AppDelegate
kita dapat mengatur viewControllerdataController
:func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { if let viewController = self.window?.rootViewController as? ViewController { viewController.dataController = SpecificDataController() } return true }
Saat kita pindah ke viewController yang berbeda, kita bisa meneruskannya
dataController
di:override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { ... }
Sekarang ketika kita ingin mengganti pengontrol data untuk tugas lain, kita dapat melakukannya di
AppDelegate
dan tidak perlu mengubah kode lain yang menggunakan pengontrol data.Ini tentu saja berlebihan jika kita hanya ingin meneruskan satu nilai. Dalam hal ini yang terbaik adalah menggunakan jawaban nhgrif.
Dengan pendekatan ini kita dapat memisahkan tampilan dari bagian logika.
sumber
Seperti yang ditunjukkan @nhgrif dalam jawabannya yang sangat bagus, ada banyak cara berbeda yang dapat digunakan VC (pengontrol tampilan) dan objek lain untuk berkomunikasi satu sama lain.
Data tunggal yang saya uraikan dalam jawaban pertama saya lebih banyak tentang berbagi dan menyelamatkan keadaan global daripada tentang berkomunikasi secara langsung.
Jawaban nhrif memungkinkan Anda mengirim informasi langsung dari sumber ke VC tujuan. Seperti yang saya sebutkan dalam balasan, itu juga memungkinkan untuk mengirim pesan kembali dari tujuan ke sumbernya.
Faktanya, Anda dapat mengatur saluran satu arah atau 2 arah yang aktif antara pengontrol tampilan yang berbeda. Jika pengontrol tampilan ditautkan melalui segmen storyboard, waktu untuk menyiapkan tautan berada dalam metode preparedFor Segue.
Saya memiliki proyek sampel di Github yang menggunakan pengontrol tampilan orang tua untuk menjadi tuan rumah 2 tampilan tabel yang berbeda sebagai anak-anak. Pengontrol tampilan anak ditautkan menggunakan segue sematan, dan pengontrol tampilan induk menyambungkan tautan 2 arah dengan setiap pengontrol tampilan dalam metode preparedForSegue.
Anda dapat menemukan proyek itu di github (tautan). Saya menulisnya di Objective-C, bagaimanapun, dan belum mengubahnya menjadi Swift, jadi jika Anda tidak nyaman dengan Objective-C, mungkin akan sedikit sulit untuk diikuti.
sumber
SWIFT 3:
Jika Anda memiliki storyboard dengan segues teridentifikasi, gunakan:
func prepare(for segue: UIStoryboardSegue, sender: Any?)
Meskipun jika Anda melakukan semuanya secara terprogram termasuk navigasi antara UIViewControllers yang berbeda, gunakan metode ini:
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool)
Catatan: untuk menggunakan cara kedua, Anda perlu membuat UINavigationController, Anda mendorong UIViewControllers, sebuah delegasi, dan harus menyesuaikan dengan protokol 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 } }
sumber
Itu tergantung kapan Anda ingin mendapatkan data.
Jika ingin mendapatkan data kapanpun Anda mau, bisa menggunakan pola singleton. Kelas pola aktif selama runtime aplikasi. Berikut adalah contoh pola singleton.
class AppSession: NSObject { static let shared = SessionManager() var username = "Duncan" } class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() print(AppSession.shared.username) } }
Jika Anda ingin mendapatkan data setelah tindakan apa pun, bisa menggunakan 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") }
sumber