【问题标题】:Access previous viewController element with its tag使用标签访问前一个 viewController 元素
【发布时间】:2019-03-06 13:34:48
【问题描述】:

在我的场景中,如果该按钮存在,我需要从前一个 viewController 访问具有特定标签的按钮。该按钮将位于重复使用的表格视图单元格中。

我想从当前视图更改该按钮的文本。我考虑过使用 NotificationCenter 发送数据,但可能有几个 viewController 与当前视图相连,所以这不是一个好方法。

试过一个

override func didMove(toParentViewController parent: UIViewController?) {
    super.didMove(toParentViewController: parent)

    if parent == self.navigationController?.parent {
        //check if previous viewController has the button and access it    
    }
}

有什么帮助吗?

【问题讨论】:

  • 您是否考虑过实现 prepareForSegue 并在此方法中传递此按钮?
  • 我已经在使用 prepareForSegue。你能告诉我如何在当前视图中使用传递的按钮吗?
  • @UtkuDalmaz 这正是发布的答案所做的,除了它不使用情节提要逻辑。我确实知道为什么当唯一一个做出回应的人在他的第一行就错了并且提供了一个沿着改变你的架构的方向的解决方案时,它为什么会被严重否决。你明白发布的答案了吗?
  • @RakeshaShastri 是的,我明白了。但我不知道为什么它有那么多反对意见
  • @UtkuDalmaz 即使我想知道是否有更好的方法。所以我会把我的答案留在那里,让有人向我解释为什么它是错误的。

标签: ios swift segue


【解决方案1】:

(此答案与应用架构无关,只是针对作者的问题发布一个简单的解决方案)

您说您的按钮代表模型(个人资料)的“关注”状态。您可能想要一个代表个人资料的模型:

class Profile {
    var following : Bool = false
}

您的第一个 ViewController 可能如下所示:

class ProfileListViewController : UIViewController, ProfileDetailsViewControllerDelegate {
    var profiles : [Profile] = [...]

    func userDidChangeProfileInfo(_ profile : Profile)() {
        (...)
    }
}

当您打开个人资料时,您会在 ProfileListViewController 中调用类似这样的内容:

func openProfileDetails(at indexPath: IndexPath) {
    let profile = profiles[indexPath.row]

    let detailsViewController = ProfileDetailsViewController.getInstance()
    detailsViewController.profile = profile
    detailsViewController.delegate = self

    self.navigationController?.pushViewController(detailsViewController, animated: true)
}

delegate 字段是一个看起来像这样的协议,并在上面的代码中实现:

protocol ProfileDetailsViewControllerDelegate : class {
    func userDidChangeProfileInfo(_ profile : Profile)
}

ProfileDetailsViewController:

class ProfileDetailsViewController : UIViewController {

   var profile: Profile? 

   weak var delegate : ProfileDetailsViewControllerDelegate?

   func didTapFollowButton() {
       profile.following = true
       delegate?.userDidChangeProfileInfo(profile)
   }
}

回到你的ProfileListViewControllerdelegate 方法将被调用,你可以重新加载你的行(如果你愿意,也可以重新加载整个 tableview):

func userDidChangeProfileInfo(_ profile : Profile)() {
    if let row = profiles.firstIndex(where: { $0 == profile }) {
        tableView.reloadRows(at: [IndexPath(row: row, section: 0)], with: .automatic)
    }
}

接下来将在此索引处重新创建单元格,因此将调用 cellForRowAt 方法。您可以根据模型的变化再次设置您的单元格(更改文本、样式、返回不同的单元格等,无论您的船漂浮并适合您的用例):

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    let cell = tableView.dequeueReusableCell(...)

    let profile = profiles[indexPath.row]

    if profile.following {
        cell.type = .following
    } else {
        cell.type = .notFollowing
    }

    return cell

}

单元格本身可能如下所示:

enum ProfileTableCellMode {
    case following
    case notFollowing
}

class ProfileTableCell : UITableViewCell {

    @IBOutlet weak var followButton : UIButton!

    var state: ProfileTableCellMode = .notFollowing { //default value
        didSet {
            onStateUpdated()
        }
    }

    func onStateUpdated() {
        switch state {

        case .following:
            followButton.setTitle("Unfollow", for: .normal)


        case .notFollowing:
            followButton.setTitle("Follow", for: .normal)

        }
    }

}

您也可以跳过所有的委派工作,直接在ProfileListViewController 中执行类似的操作:

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    self.tableView.reloadData()
}

所以当ProfileListViewController 重新成为顶级控制器时,整个表会重新加载。

这里最重要的是将 UI(用户界面)与状态(模型等)分开。 UI 应该根据状态呈现/更新自身,并且除了将“我被点击,请处理”传递给逻辑之外,不应处理任何业务逻辑。

【讨论】:

  • 天哪,我想我会选择 viewDidAppear 方式 :) 谢谢你的信息。
  • 是的,委派是更“清晰”的做事方式,但对于简单的用例,您可以根据状态重新加载所有内容。我的帖子中重要的是,等我更新帖子
  • 我应用了协议方式,效果很好。谢谢
猜你喜欢
  • 2013-11-23
  • 1970-01-01
  • 2012-07-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-16
  • 2014-12-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多