快速在标签查看的控制器之间传递数据?
Posted
技术标签:
【中文标题】快速在标签查看的控制器之间传递数据?【英文标题】:Passing data between tab viewed controllers in swift? 【发布时间】:2014-12-25 23:20:01 【问题描述】:它的作用
-
当标签栏控制器的第一页加载时,我从 json 文件中检索数据
我将它存储在一个数组中(在第一个视图控制器中)
获得的数据将显示在第二个视图控制器中。数据已加载并存储在第一个视图控制器的数组中。
问题:
我想不出一种在两个视图控制器之间传递数据的方法。无法根据 segue 标识符传递数据,因为它是一个标签栏控制器
请帮忙!
【问题讨论】:
Example using an unwind segue。不过,这仅在您从一个选项卡的后代导航到另一个选项卡的根目录时才有用。 ***.com/a/65499693/14414215 详细说明了我为我的项目所做的方式 【参考方案1】:如果您需要在视图控制器之间传递数据,那么:
var secondTab = self.tabBarController?.viewControllers[1] as SecondViewController
secondTab.array = firstArray
【讨论】:
它说 [AnyObject] 没有名为 subscript 的成员 如果标签页超过2个怎么办? @DaniSpringer 这是一种非常古老的处理方式。最好使用依赖注入和演示者来处理您的代码。当然,如果您想将其用于额外的: var thirdTab = self.tabBarController?.viewControllers[2] as ThirdViewControllerthirdTab.array = firstArray viewControllers[] 数组将所有 viewController 信息存储在其中,因此您可以从 tabController 内部访问 酷。我应该寻找什么新的方法来做到这一点? 请检查 RxSwift 以检查更好的方法和反应式编程。【参考方案2】:正如伍德斯特在上面的回答中所建议的那样,我最终使用了单例。
在 Swift 3 中
新建一个swift文件并创建一个类:
class Items
static let sharedInstance = Items()
var array = [String]()
在您的任何视图控制器中,您都可以像这样访问您的数组:
Items.sharedInstance.array.append("New String")
print(Items.sharedInstance.array)
【讨论】:
在发现这个问题被讨论了 1/2 次之后 - 在堆栈和其他地方,以及解决它的方法有很多不同的答案 - 这是最好的清晰和简洁的答案/问题/要求的解决方案。这是一个很好的干净架构,符合 Model View Presenter 等设计模式。如果可以在人们询问如何执行此操作时帮助集中流量,我想对答案进行 5 次投票。非常感谢您分享在我看来是最佳实践的东西......也就是正确的方法 - 解决这个要求 我很高兴能帮上忙 :) 更多信息在这里。 krakendev.io/blog/the-right-way-to-write-a-singleton 我不认为这会引入“良好的干净架构”。在代码库中的任何时候共享一个可访问的全局状态对我来说听起来像是一场潜在的灾难。甚至链接帖子的作者 (krakendev.io/blog/the-right-way-to-write-a-singleton) 也表示“将这篇帖子视为我不认可使用单例共享状态也很重要”。【参考方案3】:H. Serdar 的代码示例是正确的,这是访问另一个选项卡的视图控制器并为其提供数据的方式。
请记住,当您在 Swift 中传递数组时,you're passing it by value 与 Objective-C 通过引用传递它不同。这意味着您的第二个视图控制器所做的更改不会反映在您的第一个视图控制器中,因为您的第二个视图控制器使用的是数组的副本,而不是同一个数组。如果您希望两个视图控制器修改同一个数组,请将数组放入一个类中,然后传递该类的单个实例。
其他一些注意事项: 您可以对 TabBarController 进行子类化,为其提供一个存储数据的属性,并且所有选项卡都可以使用:
if let tbc = tabBarController as? YourCustomTabBarSubclass
println("here's my data \(tbc.array)")
在这种情况下,您会从多个选项卡访问同一个数组,因此一个选项卡中的更改会反映在其他地方。
我建议不要将您的 App Delegate 用作存储数据的集中位置。这不是应用程序委托的目的。其目的是处理应用程序对象的委托调用。
视图控制器应该有所有数据,封装在其中,他们需要完成他们的工作。它们与其模型数据(例如您的数组,或对数据库或托管对象上下文的引用)有连接,而不是让视图控制器通过遍历视图控制器图或进入委托甚至访问另一个对象使用全局变量。这种模块化、自包含的视图控制器结构让您可以在不同设备上重新构建应用程序以实现相似但独特的设计,例如在一台设备(如 iPad)上的弹出窗口中显示视图控制器,并在另一台设备上全屏显示,例如一部 iPhone。
【讨论】:
【参考方案4】:SWIFT 3
在你的第一个视图控制器中,像往常一样声明你的变量(在你的例子中是一个数组)。
在您的第二个视图控制器中,执行以下操作:
var yourVariable: YourVariableClass
get
return (self.tabBarController!.viewControllers![0] as! FirstViewControllerClass).yourVariable
set
(self.tabBarController!.viewControllers![0] as! FirstViewControllerClass).yourVariable = newValue
这是有效的,因为在 tabbarcontroller 中,选项卡项后面的所有视图控制器都已初始化。通过在您的第二个视图控制器中执行此操作,您实际上是从/在第一个视图控制器中获取/设置变量。
【讨论】:
【参考方案5】:对于 Xcode 11 & Swift 5 + Storyboard + 依赖注入方法
假设您使用的是故事板,这是我设计的一种方法。
第 1 步:
像我在下图中所做的那样,在您的 tabBarController 上放置一个标识符。
第 2 步:
在 scenedelegate.swift 文件(不是 appDelegate.swift)中,将以下代码添加到相应的 func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions)
方法中。
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions)
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
self.window = self.window ?? UIWindow()//@JA- If this scene's self.window is nil then set a new UIWindow object to it.
//@Grab the storyboard and ensure that the tab bar controller is reinstantiated with the details below.
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let tabBarController = storyboard.instantiateViewController(withIdentifier: "tabBarController") as! UITabBarController
for child in tabBarController.viewControllers ?? []
if let top = child as? StateControllerProtocol
print("State Controller Passed To:")
print(child.title!)
top.setState(state: stateController)
self.window!.rootViewController = tabBarController //Set the rootViewController to our modified version with the StateController instances
self.window!.makeKeyAndVisible()
print("Finished scene setting code")
guard let _ = (scene as? UIWindowScene) else return
您会注意到 scenedelgate.swift 文件有一个成员变量; var window: UIWindow?
。 曾经是 appDelegate 的一部分,但在 xCode 11 和 Swift 5 中进行了更改,因此许多类似的答案和教程都将过时。
代码中显示storyboard.instantiateViewController(withIdentifier:
的部分您需要为参数添加您使用的名称。在我的屏幕截图中,您会看到我将其命名为 tabBarController。
为了使这个函数在任何类型的 viewController 上都可以工作,而不必在索引上分别实例化每个,我使用了一种称为 StateControllerProtocol 的协议策略。接下来我们将创建它以及将保存全局变量的 StateController。
第 3 步:
在 stateController.swift 或任何你想命名的文件中,添加以下代码删除不适用于你的项目的方面。
import Foundation
struct tdfvars
var lateBED:Double = 0.0
var acuteBED:Double = 0.0
var rbe:Double = 1.4
var t1half:Double = 1.5
var alphaBetaLate:Double = 3.0
var alphaBetaAcute:Double = 10.0
var totalDose:Double = 6000.00
var dosePerFraction:Double = 200.0
var numOfFractions:Double = 30
var totalTime:Double = 168
var ldrDose:Double = 8500.0
//@JA - Protocol that view controllers should have that defines that it should have a function to setState
protocol StateControllerProtocol
func setState(state: StateController)
class StateController
var tdfvariables:tdfvars = tdfvars()
您希望在视图之间共享的变量我建议添加到结构中。我将我的 tdfvariables 命名为,但您需要将其命名为与您的项目相关的名称。注意这里定义的protocol。这是一个将作为扩展添加到每个 viewController 的协议,它定义应该有一个函数来设置其 stateController 成员变量(我们还没有定义,但会在后面的步骤中定义) .
第 4 步:
在我的例子中,我有 2 个由 tabBarController 控制的视图。 StandardRegimenViewController 和 settingsViewController。这是您要为 viewControllers 添加的代码。
import UIKit
class SettingsViewController: UIViewController
var stateController: StateController?
override func viewDidLoad()
super.viewDidLoad()
//@JA - This adds the stateController variable to the viewController
extension SettingsViewController: StateControllerProtocol
func setState(state: StateController)
self.stateController = state
此处的扩展将协议添加到您的类,并根据它的要求添加我们之前在 stateController.swift 文件中定义的函数。这就是最终将 stateController 及其结构值放入您的 viewController 的内容。
第 5 步:
使用 stateController 访问您的变量!你完成了!
以下是我如何在我的一个控制器中执行此操作的一些示例。
stateController?.tdfvariables.lateBED = 100
你可以用同样的方式读取变量!这种方法的优点是您不使用单例,而是 依赖注入 用于您的 viewController 和其他任何可能需要访问您的变量的东西。阅读有关依赖注入的更多信息以了解与单例相比的优势以了解更多信息。
【讨论】:
【参考方案6】:我的应用程序中有一个选项卡式视图控制器,我对多个选项卡视图使用相同的数组。我通过在任何类之外(在 import UIKit 和类声明之间的行中)声明数组来实现这一点,因此它本质上是每个视图都可以访问的全局变量。你试过吗?
【讨论】:
你是如何从其他类访问它的? 我只是像调用其他已声明的变量一样调用它。 为什么这个答案没有竖起大拇指?这是不好的做法,因为它似乎是这里最好/最简单的解决方案? 不要这样做哈哈 全局变量是不好的做法。它不利于架构、维护和安全。除其他原因外。【参考方案7】:您可以覆盖tabBar(didSelect:)
方法,然后在UITabViewController 上索引ViewControllers 数组,并将ViewController 转换为所需的自定义ViewController。不需要共享可变状态以及随之而来的所有问题。
class SecondViewController: UIViewController
var array: [Int] = []
override func viewDidLoad()
super.viewDidLoad()
class TabViewController: UITabBarController
override func tabBar(
_ tabBar: UITabBar,
didSelect item: UITabBarItem
)
super.tabBar(tabBar, didSelect: item)
var secondTab = viewControllers?[1] as? SecondViewController
secondTab?.array = [1, 2, 3]
【讨论】:
以上是关于快速在标签查看的控制器之间传递数据?的主要内容,如果未能解决你的问题,请参考以下文章
如何在两个视图控制器之间的快速应用程序中传递 json 数据