Можно ли настроить положение и макет параметра .outlineDisclosure для UICollectionView (стиль списка)?
На данный момент у меня такой макет:
Но я хочу получить такой макет для заголовков (развернутое состояние):
По сути, мне нужно настраиваемое представление для индикатора раскрытия, потому что в развернутом состоянии он должен указывать вниз, а в свернутом состоянии он должен указывать вверх (см. Рисунок выше). Кроме того, слева должен быть индикатор раскрытия информации.
Что я сделал до сих пор
С помощью этого кода я получаю первый пример:
class ViewController: UIViewController {
enum ListItem: Hashable {
case header(HeaderItem)
case symbol(SFSymbolItem)
}
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewLayout())
var dataSource: UICollectionViewDiffableDataSource<HeaderItem, ListItem>!
override func viewDidLoad() {
super.viewDidLoad()
configureCollectionView()
setupDataSource()
updateDate(items: HeaderItem.modelObjects)
}
private func configureCollectionView() {
let layoutConfig = UICollectionLayoutListConfiguration(appearance: .plain)
let listLayout = UICollectionViewCompositionalLayout.list(using: layoutConfig)
collectionView.register(CustomListCell.self, forCellWithReuseIdentifier: CustomListCell.reuseID)
collectionView.collectionViewLayout = listLayout
view.addSubview(collectionView)
collectionView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(collectionView)
NSLayoutConstraint.activate([
collectionView.topAnchor.constraint(equalTo: view.topAnchor),
collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
}
private func setupDataSource() {
let headerCellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, HeaderItem> {
(cell, indexPath, headerItem) in
var config = cell.defaultContentConfiguration()
config.text = headerItem.title
cell.contentConfiguration = config
let headerDisclosureOption = UICellAccessory.OutlineDisclosureOptions(style: .header)
cell.accessories = [.outlineDisclosure(options: headerDisclosureOption)]
//cell.accessories = [.customView(configuration: .init(customView: UIView(), placement: .trailing(displayed: .always, at: .center), isHidden: <#T##Bool?#>, reservedLayoutWidth: <#T##UICellAccessory.LayoutDimension?#>, tintColor: <#T##UIColor?#>, maintainsFixedSize: <#T##Bool?#>))]
}
let symbolCellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, SFSymbolItem> {
(cell, indexPath, symbolItem) in
var config = cell.defaultContentConfiguration()
config.text = symbolItem.name
cell.contentConfiguration = config
}
dataSource = UICollectionViewDiffableDataSource<HeaderItem, ListItem>(collectionView: collectionView) {
(collectionView, indexPath, listItem) -> UICollectionViewCell? in
switch listItem {
case .header(let headerItem):
// Dequeue header cell
let cell = collectionView.dequeueConfiguredReusableCell(using: headerCellRegistration,
for: indexPath,
item: headerItem)
return cell
case .symbol(let symbolItem):
// Dequeue symbol cell
let cell = collectionView.dequeueConfiguredReusableCell(using: symbolCellRegistration,
for: indexPath,
item: symbolItem)
return cell
}
}
}
private func updateDate(items: [HeaderItem]) {
var snapshot = NSDiffableDataSourceSnapshot<HeaderItem, ListItem>()
snapshot.appendSections(items)
for headerItem in items {
//section snapshot
var sectionSnapshot = NSDiffableDataSourceSectionSnapshot<ListItem>()
let headerListItem = ListItem.header(headerItem)
sectionSnapshot.append([headerListItem])
let symbolListItemArray = headerItem.symbols.map { ListItem.symbol($0) }
sectionSnapshot.append(symbolListItemArray, to: headerListItem)
dataSource.apply(sectionSnapshot, to: headerItem, animatingDifferences: false)
}
}
}
Модель:
struct HeaderItem: Hashable {
let title: String
let symbols: [SFSymbolItem]
}
struct SFSymbolItem: Hashable {
let name: String
let image: UIImage
init(name: String) {
self.name = name
self.image = UIImage(systemName: name)!
}
}
extension HeaderItem {
static let modelObjects = [
HeaderItem(title: "Communication", symbols: [
SFSymbolItem(name: "mic"),
SFSymbolItem(name: "mic.fill"),
SFSymbolItem(name: "message"),
SFSymbolItem(name: "message.fill"),
]),
HeaderItem(title: "Weather", symbols: [
SFSymbolItem(name: "sun.min"),
SFSymbolItem(name: "sun.min.fill"),
SFSymbolItem(name: "sunset"),
SFSymbolItem(name: "sunset.fill"),
]),
HeaderItem(title: "Objects & Tools", symbols: [
SFSymbolItem(name: "pencil"),
SFSymbolItem(name: "pencil.circle"),
SFSymbolItem(name: "highlighter"),
SFSymbolItem(name: "pencil.and.outline"),
]),
]
}
Изменить:
я смог приблизиться к желаемому результату, используя
.customView(...)
как аксессуар.
let testAction = UIAction(image: UIImage(systemName: "chevron.up"), handler: { [weak self] _ in
//expand / collapse programmatically
})
let testBtn = UIButton(primaryAction: testAction)
let customAccessory = UICellAccessory.CustomViewConfiguration(
customView: testBtn,
placement: .leading(displayed: .always))
cell.accessories = [.customView(configuration: customAccessory)]
Можно ли программно свернуть / развернуть, щелкнув customView?
1 ответ
Вот простой обходной путь, но он удовлетворит ваши требования. Вместо создания собственного представления аксессуаров просто используйте команду.image
собственностьUIListContentConfiguration
и изменить его в зависимости от состояния. При этом ваш headerCellRegistration будет выглядеть примерно так:
let headerCellRegistration = UICollectionView.CellRegistration<HeaderListCell, HeaderItem> {
(cell, indexPath, headerItem) in
var config = cell.defaultContentConfiguration()
config.text = headerItem.title
cell.contentConfiguration = config
// Simply make the outlineDisclosure accessory invisible but functional.
let headerDisclosureOption = UICellAccessory.OutlineDisclosureOptions(style: .header, tintColor: .clear)
cell.accessories = [.outlineDisclosure(options: headerDisclosureOption)]
}
class HeaderListCell: UICollectionViewListCell {
override func updateConfiguration(using state: UICellConfigurationState) {
super.updateConfiguration(using: state)
guard var cConfig = self.contentConfiguration?.updated(for: state) as? UIListContentConfiguration else { return }
cConfig.image = state.isExpanded ? UIImage(systemName: "chevron.down") : UIImage(systemName: "chevron.right")
cConfig.imageProperties.reservedLayoutSize = CGSize(width: 12.0, height: 12.0)
cConfig.imageToTextPadding = 16.0
self.contentConfiguration = cConfig
}
}
Надеюсь, это решит вашу проблему, ура!