具有自定义配置的 UICollectionView 列表 - 如何将单元格中的更改传递到视图控制器?

2024-04-06

我已经实现了UICollectionView自定义列表UICollectionViewCell and UIContentConfiguration使用新的iOS 14API。我一直在关注这个教程:https://swiftsenpai.com/development/uicollectionview-list-custom-cell/ https://swiftsenpai.com/development/uicollectionview-list-custom-cell/(以及 Apple 的示例项目)

基本上你现在有一个UICollectionViewCell, a UIContentConfiguration and a UIContentView. The 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这正在为单元格创建实际的视图。

单元格与其内容视图之间的唯一联系是内容配置,但不能将闭包作为属性,因为它们不相等。所以我无法建立连接。

有谁知道如何做到这一点?

Thanks!


如果您编写自己的配置,则您负责其属性。因此,让您的配置定义一个协议并给它一个delegate财产!单元格注册对象将视图控制器(或任何人)设置为配置的委托。内容视图配置 UISwitch 或向其发送信号的任何内容,内容视图将该信号传递给配置的委托。

一个工作示例

这是complete工作示例的代码。我选择使用表视图而不是集合视图,但这完全无关;内容配置适用于两者。

您需要做的就是将表视图放入视图控制器中,使视图控制器成为表视图的数据源,并使表视图成为视图控制器的数据源。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
            }
        }
    }
}

该示例的关键部分

好吧,它可能看起来很多,但它主要是任何具有自定义内容配置的表视图的纯样板。唯一有趣的部分是 SwitchListener 协议及其实现,以及addAction内容视图初始化程序中的行;这就是这个答案第一段描述的内容。

因此,在内容视图的初始值设定项中:

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
        }
    }
}

另一种方法

这个答案仍然使用协议和委托架构,OP 宁愿不这样做。现代的方法是提供一个属性,其值是一个可以调用的函数directly.

因此,我们没有给我们的配置一个委托,而是给它一个回调属性:

struct Config: UIContentConfiguration {
    var isOn = false
    var isOnChanged : ((Bool, UIView) -> Void)?

内容视图的初始化程序配置界面元素,以便当它发出信号时,isOnChanged函数被称为:

sw.addAction(UIAction {[unowned sw] action in
    (configuration as? Config)?.isOnChanged?(sw.isOn, self)
}, for: .valueChanged)

它仍然只是为了显示什么isOnChanged功能is。在我的示例中,它与之前架构中的委托方法完全相同。因此,当我们配置单元格时:

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
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

具有自定义配置的 UICollectionView 列表 - 如何将单元格中的更改传递到视图控制器? 的相关文章

  • iOS - 如何在 swift 中使用 `NSMutableString`

    我已经看过这段 Objective C 代码 但我很难在 swift 中做同样的事情 NSMutableAttributedString res self richTextEditor attributedText mutableCopy
  • 如何从 SDK 实现每个会话的 Google Places 自动完成功能?

    是否可以从 Android 和 iOS 应用程序的 place sdk 实现基于会话的自动完成 根据 6 月 11 日生效的新 Google 地图框架定价 对自动完成的请求可以分为基于击键 会话的请求 我找不到描述实施步骤的文档 除了这个参
  • CALayer边框奇怪问题

    我正在向 CALayer 添加边框 但有一些奇怪的行为 在我应用的边框之后出现模糊边框 参见屏幕截图 这是我的代码 void configureLabel self hidden YES self textAlignment NSTextA
  • (Kiss)XML xpath 和默认命名空间

    我正在开发一个 iPhone 项目 需要解析一些 xml xml 可能包含也可能不包含默认名称空间 我需要知道如何解析 xml 以防它使用默认命名空间 由于我需要读取和写入 xml 因此我倾向于使用 KissXML 但我愿意接受建议 这是我
  • ios swift parse:从 3 个类收集数据

    我有这样的结构 User CardSet 带有指向 User objectId 的指针 user 和 col name 带有点 cards 的卡片到 Card Set objectId 和列 name 我想选择所有卡数据 包括当前用户的卡集
  • iOS:生成pdf时绘制文本时如何设置字体?

    我在ios应用程序中使用drawpdf函数生成pdf 同时调用nsobject类中的drawtext函数 它根据我指定的框架和字符串清楚地绘制文本 我的代码是 void drawText NSString textToDraw inFram
  • 如何在 Swift 中从 UIColor 获取 RGB 代码(INT)[重复]

    这个问题在这里已经有答案了 我想在 Swift 中获取 UIColor 的 RGB 值 let swiftColor UIColor red 1 green 165 255 blue 0 alpha 1 println RGB Value
  • 如何让UITextView背景线与文字对齐?

    我正在尝试绘制 UITextView 的背景线 这是我用来画这些线的代码 CGContextBeginPath context CGContextSetStrokeColorWithColor context self horizontal
  • AVAssetExportSession 无法导出从 iCloud 下载的视频

    我正在尝试创建从用户相册中选择的视频的缩小版本 输出的最大尺寸为 720p 因此 在检索视频时 我使用 mediumQualityFormat as the deliveryMode 如果用户设备中不存在原始视频或其中等质量版本 这会导致
  • 调整 UIImage 的大小而不将其完全加载到内存中?

    我正在开发一个应用程序 用户可以在其中尝试加载非常非常大的图像 这些图像首先在表格视图中显示为缩略图 我的原始代码会在大图像上崩溃 因此我重写它以首先将图像直接下载到磁盘 是否有一种已知的方法可以调整磁盘上图像的大小 而无需通过以下方式将其
  • 如何在button.addTarget操作中发送多个按钮?斯威夫特3

    如何将button和button2发送到我的pressButton2函数中 当用户触摸按钮2时 我需要更改按钮和按钮2的颜色 当我的 button2 addTarget 看起来像这样时 我收到错误 表达式列表中存在预期表达式 import
  • UItextView 背景颜色 Linespacing 区域太

    我正在尝试在 UITextView 中复制文本突出显示 不是搜索文本突出显示 但我也被行间距的颜色所困扰 我该如何纠正这个问题 现在的情况 期望的结果 我已将以下属性添加到我的 UiTextview 的属性文本中 对于段落行间距 我使用了以
  • 根据一个数组对多个数组进行排序

    如何根据数组对一堆数组进行排序createdAt 例如 2015 11 02 19 19 35 0000 将它们组合成另一种类型 字典 以便在 tableView 中使用是否有益 如果有的话如何 var comment AnyObject
  • TableViewController 的 viewDidLoad 未触发

    我一直在关注这个tutorial http www appcoda com ios programming sidebar navigation menu 有一个滑出式菜单 我添加了一个 TableViewController 它将显示文章
  • 在实例化对象之前是否可以检查故事板中是否存在标识符?

    在我的代码中我有这一行 但我想知道是否有办法检查是否 一些控制器 在我将它与 一起使用之前就存在实例化ViewControllerWithIdentifier 方法 如果标识符不存在 则应用程序崩溃 如果没有好的方法 这并不是一个大问题 我
  • Objective-C 中发送给对象的消息可以被监听或者打印出来吗? [复制]

    这个问题在这里已经有答案了 可能的重复 Objective C 中拦截方法调用 https stackoverflow com questions 1618474 intercept method call in objective c 如
  • 如何在 UICollectionView 中将行居中?

    我有一个UICollectionView与随机细胞 有什么方法可以让我将行居中吗 默认情况下它是这样的 x x x x x x x x x x x x x x 这是所需的布局 x x x x x x x x x x x x 我必须做这样的事
  • 如何更改 SwiftUI 列表中分隔符的颜色?

    我在 SwiftUI 中创建了一个列表 我想更改颜色或删除分隔符 因为在 UIKit 中 我们可以轻松更改 TableView 中分隔符的颜色 下面是 SwiftUI 中列表的代码和 UI 图片 State private var user
  • SpriteKit的更新函数:时间与帧率

    一般来说 我对编程和 Spritekit 很陌生 并且有兴趣探索毫秒和帧率之间的关系 以及如何使用更新函数作为两者之间的中介 帧率与毫秒 从本质上讲 帧速率和时间之间的主要区别在于时间始终一致 而帧速率则不然 由于密集的图形程序 它可能会下
  • ios - 如何声明静态变量? [复制]

    这个问题在这里已经有答案了 C 中声明的静态变量如下 private const string Host http 80dfgf7c22634nbbfb82339d46 cloudapp net private const string S

随机推荐