标签: coordinator-pattern

iOS Swift Coordinator 模式和导航控制器的后退按钮

我正在使用模式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)

mvvm ios swift rx-swift coordinator-pattern

6
推荐指数
1
解决办法
5223
查看次数

SwiftUI 中的导航协调器

从历史上看,UIKit我们一直使用协调器模式来处理导航。

启动一个新的应用程序,SwiftUI但不清楚如何处理这个问题。

例如,我们当前的流程是:

  1. 应用程序启动
  2. 应用协调器启动
  3. 加载“开始场景”
  4. 开始场景检查身份验证状态并调用 App Coordinator 上的委托
  5. 应用协调器启动showHomeSceneshowAuthScene

就像是

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)

navigation ios swift coordinator-pattern swiftui

6
推荐指数
0
解决办法
2945
查看次数

SwiftUI MVVM 协调器/路由器/导航链接

我在将 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)

mvvm ios swift coordinator-pattern swiftui

6
推荐指数
1
解决办法
4172
查看次数

如何通过协调器模式将值从第二个视图控制器传递到第一个视图控制器

我想在我的 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)

ios swift coordinator-pattern

5
推荐指数
1
解决办法
1609
查看次数

“扩展不能包含存储的属性”阻止我重构代码

我有一个 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)

refactoring swift coordinator-pattern

5
推荐指数
3
解决办法
4952
查看次数

RxSwift 主题在调用时不触发事件

我有一个使用MVPCoordinator模式的应用程序。

当子协调器发送事件时,我希望AppCoordinator递归调用一个方法,该方法根据某些SessionState.

该应用程序的基本流程如下 -

AppCoordinator

  1. start()coordinateToRoot以初始状态调用
  2. 订阅showStartScene()启动子协调器

StartCoordinator

  1. start()创建MVP现在对用户可见的模块
  2. MVP模块调用AuthSvc对 iDP 进行异步调用并确认身份验证状态
  3. 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)

swift rx-swift publishsubject coordinator-pattern

5
推荐指数
1
解决办法
1934
查看次数

如何从Coordinator类更新EnvironmentObject变量?

当用户通过 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)

swift firebase-authentication coordinator-pattern swiftui

5
推荐指数
1
解决办法
1093
查看次数

协调器模式 - 使用Storyboard而不是Xib

这是我第一次参加协调员模式.虽然我已经意识到它的重要性,但我有一个主要的问题.
我在这个模式上经历了这篇惊人的文章.事实上,我可以使用它自己构建一个演示项目.但有一点 - 建议使用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)

xib ios xcode-storyboard swift coordinator-pattern

3
推荐指数
1
解决办法
846
查看次数

如何在 UIKit 上的 Xcode 11/iOS13 中使用协调器模式?

我一直在尝试通过使用故事板作为界面设计在 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 方法并没有帮助。有人可以在这里启发我吗?

谢谢!

ios appdelegate swift coordinator-pattern

2
推荐指数
1
解决办法
1436
查看次数

阻止 iOS 应用加载默认 Storyboard 入口点

我正在遵循有关协调器模式的教程,到目前为止,我已经完成了创建协调器并从应用程序委托运行 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)

xcode ios swift coordinator-pattern

1
推荐指数
1
解决办法
721
查看次数