pre select/highlight UICollectionViewCell on first

2020-06-01 02:05发布

问题:

Im trying to preselect the first object/UICollectionViewCell in the UICollectionView? I have tried:

self.dateCollectionView.allowsMultipleSelection=NO;

[self.dateCollectionView selectItemAtIndexPath:0 animated:YES scrollPosition:UICollectionViewScrollPositionLeft];
[self collectionView:self.dateCollectionView didSelectItemAtIndexPath:0];
[self.dateCollectionView reloadData];

in viewDidLoad.

Here are my UICollectionView methods;

 -(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{

  return self.titles.count;
 }

 -(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{

    UICollectionViewCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath];
    cell.backgroundColor= [UIColor clearColor];
    UILabel * dateLabel = (UILabel *)[cell viewWithTag:1];
    UILabel * subText = (UILabel *)[cell viewWithTag:2];
    subText.text=self.titles[indexPath.row];
    subText.adjustsFontSizeToFitWidth=YES;
    if (cell.selected) {
        cell.backgroundColor = [UIColor blueColor]; // highlight selection
    }
    else
    {
        cell.backgroundColor = [UIColor redColor]; // Default color
    }
    return cell;
}

-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath  {

    UICollectionViewCell *datasetCell =[collectionView cellForItemAtIndexPath:indexPath];
    datasetCell.backgroundColor = [UIColor blueColor]; // highlight selection
}

-(void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath {

 UICollectionViewCell *datasetCell =[collectionView cellForItemAtIndexPath:indexPath];
datasetCell.backgroundColor = [UIColor redColor]; // Default color
}

- (BOOL)collectionView:(UICollectionView *)collectionView
 shouldHighlightItemAtIndexPath:(NSIndexPath *)indexPath
{
    return YES;
}

- (BOOL)collectionView:(UICollectionView *)collectionView
 shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath;
{
    return YES;
}

回答1:

In viewDidAppear:

NSIndexPath *indexPathForFirstRow = [NSIndexPath indexPathForRow:0 inSection:0];
[self.dateCollectionView selectItemAtIndexPath:indexPathForFirstRow animated:NO scrollPosition:UICollectionViewScrollPositionNone];
[self collectionView:self.dateCollectionView didSelectItemAtIndexPath:indexPathForFirstRow];


回答2:

For Swift 3.0.1 You can try this:

self.collectionView.selectItem(at: indexPath, animated: true, scrollPosition: [])

or

self.collectionView.selectItem(at: indexPath, animated: true, scrollPosition: UICollectionViewScrollPosition(rawValue: 0))

For Objective-C You can try this:

self.collectionView selectItemAtIndexPath:indexPath animated:YES scrollPosition:UICollectionViewScrollPositionNone];

Note: You should use it in viewDidAppear



回答3:

For swift 3

Use collectionView.selectItem with-in this overload collectionView(UICollectionView, IndexPath)

This is my code, in this code I pre selected row with indexPath.row = 0

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = ScenarioCollectionView.dequeueReusableCell(withReuseIdentifier: "ReuseScenarioCollectionViewCell", for: indexPath as IndexPath) as! ScenarioCollectionViewCell

    if (indexPath.row == 0){
        collectionView.selectItem(at: indexPath, animated: true, scrollPosition: UICollectionViewScrollPosition.centeredHorizontally)
        cell.layer.borderColor=UIColor.gray.cgColor        
    }else{
        cell.layer.borderColor=UIColor.white.cgColor
    }
    return cell
}


func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    let cell = collectionView.cellForItem(at: indexPath)
    cell?.layer.borderColor = UIColor.gray.cgColor
        }

func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
    let cell = collectionView.cellForItem(at: indexPath as IndexPath)
    cell?.layer.borderColor = UIColor.white.cgColor
    collectionView.deselectItem(at: indexPath, animated: true)
}


回答4:

For me, putting it in viewDidAppear: cause a second to select, so the user will see both states (i.e. not selected, and selected). To avoid this, I put it in viewWillAppear: instead and worked like a charm

override func viewWillAppear(_ animated: Bool) {
    let selectedIndexPath = IndexPath(item: 0, section: 0)
    collectionView.selectItem(at: selectedIndexPath, animated: false, scrollPosition: .left)
}


回答5:

I solved this by subclassing UICollectionView and selecting needed item in layoutSubviews:

class InitialSelectionCollectionView: UICollectionView {

   var initialSetupPerformed: Bool = false
   var initialSelectedIndexPath: IndexPath!

   override func layoutSubviews() {
       super.layoutSubviews()

       if !initialSetupPerformed && initialSelectedIndex != nil{
           selectItem(at: initialSelectedIndexPath, animated: false, scrollPosition: .centeredHorizontally)

           initialSetupPerformed = true
       }
   }
}

Then, when you init your custom collection view, just set needed IndexPath to initialSelectedIndexPath



回答6:

For Swift 3:

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    //auto selected 1st item
    let indexPathForFirstRow = IndexPath(row: 0, section: 0)
    self.collectionView?.selectItem(at: indexPathForFirstRow, animated: true, scrollPosition: .top)
}


回答7:

    [self.collectionView reloadData];
    [self.collectionView layoutIfNeeded]; //important
    NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:0];
    [self.collectionView selectItemAtIndexPath:indexPath animated:NO scrollPosition:UICollectionViewScrollPositionNone];
    [self collectionView:self.collectionView didSelectItemAtIndexPath:indexPath];


回答8:

swift code for selecting a cell initially

func selectinitialCell() {

    let cell = venueTypeCollectionView.cellForItemAtIndexPath(NSIndexPath(forItem: 0, inSection: 0)) as! SearchTypeCollectionViewCell
    self.indexPathOfSelectedItem = NSIndexPath(forItem: 0, inSection: 0)
    cell.venueType.textColor = UIColor.whiteColor()
    cell.selected = true
}

but this does not invoke the delegate function such as didSelectItemAtIndexPath, didDeselectItemAtIndexPath.



回答9:

  • Here is full code link

In my case. I solved it via subclass a UICollectionView class with Generic types

// ******************************************
//
// MARK: - Config injection, Extend whatever you want
//
// ******************************************
struct CCCollectionViewConfig {
    var itemSize: CGSize?
    var minimumInteritemSpacing: CGFloat?
    var minimumLineSpacing: CGFloat?
    var allowsDefaultSelection: Bool = true
}

// ******************************************
//
// MARK: - Generic CCCollectionView
//
// ******************************************
final class CCCollectionView<T, Cell: UICollectionViewCell>: UICollectionView, UICollectionViewDataSource, UICollectionViewDelegate {

    typealias SelectHandler = (T, IndexPath?) -> Void

    typealias CellConfigure = (Cell, T) -> Void

    var contents: [T] = [] {
        didSet {
            DispatchQueue.main.async {
                self.reloadData()
                self.selectedIndexPath = IndexPath(item: (self.config.allowsDefaultSelection) ? 0 : -1, section: 0)
            }
        }
    }

    var configure: CellConfigure

    var selectHandler: SelectHandler

    var selectedIndexPath: IndexPath

    private var config: CCCollectionViewConfig

    private let identifier = "identifier"

    init(contents: [T], config: CCCollectionViewConfig, configure: @escaping CellConfigure, selectHandler: @escaping SelectHandler) {

        self.config = config
        self.contents = contents
        self.configure = configure
        self.selectHandler = selectHandler
        self.selectedIndexPath = IndexPath(item: (config.allowsDefaultSelection) ? 0 : -1, section: 0)

        let layout = UICollectionViewFlowLayout()

        if let size = config.itemSize {
            layout.itemSize = size
        }

        layout.minimumInteritemSpacing = config.minimumInteritemSpacing ?? 0
        layout.minimumLineSpacing = config.minimumLineSpacing ?? 0
        layout.scrollDirection = .vertical
        layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)

        super.init(frame: .zero, collectionViewLayout: layout)

        setup()
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    private func setup() {
        register(Cell.self, forCellWithReuseIdentifier: identifier)

        super.delegate = self
        super.dataSource = self

        allowsMultipleSelection = false
        isScrollEnabled = false
        backgroundColor = .clear
    }

    // ******************************************
    //
    // MARK: - UICollectionViewDataSource, UICollectionViewDelegate
    //
    // ******************************************

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return contents.count
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: identifier, for: indexPath) as! Cell
        cell.isSelected = (selectedIndexPath == indexPath)
        let content = contents[indexPath.row]
        configure(cell, content)
        return cell
    }

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {

        let content = contents[indexPath.row]
        selectHandler(content, indexPath)

        /// If selected item is equal to current selected item, ignore it
        if selectedIndexPath == indexPath {
            return
        }

        /// Handle selected cell
        let selectedCell = collectionView.cellForItem(at: indexPath)
        selectedCell?.isSelected = true

        /// Handle deselected cell
        let deselectItem = collectionView.cellForItem(at: selectedIndexPath)
        deselectItem?.isSelected = false

        selectedIndexPath = indexPath
    }
}

// ******************************************
//
// MARK: - CollectionViewCell
//
// ******************************************
final class CCCollectionViewCell: UICollectionViewCell {

    var title: String = "" {
        didSet {
            titleLabel.text = title
        }
    }

    private let titleLabel: UILabel = {
        let label = UILabel()
        return label
    }()


    var _isSelected: Bool = false
    override var isSelected: Bool {
        get {
            return _isSelected
        }
        set(newValue) {
            _isSelected = newValue
            updateSelection()
        }
    }

    private func updateSelection() -> Void {
        contentView.layer.borderColor = isSelected ? UIColor.red.cgColor : UIColor.green.cgColor
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        contentView.addSubview(titleLabel)
        contentView.layer.cornerRadius = 2.0
        contentView.layer.borderWidth = 2.0
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

Then use it inside your ViewController

private let fakeContents = Array(repeating: "123123", count: 40)

/// Collection View Config
private lazy var config: CCCollectionViewConfig = {
    return CCCollectionViewConfig(itemSize: CGSize(width: 100, height: 30),
                                  minimumInteritemSpacing: 4.0,
                                  minimumLineSpacing: 4.0)
}()

/// Collection View Config
private lazy var collectionView: CCCollectionView<String, CCCollectionViewCell> = {
    let cv = CCCollectionView<String, CCCollectionViewCell>(contents: fakeContents, config: config, configure: { (cell: CCCollectionViewCell, content) in
        cell.title = content
    }) { [weak self] (content, indexPath) in
        guard let self = self else { return }
        guard let row = indexPath?.row else { return }
    }
    return cv
}()