首页 > 解决方案 > Swift IOS 问题从 MasterView 更新 DetailView 中的标签

问题描述

我第一次在stackoverflow上发帖,如果不对,请见谅

我在 XCode 中使用 Master/Detail View 模板。这是一个 BLT Central 应用程序,它会收到 BLE 设备上发生的事件的通知。我有一个主视图,这是使用更新的

    public  func    UpdateView() {
    tableView.beginUpdates()
    tableView.reloadRows(at: self.tableView.indexPathsForVisibleRows!, with: .none)
    tableView.endUpdates()
}

这工作正常,TableView 会在 BLE 通知时实时显示更新

但是,我还想实时更新详细视图,以防显示。

我通过以下方式在 DetailView 中链接了标签:

@IBOutlet   weak var detailDescription: UILabel!

当 MasterView 连接到 DetailView 时,它会更新得很好

但是,如果我在 BLE 通知到达时尝试更新标签,@IBOutlet detailDescription 已变为 nil,因此更新失败(标签未链接)

func UpdateDetail() {
    guard let label = detailDescription else {
        return; //Label not linked
    }
    label.text = "New Data"
}

UpdateDetail() 函数也在 viewDidLoad() 中使用,在这种情况下它工作正常

真正奇怪的是,如果我在 DetailView 中添加一个计时器来进行更新

    var timer : Timer? = nil

@objc func fireTimer() {
    DispatchQueue.main.async {
        self.UpdateDetail()
    }
}

它工作正常,因此它可能与从 Detail 类外部调用 UpdateDetail() 函数有关。

我已经通过向属性添加 didSet 来检查 detailDescription 引用是否被重置,并且在加载视图时仅调用一次

猜猜我可以使用计时器来解决问题,但我完全困惑为什么 detailItem 有时会显示为 nil,所以我会很感激进行健全性检查。

更新回到基础_______

所以我现在回到标准的 Apple Master->Detail 视图模板并添加了一个简单的计时器来更新列表。ListView 更新正常,但它仍然不会动态更新详细视图。在自己的班级之外。

我在加载 MainView 后使用 1 秒计时器,列表视图更新正常,但细节没有。

调试时,它进入 configureView() 很好,但在第一个 viewDidLoad() 之后 detailDescriptionLabel 为零

已经尝试了下面的所有建议,但是在每种情况下,在初始 Load 之后对标签的弱引用都是 nil

完全困惑

    @objc func doTimer() {
    for index  in 0..<objects.count {
        objects[index]=(objects[index] as! NSDate).addingTimeInterval(1)
    }
    tableView.beginUpdates()
    tableView.reloadRows(at: self.tableView.indexPathsForVisibleRows!, with: .none)
    tableView.endUpdates()
    DispatchQueue.main.async {
        self.detailViewController?.configureView()
    }
}

MasterViewController.swift 的完整代码在这里,其余与标准模板相同。

import UIKit

class MasterViewController: UITableViewController {

var detailViewController: DetailViewController? = nil
var objects = [Any]()



override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    navigationItem.leftBarButtonItem = editButtonItem

    let addButton = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(insertNewObject(_:)))
    navigationItem.rightBarButtonItem = addButton
    if let split = splitViewController {
        let controllers = split.viewControllers
        detailViewController = (controllers[controllers.count-1] as! UINavigationController).topViewController as? DetailViewController
    }
//Added timer here
    _ = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(doTimer), userInfo: nil, repeats: true)
}

//Update data in list
@objc func doTimer() {
    for index  in 0..<objects.count {
        objects[index]=(objects[index] as! NSDate).addingTimeInterval(1)
    }
    tableView.beginUpdates()
    tableView.reloadRows(at: self.tableView.indexPathsForVisibleRows!, with: .none)
    tableView.endUpdates()
    DispatchQueue.main.async {
        self.detailViewController?.configureView()
    }
}

override func viewWillAppear(_ animated: Bool) {
    clearsSelectionOnViewWillAppear = splitViewController!.isCollapsed
    super.viewWillAppear(animated)
}

@objc
func insertNewObject(_ sender: Any) {
    objects.insert(NSDate(), at: 0)
    let indexPath = IndexPath(row: 0, section: 0)
    tableView.insertRows(at: [indexPath], with: .automatic)
}

// MARK: - Segues

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "showDetail" {
        if let indexPath = tableView.indexPathForSelectedRow {
            let object = objects[indexPath.row] as! NSDate
            let controller = (segue.destination as! UINavigationController).topViewController as! DetailViewController
            controller.detailItem = object
            controller.navigationItem.leftBarButtonItem = splitViewController?.displayModeButtonItem
            controller.navigationItem.leftItemsSupplementBackButton = true
        }
    }
}

// MARK: - Table View

override func numberOfSections(in tableView: UITableView) -> Int {
    return 1
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return objects.count
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)

    let object = objects[indexPath.row] as! NSDate
    cell.textLabel!.text = object.description
    return cell
}

override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
    // Return false if you do not want the specified item to be editable.
    return true
}

override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
    if editingStyle == .delete {
        objects.remove(at: indexPath.row)
        tableView.deleteRows(at: [indexPath], with: .fade)
    } else if editingStyle == .insert {
        // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view.
    }
}

}

标签: iosswift

解决方案


我的答案基于提供的 Git 存储库。

在 MasterViewController - 您正在调用self.detailViewController?.configureView()但您从未分配详细控制器,因为它nil无法调用该configureView函数。你可以prepare(for segue: UIStoryboardSegue, sender: Any?)通过设置来做到这一点self.detailViewController = controller

这仍然无法帮助您更新标签值。

这样做的原因是因为@objc func doTimer() {您总是Date在数组中设置(覆盖)一个新对象(您可能的目标是更新相同引用的值)。因此,您分配给 detailViewController 的引用是不同的,并且您永远不会detailItem在计时器中更新。因此调用configureView不会做任何改变,因为值保持不变。


推荐阅读