Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cell preselection #524

Closed
dmytronasyrov opened this issue Mar 4, 2017 · 11 comments
Closed

Cell preselection #524

dmytronasyrov opened this issue Mar 4, 2017 · 11 comments
Labels

Comments

@dmytronasyrov
Copy link

I'm trying to preselect a cell in cellForItem(at index: Int) -> UICollectionViewCell by cell.isSelected = true in section controller.
It works fine and cell becomes selected. When I try to deselect a cell by tapping it cell doesn't react at all. Tap triggers isHighlighted method but doesn't want to trigger neither isSelected in cell nor didDeselectItemAt delegate method.
This issue is related only to preselected cells. All other cells work fine in the same collection.

@rnystrom
Copy link
Contributor

rnystrom commented Mar 4, 2017

@PharosProduction can you describe a bit what you're trying to do? UICollectionViewCell selection APIs aren't really supposed to be changed manually, you're supposed to let the UICollectionView internals handle all of that for you.

The docs say:

You typically do not set the value of this property directly. Changing the value of this property programmatically does not change the appearance of the cell. The preferred way to select the cell and highlight it is to use the selection methods of the collection view object.

If you're trying to do custom selection state, we recommend making your own properties and settings.

@dmytronasyrov
Copy link
Author

dmytronasyrov commented Mar 4, 2017

There is a Set of posts displayed as a two column list, one section with one row for each post. Some of them should be selected when list appears. Each post contains a selection state(bool). Posts amount is a variable(pagination load). What is the best way to select marked for selection cells and where?
I have tried already different approaches including selectItem(at: IndexPath?, animated: Bool, scrollPosition: UICollectionViewScrollPosition) but no luck to find the best way to do this. The goal is just to select a cell without triggering any delegate methods so user can deselect it or proceed with a default selection set(fetched by indexPathForSelectedItems)

@rnystrom
Copy link
Contributor

rnystrom commented Mar 4, 2017

@PharosProduction at Instagram we use custom properties to represent selected state in our UI instead of relying on the UICollectionView built-in stuff. We would do something like:

  • Add a custom @property BOOL checkmarkDisplayed; to our cell subclass
  • In the setCheckmarkDisplayed:(BOOL)checkmarkDisplayed setter, set the state and call [self setNeedsLayout] and set hidden on our checkmark image view
  • Position the view in layoutSubviews
  • In cellForItemAtIndex: we can manually set the state w/out worrying about UICollectionView internals.

Then in didSelectItemAtIndex: you can flip the selection state, both in the cell and wherever you're tracking it.

@dmytronasyrov
Copy link
Author

Got it. Thanks.
BTW, collectionView doesn't trigger didSelectItemAtIndex:, only deselect. Is it possible to switch from didSelectItem(at index: Int) in section to collectionView delegate without loosing all other IGListSectionController functionality?

@rnystrom
Copy link
Contributor

rnystrom commented Mar 5, 2017

@PharosProduction you mean to use the UICollectionViewDelegate APIs alongside IGListSectionController? It's possible, but not at the section-controller level. You can attach a collectionViewDelegate to the adapter to get all the normal delegate events.

@dmytronasyrov
Copy link
Author

No, on ViewController level. didDeselectItemAtIndex works fine, but didSelectItemAtIndex doesn't work, instead IGListKit triggers didSelectItem at section level. Is it possible to trigger both of them(didSelectItemAtIndex and didSelectItem)?

Actually I understand it's possible by digging inside IGListKit and changing current code, but maybe I'm wrong and I have missed something in docs.

@rnystrom
Copy link
Contributor

rnystrom commented Mar 6, 2017

@PharosProduction it should be totally possible. That collectionViewDelegate should just forward events to the delegate while also notifying the section controller. Here's the source.

@dmytronasyrov
Copy link
Author

I have updated IGListKit to the latest master. It works, thank you.

@jessesquires
Copy link
Contributor

💯

@rnystrom rnystrom mentioned this issue Jul 12, 2017
3 tasks
facebook-github-bot pushed a commit that referenced this issue Jul 17, 2017
Summary:
Adding support for a cell deselection API. Trying to make some headway to move and drag+drop support, but also want better stock `UICollectionView` API support. Will also assist eventual `UITableView` support.

- Added overridable API to `IGListSectionController`
- Support for stacked SC
- Breaking, required protocol for binding SC

Assists #524 and #184

- [x] All tests pass. Demo project builds and runs.
- [x] I added tests, an experiment, or detailed why my change isn't tested.
- [x] I added an entry to the `CHANGELOG.md` for any breaking changes, enhancements, or bug fixes.
Closes #853

Reviewed By: jeremycohen

Differential Revision: D5425414

Pulled By: rnystrom

fbshipit-source-id: 0b25c125b1f171979a15c3095095fc18b4108be6
@mohpor
Copy link

mohpor commented Jul 12, 2021

I know it might be a little late to solve this issue, but for those who still need to resolve this issue without too much hassle, you just need to call the selectItem(at: IndexPath?, animated: Bool, scrollPosition: UICollectionViewScrollPosition) inside cellForItem(at index: Int) -> UICollectionViewCell:

override func cellForItem(at index: Int) -> UICollectionViewCell {
    reusableCell(at: index, isNib: false) {
      let item = self.items[safe: index]
      $0.item = item
      $0.isSelected = item?.isSelected ?? false
      if item?.isSelected == true {
        self.collectionContext.selectItem(at: index,
                                          sectionController: self,
                                          animated: false,
                                          scrollPosition: [])
      }
    } as Cell
  }

Then the didSelect and didDeselect delegates will be called.

@mohpor
Copy link

mohpor commented Jul 12, 2021

Here is the syntactic sugar I used for the previous comment:

extension ListSectionController {
  
  public func reusableCell<T: UICollectionViewCell>(at index: Int, isNib: Bool, configCell: ((T) -> Void)? = nil) -> T {
    
    if isNib {
      guard let cell = collectionContext?.dequeueReusableCell(withNibName: String(describing: T.self), bundle: nil, for: self, at: index) as? T else {
        fatalError("Could not dequeeu nib cell of type: {\(type(of: T.self))}")
      }
      configCell?(cell)
      return cell
    } else {
      guard let cell = collectionContext?.dequeueReusableCell(of: T.self, for: self, at: index) as? T else {
        fatalError("Could not dequeeu class cell of type: {\(type(of: T.self))}")
      }
      configCell?(cell)
      return cell
    }
    
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants