我正在使用模式MVVM+Coordinator。我的每个控制器都是由coordinators. 但是,当点击Navigation控制器的后退按钮时,停止我的协调器的正确方法是什么?
class InStoreMainCoordinator: NavigationCoordinatorType, HasDisposeBag {
let container: Container
enum InStoreMainChildCoordinator: String {
case menu = "Menu"
case locations = "Locations"
}
var navigationController: UINavigationController
var childCoordinators = [String: CoordinatorType]()
init(navigationController: UINavigationController, container: Container) {
self.navigationController = navigationController
self.container = container
}
func start() {
let inStoreMainViewModel = InStoreMainViewModel()
let inStoreMainController = InStoreMainController()
inStoreMainController.viewModel = inStoreMainViewModel
navigationController.pushViewController(inStoreMainController, animated: true)
}
}
Run Code Online (Sandbox Code Playgroud) 从历史上看,UIKit我们一直使用协调器模式来处理导航。
启动一个新的应用程序,SwiftUI但不清楚如何处理这个问题。
例如,我们当前的流程是:
showHomeScene或showAuthScene就像是
final class AppCoordinator: BaseCoordinator {
private(set) var navigationController: UINavigationController?
init(navigationController: UINavigationController?) {
self.navigationController = navigationController
}
override func start() {
showStartScene()
}
private func showStartScene() {
let configurator = StartConfigurator()
let viewController = configurator.create(self)
navigationController?.setViewControllers([viewController], animated: false)
}
private func showHomeScene() {
let coordinator = HomeCoordinator(self, navigationController: navigationController)
store(coordinator: coordinator)
coordinator.start()
}
private func showAuthScene() {
let coordinator = …Run Code Online (Sandbox Code Playgroud) 我在将 UIKit 架构模式转换为 SwiftUI 时遇到问题。我目前的模式主要是带有协调器/路由器的 MVVM。添加@ObservableObject/@Published 后,MVVM 部分看起来非常简单和自然。但是协调/路由似乎不直观。视图和协调(导航)功能在 SwiftUI 中紧密耦合。似乎不可能将它们与使用 helper struct 分开AnyView。
这是一个示例:我想在 SwiftUI 中创建一个可重用的行/单元格。假设生产中的这一行非常复杂,因此我想重用它。我也想把它放在另一个模块中,这样我就可以在多个目标中重用它。(如 iOS、macCatalyst 等...)
现在我想控制当用户点击该视图或该视图中的按钮时会发生什么。根据上下文,我需要导航到不同的目的地。据我所知,可能的 NavigationLink 目标要么硬连线到视图中,要么AnyView传递到视图中。
这里有一些示例代码。此单元格/行包含两个按钮。我想导航到其他一些取决于上下文的视图,而不是硬连接到代码中:
struct ProductFamilyRow: View {
@State private var selection: Int? = 0
let item: ProductFamilyItem
let destinationView1: AnyView
let destinationView2: AnyView
var body: some View {
VStack {
NavigationLink(
destination: destinationView1,
tag: 1,
selection: self.$selection
) {
EmptyView()
}
NavigationLink(
destination: destinationView2,
tag: 2,
selection: self.$selection
) {
EmptyView()
}
HStack {
Text(item.title) …Run Code Online (Sandbox Code Playgroud) 我想在我的 iOS 应用程序中使用 Soroush Khanlou 协调器模式 ( http://khanlou.com )。我的问题是如何通过协调器将数据传递回我的第一个视图控制器?在使用协调器模式之前,我使用委托(协议)将数据从第二个视图控制器传递回第一个视图控制器,因为我的第一个视图控制器负责创建和呈现第二个视图控制器,这不是一个好的解决方案。因此,我决定使用协调器从视图控制器中删除应用程序导航的工作。这是场景:
class FirstViewController: UIViewController {
weak var coordinator: MainCoordinator?
func showSecondViewController(data: Data) {
coordinator?.presentSecondViewController(data: data) {[weak self] in
guard let self = self else { return }
//Do something
}
}
class MainCoordinator: NSObject, Coordinator {
var childCoordinators = [Coordinator]()
var navigationController: UINavigationController
init(navigationController: UINavigationController) {
self.navigationController = navigationController
}
func start(completion: (() -> Void)?) {
navigationController.delegate = self
let firstViewController = FirstViewController.instantiate()
firstViewController.coordinator = self
navigationController.pushViewController(firstViewController, animated: false)
}
func presentSecondViewController(data: …Run Code Online (Sandbox Code Playgroud) 我有一个 13 行的 func,它在我的应用程序中的每个 ViewController 中重复,在整个项目中总共有 690 行代码!
/// Adds Menu Button
func addMenuButton() {
let menuButton = UIButton(frame: CGRect(x: 0, y: 0, width: 30, height: 30))
let menuImage = UIImage(named: "MenuWhite")
menuButton.setImage(menuImage, for: .normal)
menuButton.addTarget(self, action: #selector(menuTappedAction), for: .touchDown)
self.navigationItem.rightBarButtonItem = UIBarButtonItem(customView: menuButton)
}
/// Launches the MenuViewController
@objc func menuTappedAction() {
coordinator?.openMenu()
}
Run Code Online (Sandbox Code Playgroud)
要使 menuTappedAction 函数正常工作,我必须像这样声明一个弱变量:
extension UIViewController {
weak var coordinator: MainCoordinator?
Run Code Online (Sandbox Code Playgroud)
但是通过这样做,我得到了错误Extensions must not contain stored properties
到目前为止我尝试过的:
1)删除weak关键字将导致我所有应用程序中的冲突。2)这样声明:
weak var coordinator: MainCoordinator? …Run Code Online (Sandbox Code Playgroud) 我有一个使用MVP该Coordinator模式的应用程序。
当子协调器发送事件时,我希望AppCoordinator递归调用一个方法,该方法根据某些SessionState.
该应用程序的基本流程如下 -
AppCoordinator
start()coordinateToRoot以初始状态调用showStartScene()启动子协调器StartCoordinator
start()创建MVP现在对用户可见的模块MVP模块调用AuthSvc对 iDP 进行异步调用并确认身份验证状态AppCoordinator完成此任务后,发布一个事件,该事件由的方法中的订阅拾取coordinateToRoot,并使用视图状态的适当协调器重复该循环。然而问题是,在该事件发布后,什么也没有发生。start()没有显示它收到了事件并且coordinateToRoot不会再次被调用。
我在下面创建了最基本的版本来演示这一点。我还硬编码showStartScene为返回.signedIn而不是查找身份验证状态。
在下面的示例中,我希望一旦加载视图,presenter.signal应该立即发出一个导致打印语句显示的事件。
会话状态
enum SessionState: String {
case unknown, signedIn, signedOut
}
Run Code Online (Sandbox Code Playgroud)
应用程序协调员
final class AppCoordinator: BaseCoordinator<Void> {
private let window: UIWindow
init(window: UIWindow) {
self.window = window
}
override func start() -> Observable<Void> { …Run Code Online (Sandbox Code Playgroud) 当用户通过 FB 登录按钮完成授权时,我使用 Coordinator(符合 LoginButtonDelegate)对象来接收用户数据(配置文件、名称)。Coordinator().userId 属性使用用户 ID 进行更新,但我需要将其向上传递 1 级到 LoginView 并更新名为 thisSession 的环境对象(以某种方式使 thisSession.userId = Coordinator().userId )。
有什么办法可以做到这一点吗?我尝试使用 ObservableObject/Published 属性,但无法从协调器更新父对象的属性。
另一个想法是订阅 Auth.auth() 更改,但它似乎太复杂并且有点“老派”解决方案。我缺少一些简单的方法。
有什么提示/想法吗?
import SwiftUI
import FirebaseCore
import FirebaseAuth
import FBSDKLoginKit
import FBSDKCoreKit
struct LoginView: View {
@EnvironmentObject var thisSession: CurrentSession
@ObservedObject var mainData = MainViewModel()
var facebookView = facebook()
var body: some View {
VStack {
facebookView.frame(width: 240, height: 50)
Text("\(self.thisSession.userId ?? "none")")
}
}
}
struct LoginView_Previews: PreviewProvider {
static var previews: some View {
LoginView().environmentObject(CurrentSession()) …Run Code Online (Sandbox Code Playgroud) 这是我第一次参加协调员模式.虽然我已经意识到它的重要性,但我有一个主要的问题.
我在这个模式上经历了这篇惊人的文章.事实上,我可以使用它自己构建一个演示项目.但有一点 - 建议使用Xib.并不是唯一提到故事板不能使用,但通过这些线条到文章末尾,让我想到:
强大的力量带来了巨大的责任(和限制).要使用此扩展,您需要为每个UIViewController创建单独的故事板.故事板的名称必须与UIViewController类的名称匹配.必须将此UIViewController设置为此故事板的初始UIViewController.
有人提到,在Storyboard的情况下,我们应该创建一个扩展并将其用于UIViewController:
extension MyViewController: StoryboardInstantiable {
}
Run Code Online (Sandbox Code Playgroud)
故事板可实现:
import UIKit
protocol StoryboardInstantiable: NSObjectProtocol {
associatedtype MyType // 1
static var defaultFileName: String { get } // 2
static func instantiateViewController(_ bundle: Bundle?) -> MyType // 3
}
extension StoryboardInstantiable where Self: UIViewController {
static var defaultFileName: String {
return NSStringFromClass(Self.self).components(separatedBy: ".").last!
}
static func instantiateViewController(_ bundle: Bundle? = nil) -> Self {
let fileName …Run Code Online (Sandbox Code Playgroud) 我一直在尝试通过使用故事板作为界面设计在 Xcode 11.2 上创建一个新应用程序来学习协调器模式。
我关注了 Paul Hudson 的这个视频,但是当需要将代码添加到 AppDelegate.swift 文件时,我在第 12 分钟卡住了。就像应用程序将启动一样,第一个视图控制器将显示但不会导航。
我应该更改什么,或者更好的是,我应该将当前代码移到哪里以使其工作?
整个项目可以在这里找到。
简而言之,iOS 12 及之前在 AppDelegate 中的代码是这样的:
var coordinator: MainCoordinator?
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let navController = UINavigationController()
coordinator = MainCoordinator(navigationController: navController)
coordinator?.start()
window = UIWindow(frame: UIScreen.main.bounds)
window?.rootViewController = navController
window?.makeKeyAndVisible()
return true
}
Run Code Online (Sandbox Code Playgroud)
我已经看到现在window在 SceneDelegate 中,但是将那里的所有内容移动到 sceneDidConnect 方法并没有帮助。有人可以在这里启发我吗?
谢谢!
我正在遵循有关协调器模式的教程,到目前为止,我已经完成了创建协调器并从应用程序委托运行 start() 的过程。但是在该协调器上调用函数不起作用,因为协调器 var 突然为零。看起来显示的初始视图控制器不是来自 coordinator.start() 的控制器,而是来自故事板入口点的控制器。我确实在项目目标的主界面中禁用了 Main。
应用程序委托:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
if let registry = DependencyResolver.shared as? DependencyRegistry {
DependencyGraph.setup(for: registry)
}
let navController = UINavigationController()
coordinator = MainCoordinator(navigationController: navController)
coordinator?.start()
window = UIWindow(frame: UIScreen.main.bounds)
window?.rootViewController = navController
window?.makeKeyAndVisible()
return true
}
Run Code Online (Sandbox Code Playgroud)
主要协调员:
class MainCoordinator: Coordinator {
var navigationController: UINavigationController
var childCoordinators = [Coordinator]()
init(navigationController: UINavigationController) {
self.navigationController = navigationController
}
func start() {
let vc = InitViewController.instantiate()
vc.coordinator …Run Code Online (Sandbox Code Playgroud) swift ×10
ios ×7
swiftui ×3
mvvm ×2
rx-swift ×2
appdelegate ×1
navigation ×1
refactoring ×1
xcode ×1
xib ×1