I have class UIViewCollection. I need will block UIViewCollection and unlock after terminating scroll animation UIViewCollection. This method work good but while when i touch to screen it's not complete scrolling
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
collectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
}
Since CollectionView inherits from UIScrollView you can set the property
collectionView.isScrollEnabled to true or false as you see fit, however in the code you shared I see that you want to scroll to each item in your collection like it was pages in a book, right? to achieve that there is another property called paging, and you can set it to true or false as well, I believe it would be something like collectionView.isPagingEnabled = true or false as you wish, hope this helps
Related
I have a collectionView (nested) inside a collectionViewCell. When I open this VC, the cell inside nested collectionView have wrong size. After scroll down and up - everything is fine.
I think when I am scrolling method layoutSubviews are calling.
But when I added
cell.setNeedsLayout() cell.layoutIfNeeded()
inside func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) the result is not change.
Constraints:
i think problem is not with the size, it is from constraints.
Would you please share a snippet of your code
I've been trying to figure out how to make my collection view of 3 cells load with the 2nd cell, and I finally figured it out after looking through StackOverFlow. However, the code that I came across is a bit confusing to me. Would anyone be able to explain why this code below works in making my collection view cell (that covers the whole screen) start with the 2nd of 3 cells? (this is the effect I wanted to achieve all along, but I want to learn more about why this code works exactly.
In this block of code, there's a bool variable and an if statement, why are they needed? When I took out the boolean variable and if statement, the collection view was unable to scroll.
How does this block of code work exactly?
Thank you.
var onceOnly = false
internal override func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
if !onceOnly {
let indexToScrollTo = IndexPath(item: 1, section: 0)
self.collectionView.scrollToItem(at: indexToScrollTo, at: .left, animated: false)
onceOnly = true
}
}
1- This scrollToItem in Docs
let indexToScrollTo = IndexPath(item: 1, section: 0)
self.collectionView.scrollToItem(at: indexToScrollTo, at: .left, animated: false)
makes the collectionView scroll to the second item in the list
2-
When I took out the boolean variable and if statement, the collection view was unable to scroll
Because willDisplay is called for every cell display , so when for example you scroll to 3rd cell willdisplay is called and causes the collectionView to go to the second cell , so it makes it stuck in the second item all the time ( and this seems like no scroll but the scroll happens and you won't notice that as it happens instantly ) , so the boolean var is needed to make that scroll action happens once which is the scroll to the specified index
I am stumbling into an issue with isHighlighted state of an imageView inside a cell of a collection view: I cannot manage to animate the transition between 2 images the way I aim for.
Each custom cell of my collection view has 3 views:
an UIImageView : a background image with highlighted and non highlighted related .pngs
a second UIImageView : another image with highlighted and non highlighted related .pngs as a subview of the previous UIImageView
a custom UILabel : a text label
When I touch the cell, both ImageViews instantly move to highlighted state but I would like them to transition to this state instead (a simple CrossDissolve transition)
I have tried 2 different approaches : overriding isSelected method in the custom cell class and putting the animation in collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
Overriding isSelected property:
override var isSelected: Bool {
didSet {
UIView.transition(with: cellImageView, duration: 1, options: .transitionCrossDissolve, animations: {
self.cellImageView.setNeedsLayout()
self.cellImageView.layoutIfNeeded()
self.cellImageView.isHighlighted = self.isSelected
})
}
}
With this way, I get no animation when I click on the cell in the simulator, either when the cell is selected or when it loses it selected state, except when I click and hold a cell: then I get an animation of the previous cell losing its selected cell (in other words I have the transition of the previous selected imageView from isHighlighted(true) to isHighlighted(false)).
didSelectItemAt indexPath Method
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath) as! SubMenuCell
UIView.transition(with: cell, duration: 1.0, options: .transitionCrossDissolve, animations:
cell.setNeedsLayout()
cell.layoutIfNeeded()
cell.cellImageView.isHighlighted = cell.isSelected
})
}
This method does not trigger an animation when the cell is selected but does provide a transition animation when the cell moves from isSelected(true) to isSelected(false): in other words, I see the previous selected cell fading out and I don't see the selected cell fading in.
I'd be grateful for any tips as to why it does not work and how it could be fixed!
Thank you all for the time you took to read and reply!
Best,
Thanks to the guys to have taken the time to think about my question.
By investigating further, I decided to check how and when 3 events, namely cell.isHighlighted, cell.isSelected and collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) where triggered and I found some pretty interesting things actually. Let me sum it up:
When you touch a cell (cell1) in a collection view, it appears the following events occur:
cell1.isHightlighted is set to true
cell1.isHightlighted is set to false
cell1.isSelected is set to true, which calls back cell1.isHighlighted property
and lastly collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) method is called
Now when you touch another cell (cell2) in the collection View, the following events occur:
cell2.isHighlighted is set to true
cell1.isSelected is set to false, which calls back cell1.isHightlighted property
cell2.isHighlighted is set to false
cell1.isSelected is set to false again (!)
cell2.isSelected is set to true, which calls back cell2.isHighlighted property
and again lastly collectionView(_ collectionView:
UICollectionView, didSelectItemAt indexPath: IndexPath) method is
called
With that in mind, I came to the conclusion that to run a transition animation on isHighlighted state in the collection view method will never work as intended since the property has already been set prior to reach the method.
One solution to obtain both animations (isHighlighted true and false) that I have found is to act on the property observers of isHighlighted and isSelected of my custom collection view cell, as follow:
override var isHighlighted: Bool {
willSet {
if newValue == true {
UIView.transition(with: cellImageView, duration: 1.0, options: .transitionCrossDissolve, animations: {
self.cellImageView.isHighlighted = newValue
})
}
}
}
override var isSelected: Bool {
willSet {
if newValue == false {
UIView.transition(with: cellImageView, duration: 1.0, options: .transitionCrossDissolve, animations: {
self.cellImageView.isHighlighted = newValue
})
}
}
}
I hope it helps, and thanks again to all of you whom have spent some time trying to figuring it out!
If anyone finds a more elegant solution, do not hesitate to post it!!!
Best,
I'm not convinced but I think you might also want to override;
override func setSelected(_ highlighted: Bool, animated: Bool)
In your UICollectionViewCell as well as the isSelected property and therefore your animation code in func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath).
*Note that there is also a difference between isHighlighted and isSelected, you said isHighlighted in your question, but leveraged isSelected in your implementation.
How can I achieve that the images which comes into view is automatically selected?
I know that I can have the first item in the collection view selected by default, but how to do it with every item that comes into view?
To explain it in more detail: I have an imageview depending on the selection of the collectionView. What I want to do is, that the user don't have to select the collectionViewcell to get the depending imageview. I want the user to just swipe through the collection view and the image that comes up is selected and an overview shown in the depending imageview.
The main problem is in my opinion, that I need to Ave just one selected at a time.
You can try to enable multiple selection:
collectionView.allowsMultipleSelection = true
And then in a loop select items that you want to have selected:
collectionView.selectItem(at: indexPath, animated: false, scrollPosition: .top)
EDIT
You can use willDisplay delegate method of the UICollectionViewDelegate to do the selection when the cell is being displayed:
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
// this is called before the cell is displayed, check if this is the cell to be selected, and if yes, select it:
collectionView.selectItem(at: indexPath, animated: false, scrollPosition: .top)
}
As far as I understood your question try this selecting an item in the cellForItemAt indexPath. So as soon as the cell is loaded (shown) you select it.
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
[...]
collectionView.selectItem(at: indexPath, animated: true, scrollPosition: .top)
[...]
}
Also as mentioned in other answer: collectionView.allowsMultipleSelection = true
I think you should use collectionView:willDisplayCell:forItemAtIndexPath: rather than the cellForItemAtIndexPath: (mentioned above) to call the selectItem(at:animated:scrollPosition:), this will ensure the cell is actually about to show.
I have a UICollectionView displaying custom cells in a horizontal flow layout; in other words, some content is placed outside the screen bounds.
Additionally, I have a gesture that fires an NSNotification leading to a color change of some of the elements of my cells (i.e. a theme). Everything works perfectly except for the fact that the cells that are present out of the bounds of the screen don't all update to the new color change. Is there any way to force them to redraw?
In the function called when the NSNotification is fired I've tried redrawing the collection view with self.collectionView.reloadData(), self.collectionView.setNeedsDisplay() and self.collectionView.setNeedsLayout but to no avail. I tried the last two of the list in the awakeFromNib() of the custom cell class but nothing.
Here is the code for my cellForItemAtIndexPath:
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = popularCollectionView!.dequeueReusableCellWithReuseIdentifier("popular", forIndexPath: indexPath) as! PopularPainting
cell.thumbnail.image = paintings[indexPath.row].paintingImage
cell.name!.text = paintings[indexPath.row].paintingName
cell.price!.setTitle(paintings[indexPath.row].paintingPrice, forState: UIControlState.Normal)
if cell.isDark {
cell.name!.textColor = UIColor(red: 205/255, green: 205/255, blue: 205/255, alpha: 1)
cell.price!.setTitleColor(self.kPaleBlue, forState: .Normal)
self.popularCollectionView.reloadData()
}
return cell
}
Any suggestions?
Note: Scrolling to the offscreen content and repeating the gesture to change themes works perfectly so I have no idea what's up.
Your assumption that there are cells that exist off-screen for every item in your collection view is incorrect. In fact, table views and collection views re-use cells that get scrolled off-screen for new cells coming on-screen, so there are only ever just over a screenful of cells in existence.
You're right to call reloadData after the notification fires. But you need to ensure your implementation of collectionView:itemForRowAtIndexPath: will correctly configure cells that are scrolled on-screen subsequently. That will probably mean saving the state change in a property after the notification fires, and checking that property when you configure cells in collectionView:itemForRowAtIndexPath:.
To get around the "view cannot be reached using cellForItemAtIndexPath:, but will not be recycled before being shown on-screen" issue, you can move the view initialization logic from collectionView:cellForItemAtIndexPath: to collectionView:willDisplayCell:forItemAtIndexPath:.
For example, where you originally had:
override func collectionView(
_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath
) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(
withReuseIdentifier: self.reuseIdentifier,
for: indexPath
)
// Initialize cell
return cell
}
You can replace it with:
override func collectionView(
_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath
) -> UICollectionViewCell {
return collectionView.dequeueReusableCell(
withReuseIdentifier: self.reuseIdentifier,
for: indexPath
)
}
override func collectionView(
_ collectionView: UICollectionView,
willDisplay cell: UICollectionViewCell,
forItemAt indexPath: IndexPath
) {
// Initialize cell
}
This ensures that if cellForItemAtIndexPath: returns nil, the cell will be properly initialized by collectionView:willDisplayCell:forItemAtIndexPath: the next time before it is shown on screen.