【问题标题】:Code structure for iOS with Swift, what goes where, all code?带有 Swift 的 iOS 的代码结构,什么去哪里,所有代码?
【发布时间】:2016-05-31 12:29:23
【问题描述】:

因此,我是 iOS 新手,但对 Android 经验丰富,我开始掌握大部分内容的要点,但我很难理解去哪里,为什么会这样......

当你只使用故事板时,很容易实现 MVC 模式(或任何其他视图分离模式),但当你用代码编写所有内容时,我觉得它变得有点混乱。

假设我有一个 ViewController,其父 View 包含子 View,它可能又包含子 View。现在我应该在哪里创建孩子?在 ViewController 中(最方便)还是在父 View 中(最近)?

如果我使用 ViewController 我有参考,我会很容易制作插座等,但是 ViewController 是否也应该做一些事情,比如 setTitle、setImage、背景等等等?这绝对是最简单的解决方案。缺点是 View 只是简单的对象,这会导致 ViewController 变得臃肿。

如果我使用 View,我将很难将 outlets 返回给 ViewController,而 ViewController 最终几乎什么都不做。

Apple 也没有太大帮助,官方 FoodTracker 教程显示了 ViewController 和 Views using outlets 等。

我现在的基本结构:

updateLayoutStates()
setupLayoutPositions()
updateLayoutPositions()

...

/// Setups the initial constraints for all views
func setupLayoutPositions() {

    // add views by order of appearances
    addSubview(languageBtn)
    addSubview(playBtn)
    addSubview(menuBtn)
    //addSubview(barScrollOverlayView)
    addSubview(barScrollView)
    addSubview(collapseBtn)

    // add bar scroll inner subviws by order of appearance
    barScrollView.addSubview(barScrollContentView)

    barScrollContentView.addSubview(speedContainerView)
    barScrollContentView.addSubview(speedProgress)
    barScrollContentView.addSubview(readingStratBtn)
    // More code that adds constraints etc. etc.

...

/**
 Updates all views based on the current status of the bar
 */
func updateLayoutStates() {
    print("Bar updateLayoutStates")

    // setup base layout
    // setup permanent items, listed by appearance
    languageBtn.backgroundColor = UIColorFromHex(Constants.Colors.dark_blue, alpha: 1)
    languageBtn.setTitle("lang_da".localized, forState: .Normal)
    languageBtn.postSetup()
    languageBtn.setTitleColor(UIColorFromHex(Constants.Colors.white, alpha: 1), forState: .Normal)

    playBtn.setImage(UIImage(named: "Play"), forState: .Normal)
    playBtn.setImage(UIImage(named: "PlayActive"), forState: .Highlighted)
    playBtn.setTitle("label_play_key".localized, forState: .Normal)
    playBtn.postSetup()

   ...

/// updates the positions of all layouts based on the current status of the bar
func updateLayoutPositions() {
    if currentBarState == BarState.EnabledStandardExpanded {
        self.removeConstraint(collapseLeftConstraint)
        self.addConstraint(collapseRightConstraint)
    } else if currentBarState == BarState.EnabledStandardCollapsed {
        self.removeConstraint(collapseRightConstraint)
        self.addConstraint(collapseLeftConstraint)
    }
}

非常感谢所有带有示例的解释,如果我不够清楚,我很乐意进一步解释。

【问题讨论】:

    标签: ios swift model-view-controller


    【解决方案1】:

    一方面,您在谈论良好的架构。另一方面,您需要一些有关特定视图结构的帮助。

    1) 对于第一部分,您有很多资源。 DJohnsen 已经提到了 MVVM 和 VIPER,我认为这对于 Google 来说是一个很好的搜索字符串。 我认为熟悉建筑主题总是好的。每种架构都有权衡取舍。 一个很好的起点是 Bobs 叔叔的“清洁代码”。

    由此产生了许多想法。

    2)关于您的视图问题:

    如果您有嵌套的视图层次结构,考虑 视图控制器包含 可能是个好主意。只要您看到,您的视图由许多其他视图组成,并且变得更加复杂,您可能希望拆分组件。 您总是需要考虑您要解决的具体问题:

    • 您只想显示一个标签和一个按钮吗?将它们直接添加到视图控制器并连接出口或设置目标/动作。

    • 您是否有一个带有标题、自定义子视图、可能还有 tableView 等的复杂视图?使用子视图控制器

    但是要分解它:

    视图控制器和视图彼此非常接近。 在 iOS 中,我开始通过以下方式处理视图和视图控制器:

    • 视图控制器应该是哑的。它们的唯一目的是管理子控制器或设置一些视图的属性。视图控制器还管理高级屏幕处理(设备旋转、状态栏处理等)并与表示层对话。

    • 视图比较笨,应该只设置子视图、布局(设置布局约束)并提供一些自定义方法(可以由视图控制器使用)。

    这听起来有点难:但我认为这对初学者来说是一个很好的指南。过一段时间你就会舒服了。

    您提出的问题很好,但您会在一段时间后自己回答。我认为最重要的是您应该只将视图特定代码放入视图控制器和视图中。不多了。

    其余的一切很快升级为宗教战争:)

    干杯
    奥兰多?

    【讨论】:

    • 谢谢,我开始对一切有了更好的理解。我也意识到对此没有一个答案:)。从 Android 的角度来看,我猜 ViewController 类似于 Fragment。在 Android 中,只要你在代码中而不是在 xml 中做 UI,你也可以随心所欲地搞砸一切。
    • 是的。可以说 ViewController 就像 Android 中的 Activity 或 Fragment。我希望您理解“视图控制器包含”的含义。如果没有:developer.apple.com/library/ios/featuredarticles/…
    • 是的,这已经是我决定采用的方法了。我从一个越来越复杂的简单视图开始,此时我意识到它是一个子视图控制器:) 但正如你所说,我们仍然可以这样编码,这不是一个好方法。
    【解决方案2】:

    有很多方法/模式可以解决这个问题并避免视图控制器膨胀。在看了 MVC、VIPER、MVVM 和许多其他之后,我非常喜欢 Clean Swift 的实现。它支持单次使用函数,依赖注入,并且保持视图控制器非常干净。

    虽然起初它似乎有点矫枉过正(对于非常小的项目来说可能是这样)——我最近重构了一个 OS X 项目,该项目变得越来越笨拙。令人惊讶的是,现在实现新功能、隔离错误和重用代码是多么简单。可能值得一试?

    坏消息 - 如果您询问一千个开发人员对此的意见,您将获得至少一千个意见。

    好消息 - 该平台对所有人来说都足够灵活!

    Apple 在这里提供了 MVC 指南,https://developer.apple.com/library/ios/documentation/General/Conceptual/DevPedia-CocoaCore/MVC.html,我会告诉你我的看法:

    通过一个非常基本的应用程序示例来选择和安排会议室:

    型号 如您所知,模型代表数据结构。我为每个结构/类保留 1 个文件,并且我将代码保留为仅代表数据(而不是业务逻辑)。我的会议室模型可能类似于:

    struct ConferenceRoom {
        let maxOccupancy: Int
        let roomNumber: Int
        let hasProjector: Bool
        var reservations: 
    }
    

    控制器 我尝试将业务逻辑保留在控制器中(不要与 ViewControllers 混淆)。例如:

    class ConferenceRoomController {
      var conferenceRooms = [ConferenceRoom]()
    
      init() {
        ...
      }
    
      func retrieveConferenceRoomsFromDatastore() -> [ConferenceRoom] {
        ...
      }
    
      func reserveConferenceRoom(roomNumber: Int, startDate: NSDate, endDate: NSDate) -> Bool {
        ...
    
      }
    
      func findRoomsForDateWithCapacity(capacity: Int, date: NSDate) -> [ConferenceRoom] {
        ...
      }
    }
    

    查看 这通常是一个 View/ViewController。在 IOS 中,ViewcController 既是视图,又是用于管理视图的控制器,这对很多人来说是一个困惑点,而且因为它基本上可以做任何事情,所以在这里结束了太多的业务逻辑或其他代码(膨胀)。我尝试只在此处放置与显示数据相关的功能(而不是显示的内容 - 那是控制器)。如果我以编程方式向视图添加子视图,我也会在此处执行此操作。

    class ConferenceRoomScheduler: UIViewController {
      @IBOutlet var numberOfParticipants: UITextField!
      @IBOutlet var reservationDate: UITextField!
      @IBOutlet var tableView: UITableView!
      @IBOutlet var submitButton: UIButton!
    
      let conferenceRoomController = ConferenceRoomController()
    
      @IBAction func submitButtonPressed(sender: UIButton) {
        let participants = Int(numberOfParticipants.text!)
        if let participants = participants {
          conferenceRoomController.findRoomsForDateWithCapacity(participants, date: NSDate())
        } else {
          print("Not a number")
        }
      }
    }
    

    这些类并不完整,但希望能提供一些示例来说明将什么放在哪里。我发现这些类型的决策会随着您为平台开发而变得更加舒适而发展。我同意 - 一些模式,例如 Clean-Swift 似乎你会花很多时间“管道”,如果你不熟悉具体的实现,你可能会这样做。例如,当我开始 IOS 开发时,我可以花几个小时来组装一个简单的 tableView,它看起来很复杂。现在,在完成了太多次之后,一个功能齐全且功能超出基础的 tableView 只需几分钟即可设置。应用程序架构对我来说也是一样的。 Clean-Swift(和其他)似乎不再那么繁重了(而且好处超过了最初感知的复杂性)。

    【讨论】:

    • 是的,这一切听起来都很棒,但是通过将所有内容分离到用例中,我将花费大量时间进行填充。这似乎比 MVVM 模式更有效。但我的问题实际上比这更简单。我的问题只是我何时将功能代码放入视图中,何时将其放入 ViewController,是否有任何指导方针?我提供的上述代码从一个视图开始,后来被移到了 ViewController。两种方式都可以,但首选的是什么?
    • @DJohnson:我对 clean-swift.com 上的概念有一个问题。作者用VC -> I -> P -> VC设置循环依赖。我认为从视图控制器直接依赖于交互器是不好的。这意味着视图控制器知道业务逻辑接口。从我的角度来看,VC 应该只传达用户触发了任何事件并且它会从表示层推送任何结果......但我不想在这里开始一场激烈的战争;)
    猜你喜欢
    • 2021-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-04
    相关资源
    最近更新 更多