ios - 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.
}
}
}
解决方案
我的答案基于提供的 Git 存储库。
在 MasterViewController - 您正在调用self.detailViewController?.configureView()
但您从未分配详细控制器,因为它nil
无法调用该configureView
函数。你可以prepare(for segue: UIStoryboardSegue, sender: Any?)
通过设置来做到这一点self.detailViewController = controller
这仍然无法帮助您更新标签值。
这样做的原因是因为@objc func doTimer() {
您总是Date
在数组中设置(覆盖)一个新对象(您可能的目标是更新相同引用的值)。因此,您分配给 detailViewController 的引用是不同的,并且您永远不会detailItem
在计时器中更新。因此调用configureView
不会做任何改变,因为值保持不变。
推荐阅读
- javascript - JS 字符串字面量
- javascript - 为什么空数组通过 if(array !== []) 检查
- sql - mariadb json中正确使用json_table
- python - 使用 python-crontab 的多个作业
- python - Flask URL 像这样“/api/hello”工作,但不像这样“/api/hello/”
- regex - 匹配浮点数或整数的正则表达式,可以是正数或负数
- html - 使用 get 方法将表单绑定到 GetMapping 控制器时出现问题
- python - 如果并行调用,Redis Pipeline 需要时间
- vue.js - vue-croppa:获取原始图像并用作另一个 Croppa 组件的初始图像
- swift - 自定义 UITextField 类中的 draw(_ rect: CGRect) 不要在 ios 14 上调用