Можно ли настроить положение и расположение параметра .outlineDisclosure для UICollectionView (стиль списка)?

#swift #uicollectionview #uikit #uicollectionviewlistcell #uicollectionviewlist

Вопрос:

В настоящее время у меня есть следующий макет:
введите описание изображения здесь

Но я хочу получить этот макет для заголовков (развернутое состояние):
введите описание изображения здесь

Поэтому, по существу, мне нужно пользовательское представление для индикатора раскрытия, потому что для развернутого состояния он должен указывать снизу, а для свернутого состояния он должен указывать сверху (см. Рисунок выше). Кроме того, индикатор раскрытия должен быть слева.

Что я сделал до сих пор
С помощью этого кода я получаю первый пример:

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

Model:

 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 amp; Tools", symbols: [
            SFSymbolItem(name: "pencil"),
            SFSymbolItem(name: "pencil.circle"),
            SFSymbolItem(name: "highlighter"),
            SFSymbolItem(name: "pencil.and.outline"),
        ]),
        
    ]
}
 

Edit:
I was able to get closer to the desired result by using .customView(...) as accesory type.

 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)]
 

введите описание изображения здесь
Можно ли свернуть / развернуть программно, щелкнув на пользовательском представлении?