gpt4 book ai didi

ios - 具有自定义配置的 UICollectionView 列表 - 如何将单元格中的更改传递给 View Controller ?

转载 作者:行者123 更新时间:2023-12-05 00:23:21 26 4
gpt4 key购买 nike

我已经实现了 UICollectionView自定义列表 UICollectionViewCellUIContentConfiguration使用新的 iOS 14 API。我一直在关注本教程:https://swiftsenpai.com/development/uicollectionview-list-custom-cell/ (与 Apple 的示例项目一起)
基本上你现在有一个 UICollectionViewCell , 一个 UIContentConfigurationUIContentView . cell只是设置它的配置,content configuration保存单元格及其所有可能状态的数据,content view是实际的 UIView替换 UICollectionViewCell.contentView .
我让它工作了,它非常棒而且干净。但是有一点我不明白:
您如何将回调添加到 UIContentView , 或将单元格中所做的更改(例如 UISwitch 切换或 UITextField 更改)传达给 viewController ? viewController 之间的唯一联系创建 collectionView 时,单元格在单元格注册内的数据来源:

// Cell
class Cell: UICollectionViewListCell {

var event: Event?
var onEventDidChange: ((_ event: Event) -> Void)?
//...
}


// Example cell registration in ViewController
let eventCellRegistration = UICollectionView.CellRegistration<Event.Cell, Event> { [weak self] (cell, indexPath, event) in
cell.event = event // Setting the data model for the cell
// This is what I tried to do. A closure that the cell calls, whenever the cell made changes to the event (the model)
cell.onEventDidChange = { event in /* update database */ }
}
这是我能想到的唯一可以放置这种连接的地方,如上面的示例所示。但是,这不起作用,因为单元格不再对其内容负责。这个闭包必须传递给 UIContentView这是为单元格创建实际 View 。
单元格与其内容 View 之间的唯一联系是内容配置,但不能将闭包作为属性,因为它们不相等。所以我无法建立连接。
有谁知道如何做到这一点?
谢谢!

最佳答案

如果您正在编写自己的配置,则您负责其属性。所以让你的配置定义一个协议(protocol)并给它一个delegate属性(property)!单元注册对象将 View Controller (或任何人)设置为配置的委托(delegate)。内容 View 配置 UISwitch 或任何向它发出信号的内容 View ,内容 View 将该信号传递给配置的委托(delegate)。
一个工作示例
这是一个工作示例的完整代码。我选择使用表格 View 而不是 Collection View ,但这完全不相关;内容配置适用于两者。
你需要做的就是在你的 View Controller 中放置一个 TableView ,使 View Controller 成为 TableView 的数据源,并使 TableView 成为 View Controller 的tableView。 .

extension UIResponder {
func next<T:UIResponder>(ofType: T.Type) -> T? {
let r = self.next
if let r = r as? T ?? r?.next(ofType: T.self) {
return r
} else {
return nil
}
}
}
protocol SwitchListener : AnyObject {
func switchChangedTo(_:Bool, sender:UIView)
}
class MyContentView : UIView, UIContentView {
var configuration: UIContentConfiguration {
didSet {
config()
}
}
let sw = UISwitch()
init(configuration: UIContentConfiguration) {
self.configuration = configuration
super.init(frame:.zero)
sw.translatesAutoresizingMaskIntoConstraints = true
self.addSubview(sw)
sw.center = CGPoint(x:self.bounds.midX, y:self.bounds.midY)
sw.autoresizingMask = [.flexibleTopMargin, .flexibleBottomMargin, .flexibleLeftMargin, .flexibleRightMargin]
sw.addAction(UIAction {[unowned sw] action in
(configuration as? Config)?.delegate?.switchChangedTo(sw.isOn, sender:self)
}, for: .valueChanged)
config()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func config() {
self.sw.isOn = (configuration as? Config)?.isOn ?? false
}
}
struct Config: UIContentConfiguration {
var isOn = false
weak var delegate : SwitchListener?
func makeContentView() -> UIView & UIContentView {
return MyContentView(configuration:self)
}
func updated(for state: UIConfigurationState) -> Config {
return self
}
}
class ViewController: UIViewController, UITableViewDataSource {
@IBOutlet var tableView : UITableView!
var list = Array(repeating: false, count: 100)
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.list.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
var config = Config()
config.isOn = list[indexPath.row]
config.delegate = self
cell.contentConfiguration = config
return cell
}
}
extension ViewController : SwitchListener {
func switchChangedTo(_ newValue: Bool, sender: UIView) {
if let cell = sender.next(ofType: UITableViewCell.self) {
if let ip = self.tableView.indexPath(for: cell) {
self.list[ip.row] = newValue
}
}
}
}
该示例的关键部分
好的,它可能看起来很多,但对于任何具有自定义内容配置的 TableView 来说,它主要是纯粹的样板。唯一有趣的部分是 SwitchListener 协议(protocol)及其实现,以及 addAction内容 View 的初始化程序中的行;这就是这个答案的第一段描述的东西。
因此,在内容 View 的初始化程序中:
sw.addAction(UIAction {[unowned sw] action in
(configuration as? Config)?.delegate?.switchChangedTo(sw.isOn, sender:self)
}, for: .valueChanged)
在扩展中,响应该调用的方法:
func switchChangedTo(_ newValue: Bool, sender: UIView) {
if let cell = sender.next(ofType: UITableViewCell.self) {
if let ip = self.tableView.indexPath(for: cell) {
self.list[ip.row] = newValue
}
}
}
另一种方法
该答案仍然使用协议(protocol)和委托(delegate)架构,并且 OP 宁愿不这样做。现代方法是提供一个属性,其值是一个可以直接调用的函数。
因此,我们没有给我们的配置一个委托(delegate),而是给它一个回调属性:
struct Config: UIContentConfiguration {
var isOn = false
var isOnChanged : ((Bool, UIView) -> Void)?
内容 View 的初始化程序配置接口(interface)元素,以便当它发出信号时, isOnChanged函数被调用:
sw.addAction(UIAction {[unowned sw] action in
(configuration as? Config)?.isOnChanged?(sw.isOn, self)
}, for: .valueChanged)
剩下的只是显示 isOnChanged功能是。在我的示例中,它与之前架构中的委托(delegate)方法完全相同。所以,当我们配置单元格时:
config.isOn = list[indexPath.row]
config.isOnChanged = { [weak self] isOn, v in
if let cell = v.next(ofType: UITableViewCell.self) {
if let ip = self?.tableView.indexPath(for: cell) {
self?.list[ip.row] = isOn
}
}
}

cell.contentConfiguration = config

关于ios - 具有自定义配置的 UICollectionView 列表 - 如何将单元格中的更改传递给 View Controller ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64049419/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com