Checking if view is added on didSelectItem - Swift - ios

I'm hoping you can help me with my code. I'm trying to make it so that when a cell is clicked it checks if the view has already been added. If it has then it dismisses the view and re-adds it. If it doesn't then it simply adds the view.
Here's my current code:
func collectionView(_ collectionView: UICollectionView,
didSelectItemAt indexPath: IndexPath) {
let vc = NoteViewController()
if self.view.subviews.contains(vc.view) {
print("view removed")
}
else {
let cell = collectionView.cellForItem(at: indexPath) as! AnnotatedPhotoCell
sourceCell = cell
vc.picture = resizeImage(image: cell.imageView.image!,
targetSize: CGSize(width:(view.bounds.width - 45),height: 0))
vc.comment = cell.commentLabel
vc.parentVC = parentVC.self
parentVC.self.addChildViewController(vc)
parentVC.self.view.addSubview(vc.view)
vc.didMove(toParentViewController: self)
// 3- Adjust bottomSheet frame and initial position.
let height = view.frame.height
let width = view.frame.width
vc.view.frame = CGRect(x:0, y:self.view.frame.maxY,
width: width, height: height)
}
}
Hoping you can help me with this.

Related

Hide UICollectionViewCell Drag Preview

i'm implementing drag and drop in collection view by using UICollectionViewDragDelegate and tried to hiding the drag preview while dragging
i manage to hide it by using this line of codes after following this thread Custom View for UICollectionViewCell Drag Preview:
public func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
let dragItem = UIDragItem(itemProvider: NSItemProvider())
dragItem.previewProvider = {
return nil
}
}
but the drag preview is still displayed when it's being lifted and the only method that allow me to modify the drag preview during lifting is
public func collectionView(_ collectionView: UICollectionView, dragPreviewParametersForItemAt indexPath: IndexPath) -> UIDragPreviewParameters? {
let previewParameters = UIDragPreviewParameters()
previewParameters.visiblePath = UIBezierPath(roundedRect: CGRect(x: 0, y: 0, width: 50, height: 50), cornerRadius: 0)
previewParameters.backgroundColor = UIColor.clear
return previewParameters
}
but it only allows me to set the background color not hide the drag preview
and the second way that i tried is by checking the cell state
public override func dragStateDidChange(_ dragState: UICollectionViewCell.DragState) {
switch dragState {
case .none:
self.layer.opacity = 1
case .lifting:
self.layer.opacity = 0
case .dragging:
self.layer.opacity = 1
}
}
but it's also not working
do any of you know how to hide this? or atleast hide the border and the shadow is also could solve this problem
here is the lifted cell
finally i found out the solution, the drag preview actually is named as _UIPlatterView (after debug hierarchy it) and it's child view named as _UIPortalView that block the cell during long press / lifting
and as solution of this post, just subclass the collection view and remove the the child view of _UIPlatterView
How to hide shadows in UITableViewCell when cell is dragging
public class CustomCollectionView: UICollectionView {
override public func didAddSubview(_ subview: UIView) {
super.didAddSubview(subview)
if "\(type(of: subview))" == "_UIPlatterView" {
subview.subviews.forEach({ $0.removeFromSuperview() })
}
}
}
but it's not the end, the above solution still shows the drag preview in just a little second and i add this code to clean it
extension ExampleViewController: UICollectionViewDragDelegate {
public func collectionView(_ collectionView: UICollectionView, dragPreviewParametersForItemAt indexPath: IndexPath) -> UIDragPreviewParameters? {
guard let currentCell: MUICalendarCollectionViewCell = collectionView.cellForItem(at: indexPath) as? MUICalendarCollectionViewCell else { return nil }
let previewParameters = UIDragPreviewParameters()
let path = UIBezierPath(rect: CGRect.zero)
previewParameters.visiblePath = path
previewParameters.backgroundColor = MUIColor.clear
return previewParameters
}
}

UICollectionView reloadData() causes cells to overlap

Intro
Every time a user submits a comment, it is uploaded to the database. After that happened, the collectionView is reloaded. However, the reload does not have the expected behavior. Instead of adding the one comment to the end of the collectionView, it seems to randomly stack some other comment-cells and only display two comment cells, no matter how many comments there are.
This is the output of the View Debugger, you can clearly see that the collectionView cells are stacked:
At this point I would like to clarify that this is not a sizing error of the cells. They all have the correct size assigned, and if I call reloadData on the collectionView, at a later point in time, all the cells are displayed correctly.
I think things will become more clear if you take a look at the gif:
I really don't have any idea where this error comes from or how to fix it.
Code
Initialization of collectionView
private lazy var timelineCollectionView: UICollectionView = {
let flowLayout = UICollectionViewFlowLayout()
flowLayout.scrollDirection = .vertical
flowLayout.estimatedItemSize = CGSize(width: self.view.frame.width, height: 10)
flowLayout.minimumInteritemSpacing = 0
flowLayout.minimumLineSpacing = 0
let cv = UICollectionView(frame: .zero, collectionViewLayout: flowLayout)
cv.contentInsetAdjustmentBehavior = .never
cv.backgroundColor = .clear
cv.scrollIndicatorInsets = UIEdgeInsets(top: self.coloredTitleBarHeight - self.getStatusBarHeight(), left: 0, bottom: 0, right: 0)
cv.delegate = self
cv.dataSource = self
cv.register(TLContentCell.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "content-header-cell")
cv.register(TLContentCell.self, forCellWithReuseIdentifier: "comment-cell")
return cv
}()
Code of the UICollectionViewDelegate methods:
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
let cell = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "content-header-cell", for: indexPath) as! TLContentCell
cell.isHeaderCell = true
cell.timelineContent = tlContentItem
cell.delegate = self
cell.isUserInteractionEnabled = true
return cell
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return commentItems.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "comment-cell", for: indexPath) as! TLContentCell
cell.delegate = self
cell.timelineContent = TLContent(nativeContentItem: commentItems[indexPath.row])
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
let indexPath = IndexPath(row: 0, section: section)
let headerView = self.collectionView(collectionView, viewForSupplementaryElementOfKind: UICollectionView.elementKindSectionHeader, at: indexPath) as! TLContentCell
return headerView.contentView.systemLayoutSizeFitting(CGSize(width: collectionView.frame.width, height: UIView.layoutFittingExpandedSize.height),
withHorizontalFittingPriority: .required, // Width is fixed
verticalFittingPriority: .fittingSizeLevel) // Height can be as large as needed
}
The code of the comment-upload handler function:
#objc func submitComment() {
submitCommentButton.isEnabled = false
submitCommentButton.backgroundColor = ColorCodes.lightGray
guard let user = Auth.auth().currentUser else {
print("No user detected. Redirecting to the login screen!")
self.displayWelcomeScreen()
return
}
guard commentTextView.text.count > 0, let commentContent = commentTextView.text else { print("Submission of comment aborted due to empty content."); return }
commentTextView.resignFirstResponder()
commentTextView.text = ""
calculateTextViewSize()
ContentUploader.uploadComment(uid: user.uid, content: NativeContentBase(msg: commentContent, usersTagged: [], topicsTagged: [], mediaAssigned: []), referencedContent: tlContentItem.nativeContent.contentId) { (error, comment) in
if let err = error {
print("An error occured during the comment upload: ", err)
return
}
print("Successfully uploaded comment!")
self.commentItems.append(comment)
DispatchQueue.main.async {
print(self.commentItems.count)
self.timelineCollectionView.reloadData()
}
}
print("Comment scheduled for submission!")
}
I also made sure that I am in the main thread when calling the reloadData() functionality. However, the weird behavior persists.
In order to adjust the collectionView's contentInset and scrollIndicatorInsets to the display of the keyboard, I am using these two functions:
private func adjustScrollBehavior() {
let window = UIApplication.shared.windows.filter {$0.isKeyWindow}.first
let bottomSafeAreaHeight = window?.safeAreaInsets.bottom ?? 0
// adjust the content Inset of the timelineCollectionView
let heightOfCommentSection: CGFloat = commentTextViewHeightAnchor.constant + abs(commentTextViewBottomAnchor.constant) + 8 // get the absolute value of the bottomAnchor's constant as it would be negative
timelineCollectionView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: heightOfCommentSection + bottomSafeAreaHeight, right: 0) // does not automatically take care of safe area insets
timelineCollectionView.scrollIndicatorInsets = UIEdgeInsets(top: 0, left: 0, bottom: heightOfCommentSection, right: 0) // automatically takes care of safe area insets
}
func calculateTextViewSize() {
let minimumSize = commentTextView.layoutMargins.top + commentTextView.layoutMargins.bottom + commentTextView.font!.lineHeight
let fixedWidth = self.view.frame.width - 32
let maximumSize: CGFloat = 155
let newSize = commentTextView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude))
print(newSize)
if newSize.height > minimumSize && newSize.height <= maximumSize {
commentTextViewHeightAnchor.constant = newSize.height
} else {
commentTextViewHeightAnchor.constant = newSize.height > minimumSize ? maximumSize : minimumSize
}
adjustScrollBehavior()
}
Closing
I know this is a lot of code, but basically, that's everything I use. I have no idea why this happens, and I would be incredibly grateful for any help I could get.
Thanks a lot for your help :)

Single CollectionView with multiple CollectionViewLayout.why Layout is mess up?

I have Single UICollectionView , and I want to Apply Two different layout dynamically.
UICollectionViewFlowLayout : A Layout with same size cell and circle image.
var flowLayout: UICollectionViewFlowLayout {
let flowLayout = UICollectionViewFlowLayout()
flowLayout.itemSize = CGSize(width: UIScreen.main.bounds.width/3, height: 140)
flowLayout.sectionInset = UIEdgeInsetsMake(0, 0, 0, 0)
flowLayout.scrollDirection = UICollectionViewScrollDirection.vertical
flowLayout.minimumInteritemSpacing = 0.0
return flowLayout
}
Pintrest Layout :
https://www.raywenderlich.com/392-uicollectionview-custom-layout-tutorial-pinterest
For Example : when user Click on Profile Button FlowLayout will be Apllied and Cell Appear with image in Circle Shape. when user click on Picture button pintrest layout will be Applied and cell Appear with image in Rectangle shape with dynamic height.
intially CollectionView have 1.flowLayout and it appears perfectly.but when I click on Picture button Pintrest layout is messed up with previous layout as shown in above image.
Following is Code For changing Layout.
if isGrid {
let horizontal = flowLayout
recentCollectionView.setCollectionViewLayout(horizontal, animated: true)
recentCollectionView.reloadData()
}
else {
let horizontal = PinterestLayout()
horizontal.delegate = self
recentCollectionView.setCollectionViewLayout(horizontal, animated: true)
recentCollectionView.reloadData()
}
ViewHiarchy:
I have main Collection-view that Contain header and one bottom cell.cell contain other Collection-view to which I am Applying multiple layout.I have Two Different cell for each layout.I want bottom cell size equal to content Collection-view Content size so user can Scroll entire main collection-view vertically.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
var cell : UICollectionViewCell!
switch isGrid {
case true:
cell = collectionView.dequeueReusableCell(withReuseIdentifier: "SearchProfileCell", for: indexPath)
if let annotateCell = cell as? SearchProfileCell {
annotateCell.photo = photos[indexPath.item]
}
case false:
cell = collectionView.dequeueReusableCell(withReuseIdentifier: "AnnotatedPhotoCell", for: indexPath)
if let annotateCell = cell as? AnnotatedPhotoCell {
annotateCell.cellwidth = collectionView.contentSize.width/3
annotateCell.photo = photos[indexPath.item]
}
}
cell.contentView.layer.cornerRadius = 0
return cell
}
Code of profile and picture button Action.
#IBAction func pictureClick(sender:UIButton) {
isGrid = false
self.searchCollectionView.reloadData()
}
#IBAction func profilClick(sender:UIButton) {
isGrid = true
self.searchCollectionView.reloadData()
}
I think problem is not inside layout but might be inside cellForItemAt. if you are using different cell for both layout then do not compare bool at cellForItemAt method. you should compare layout class type
like below code :
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if collectionView.collectionViewLayout.isKind(of: PinterestLayout.self) {
// return cell for PinterestLayout
guard let annotateCell = collectionView.dequeueReusableCell(withReuseIdentifier: "SearchProfileCell", for: indexPath) as? SearchProfileCell else {
fatalError("SearchProfileCell Not Found")
}
annotateCell.photo = photos[indexPath.item]
return annotateCell
} else {
// return cell for flowLayout
guard let annotateCell = collectionView.dequeueReusableCell(withReuseIdentifier: "AnnotatedPhotoCell", for: indexPath) as? AnnotatedPhotoCell else {
fatalError("AnnotatedPhotoCell Not Found")
}
annotateCell.cellwidth = collectionView.contentSize.width/3
annotateCell.photo = photos[indexPath.item]
return annotateCell
}
}
Also need to update layout change action methods like:
#IBAction func pictureClick(sender:UIButton) {
isGrid = false
self.collectionView?.collectionViewLayout.invalidateLayout()
self.collectionView?.setCollectionViewLayout(PinterestLayout(),
animated: false, completion: { [weak self] (complite) in
guard let strongSelf = self else {
return
}
strongSelf.searchCollectionView?.reloadData()
})
}
#IBAction func profilClick(sender:UIButton) {
isGrid = true
self.collectionView?.collectionViewLayout.invalidateLayout()
self.collectionView?.setCollectionViewLayout(flowLayout,
animated: false, completion: { [weak self] (complite) in
guard let strongSelf = self else {
return
}
strongSelf.searchCollectionView?.reloadData()
})
}
Why you are using two different layout even though you can achieve same Result with pintrestLayout. https://www.raywenderlich.com/392-uicollectionview-custom-layout-tutorial-pinterest.
Check pintrestLayout carefully , it have Delegate for Dynamic height.
let photoHeight = delegate.collectionView(collectionView, heightForPhotoAtIndexPath: indexPath)
if you return static height here , your pintrest layout become GridLayout(your First Layout).
if you want pintrest layout as work for both layout , you need to declare same Boolean(isGrid) in pintrest layout.and use this boolean to return UICollectionViewLayoutAttributes
more important raywenderlich pintrest layout uses cache to store layout attribute.you have to remove cache object before applying other layout.
Check in this tutorial , how same layout used for grid,list and linear.
https://benoitpasquier.com/optimise-uicollectionview-swift/
what you need in your layout.
var isGrid : Bool = true {
didSet {
if isGrid != oldValue {
cache.removeAll()
self.invalidateLayout()
}
}
}

UICollectionView will recreate cells when calling the "reloadItems" method

My cell has an image which be downloaded from network, therefore I need to set the height of cell as dynamic.
When an image download is finished, I am going to call self.collectionView.reloadItems(at: [indexPath]) to trigger the delegate method for setting a new height.
But it seems that the reloadItems method will recreate a cell, not just re-layout an original reuse cell.
How can I solve this problem? Is it a bug on UICollectionView from apple or something wrong I did?
Whole code:
// code from ViewController
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: AnnounmentWallCollectionViewCell.cellIdentifier, for: indexPath) as! AnnounmentWallCollectionViewCell
let announcement = announcements[indexPath.row]
cell.collectionView = collectionView
cell.setBanner(from: announcement.banner, indexPath: indexPath, completion: { [unowned self] (height) in
self.bannersHeight[indexPath.row] = height
})
cell.setHTMLContent(announcement.content)
contentsHeight[indexPath.row] = cell.htmlContentSize.height
printD("indexPath: \(indexPath)")
return cell
}
// code from cell
func setBanner(from url: URL?, indexPath: IndexPath, completion: #escaping (_ height: CGFloat)->()) {
// URL(string: "https://i.imgur.com/qzY7BJ9.jpg")
if let url = url {
if let banner = SDImageCache.shared().imageFromDiskCache(forKey: url.absoluteString) {
self.bannerView.isHidden = false
self.bannerView.image = banner.scaleWidth(to: self.bounds.width - 32) // leading + trailling
self.bannerHeight.constant = self.bannerView.image?.size.height ?? 1
completion(self.bannerHeight.constant)
printD("NO Download: \(indexPath)")
let animationsEnabled = UIView.areAnimationsEnabled
UIView.setAnimationsEnabled(false)
self.collectionView.reloadItems(at: [indexPath])
UIView.setAnimationsEnabled(animationsEnabled)
} else {
DispatchQueue.global().async {
SDWebImageDownloader.shared().downloadImage(with: url, options: .useNSURLCache, progress: nil) { (banner, data, error, finished) in
DispatchQueue.main.async {
if let banner = banner {
SDImageCache.shared().store(banner, forKey: url.absoluteString, toDisk: true)
self.bannerView.isHidden = false
self.bannerHeight.constant = banner.scaleWidth(to: self.bounds.width - 32)?.size.height ?? 1
completion(self.bannerHeight.constant)
self.collectionView.reloadData()
printD("Download: \(indexPath): \(self.bannerHeight.constant)")
} else {
self.bannerView.isHidden = true
self.bannerHeight.constant = 1
completion(self.bannerHeight.constant)
}
}
}
}
}
} else {
bannerView.isHidden = true
bannerHeight.constant = 1
completion(bannerHeight.constant)
}
}
// code from delegate
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = self.view.bounds.width
let height = bannersHeight[indexPath.row] + contentsHeight[indexPath.row]
+ 1 // sticker
+ 11 // banner top
printD("indexPath: \(indexPath): \(height)")
return CGSize(width: width, height: height)
}
That's not a bug. That's how you reload a cell for a given index path. If you only want to update the layout you can also try
[self.collectionView.collectionViewLayout invalidateLayout]
instead of
self.collectionView.reloadItems(at: [indexPath])
and then return the proper size in the delegate method.
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
return //whatever size that you want to return.
}
I would also highly suggest you to cache your image sizes so that you can use that for the next time, instead of downloading/calculating over an over...

Retaining button content in UICollectionView after scrolling with swift

I'm trying to make a UICollectionView that has infinite scrolling of buttons and the button's background is populated base on the result of http request to a server.
let reuseIdentifier = "Cell"
let screenSize: CGRect = UIScreen.mainScreen().bounds
let screenWidth = screenSize.width
let screenHeight = screenSize.height
let categoryApiUrl = "url"
let categoryImageField = "field"
class BrowseViewController: UICollectionViewController, UICollectionViewDataSource, UICollectionViewDelegate {
var categoryImgUrl:[String] = []
var buttonList:[UIButton] = []
func setupView(){
self.title = "Browse"
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 20, left: 10, bottom: 10, right: 10)
layout.itemSize = CGSize(width: screenWidth/2-15, height: screenHeight/3.5)
collectionView = UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
collectionView!.dataSource = self
collectionView!.delegate = self
collectionView!.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)
collectionView!.backgroundColor = UIColor.whiteColor()
self.view.addSubview(collectionView!)
}
func setupButton(cell: UICollectionViewCell, cellNumber: Int){
var button = UIButton.buttonWithType(UIButtonType.System) as UIButton
button.frame = CGRectMake(0, 0, screenWidth/2-15, screenHeight/3.5)
button.backgroundColor = UIColor.orangeColor()
button.setTitle("Category", forState: UIControlState.Normal)
button.addTarget(self, action: "btnClicked:", forControlEvents: UIControlEvents.TouchUpInside)
buttonList.append(button)
cell.addSubview(button)
}
override func viewDidLoad(){
super.viewDidLoad()
let url = NSURL(string: categoryApiUrl)
let request = NSURLRequest(URL: url!)
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()) {(response, dataValue, error) in
let json = JSON(data: dataValue)
for(var i = 0; i < json.count; i++){
self.categoryImgUrl.append(json[i]["CATEGORY_IMAGE"].stringValue)
let imageUrl = self.categoryImgUrl[i]
let url = NSURL(string: imageUrl)
let data = NSData(contentsOfURL: url!)
let image = UIImage(data: data!)
self.buttonList[i].setBackgroundImage(image, forState: .Normal)
}
}
override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
//#warning Incomplete method implementation -- Return the number of sections
return 1
}
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
//#warning Incomplete method implementation -- Return the number of items in the section
return 10;
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as UICollectionViewCell
let cellNumber = indexPath.row as Int
setupButton(cell, cellNumber: cellNumber)
// Configure the cell
return cell
}
override func scrollViewDidScroll(scrollView: UIScrollView) {
let offsetY = scrollView.contentOffset.y
let contentHeight = scrollView.contentSize.height
if offsetY > contentHeight - scrollView.frame.size.height {
numberOfItemsPerSection += 6
self.collectionView!.reloadData()
}
}
}
Currently, the code is able to pull the image from the server and populate it as the button's background image.
However, since I made this collection view scrollable. When I scroll the view down and then back up, the background image of the previous buttons disappear.
I did some research but couldn't find a solution to it. The reason that the button disappears is because IOS only loads the cell that is visible on screen. So when I scroll down and then scroll back up, the previous cells are consider as "New Cells". Therefore the background image that was in it are now gone.
Questions:
Does anyone have an idea on how to retain the previous buttons even if we scroll down and then scroll back up? In addition, with my current code, I added the image onto the button inside the http request because the http request is always the last execution that finishes. Is there anyway to change the code so then the http request will be finish before the cells get loaded?
I would suggest to create uicollectionview in interface builder, subclass uicollectionviewcell, add one dynamic cell to collectionview, change its class to collectionviewcell you subclassed, drop uibutton on it, and everytime cell is being created, you would download the image.
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as myCollectionViewCell
let cellNumber = indexPath.row as Int
//downloadimghere
cell.myButton.setBackgroundImage(downloadedImg, forState: .Normal)
return cell
}
This would download image everytime cell is being created. For more info you should checkout "lazy image loading". I think this is a better approach to your problem.
Now to your code, first of all you are not using your buttonList array, everytime cell is being created you create a new button and place it there, so you are not reusing already created buttons. If you fixed this, it might work like you wanted.
Here is another problem, since collectionview is reusing cells, everytime you create a button and place it on cell, it stays there, so basically now you are creating button on button. So if you want this to work correctly and have only one button on your cell, you need to remove previous button from the cell before you create it, you can do this in cellForItemAtIndexPath.
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as myCollectionViewCell
//something like this
for view in cell.subviews(){
if view == <UIButton>{
view.removeFromSuperview()
}
}
return cell
}
There might be some syntax errors in my code, I didnt test it, but you get the idea how to do it.

Resources