Original question was posted on stack overflow here: http://stackoverflow.com/questions/35076277/uicollectionviewcell-message-sent-to-deallocated-instance-right-after-being-dequ
I have a UICollectionViewCell which is being dequeued for reuse. I recently introduced a sliding drawer functionality which is basically a hidden view which gets displayed when the user swipes.
After the last change this seemed to create a case where the instance was being deallocated while being initialized. The exact location where the Zombie was being reported was on the call to "addGestureRecognizers()" which was being called from setupSlidingDrawer() which was in turn being called from the init function.
I have attached an image of the break point before and after the Zombie call. I have been pulling my hair all day looking at this and keep drawing a blank.
As a work around (hack) I have made the deleteView a instance variable and that seems to keep the class from releasing as the retain count is not decremented.
I feel like like the code for my collection view cell / controller is pretty standard and I am not doing anything crazy.
This seems like it is a Apple bug but I can not seem to find anything on the inter webs to prove it. If
class MomentsViewCell : UICollectionViewCell, UIGestureRecognizerDelegate {
static let reuseIdentifier = "MomentsViewCell"
let imageView = UIImageView()
let activityIndicator = UIActivityIndicatorView()
let shareButton = UIButton()
var shareCallback: (() -> ())?
var isSwiped = false
let overlayViewTag = 999
var delegate: MomentsViewController?
let shareButtonLength: CGFloat = 44
lazy var drawerWidth: CGFloat = {
self.frame.width * 0.15
}()
override init(frame: CGRect) {
super.init(frame: frame)
contentView.addSubview(imageView)
imageView.contentMode = UIViewContentMode.ScaleAspectFit
imageView.snp_makeConstraints { (make) -> Void in
make.centerX.equalTo(self.contentView.snp_centerX)
make.width.equalTo(self.contentView.snp_width)
make.top.equalTo(self.snp_top)
make.bottom.equalTo(self.snp_bottom)
}
contentView.addSubview(activityIndicator)
activityIndicator.hidesWhenStopped = true
activityIndicator.snp_makeConstraints { (make) -> Void in
make.center.equalTo(self.snp_center)
}
let shareImage = UIImage(named: "share-moment")
shareButton.setImage(shareImage, forState: .Normal)
shareButton.addTarget(self, action: "didTapShareButton", forControlEvents: .TouchUpInside)
shareButton.enabled = false
shareButton.alpha = 0.0
imageView.addSubview(shareButton)
imageView.userInteractionEnabled = true
shareButton.snp_makeConstraints { (make) -> Void in
let size = CGSizeMake(shareButtonLength, shareButtonLength)
var offset = shareButtonLength / 2
if let imageLength = shareImage?.size.height {
offset = (shareButtonLength - imageLength) / 2
}
make.bottom.equalTo(self.imageView).multipliedBy(0.95).offset(offset)
make.right.equalTo(self.imageView).multipliedBy(0.95).offset(offset)
make.size.equalTo(size)
}
setupSlidingDrawer()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func prepareForReuse() {
super.prepareForReuse()
imageView.image = nil
exitDeleteMode()
}
func enableSharing() {
shareButton.enabled = true
shareButton.alpha = 1.0
}
func didTapShareButton() {
shareCallback?()
}
func setupSlidingDrawer() {
let deleteView = UIView(frame: CGRectMake(self.frame.width - drawerWidth, self.frame.origin.y, drawerWidth, self.frame.height))
deleteView.backgroundColor = UIColor.whiteColor()
let deleteGradientLayer = CAGradientLayer()
deleteGradientLayer.frame = deleteView.bounds
deleteGradientLayer.colors = [UIColor(hex: 0xE8557C).CGColor, UIColor(hex: 0xCC2B49).CGColor]
deleteView.layer.insertSublayer(deleteGradientLayer, atIndex: 0)
let trashButton = UIButton()
trashButton.setImage(UIImage(named: "trash_can"), forState: UIControlState.Normal)
trashButton.addTarget(self, action: Selector("didPressDelete"), forControlEvents: .TouchUpInside)
deleteView.addSubview(trashButton)
deleteView.bringSubviewToFront(trashButton)
trashButton.snp_makeConstraints(closure: { (make) -> Void in
make.center.equalTo(deleteView.snp_center)
make.width.equalTo(deleteView.snp_width).multipliedBy(0.354)
make.height.equalTo(deleteView.snp_height).multipliedBy(0.07)
})
contentView.addSubview(deleteView)
deleteView.snp_makeConstraints(closure: { (make) -> Void in
make.left.equalTo(self.imageView.snp_right)
make.height.equalTo(self.contentView.snp_height)
make.width.equalTo(drawerWidth)
})
addGestureRecognizers()
}
func addGestureRecognizers() {
let swipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: Selector("didSwipeCell:"))
swipeGestureRecognizer.delegate = self
swipeGestureRecognizer.direction = .Left
self.addGestureRecognizer(swipeGestureRecognizer)
}
class MomentsViewController: ContainerSubController, UICollectionViewDelegate, UICollectionViewDataSource {
var moments: [Moment] = []
let layout = UICollectionViewFlowLayout()
var collectionView = UICollectionView(frame: CGRectZero, collectionViewLayout: UICollectionViewFlowLayout())
let headerIdentifier = "headerCellIdentifier"
let headerLabelText = "Moments"
let headerCellHeight: CGFloat = 103.0
let noMomentsLabelText = "It's looking pretty empty in here! Assign a Moments action (Share Moment To Twitter) to a Hot Key to get started."
let noMomentsReuseIdentifier = "NoMoments"
var currentSwipedCellIndexPath: NSIndexPath?
override func viewDidLoad() {
super.viewDidLoad()
layout.minimumLineSpacing = 0
collectionView = UICollectionView(frame: view.frame, collectionViewLayout: layout)
collectionView.showsVerticalScrollIndicator = false
collectionView.backgroundColor = UIColor.clearColor()
collectionView.registerClass(MomentsViewCell.self, forCellWithReuseIdentifier: MomentsViewCell.reuseIdentifier)
collectionView.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: noMomentsReuseIdentifier)
collectionView.registerClass(UICollectionViewCell.self, forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: headerIdentifier)
collectionView.delegate = self
collectionView.dataSource = self
view.backgroundColor = UIColor.blackColor()
view.addSubview(collectionView)
collectionView.snp_makeConstraints { (make) -> Void in
make.height.equalTo(view)
make.width.equalTo(view)
make.center.equalTo(view)
}
setupTransitionToMainButton()
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
refreshMoments()
collectionView.reloadData()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "handleMomentSavedNotifcation", name: MomentNotifcation.MomentSaved.rawValue, object: nil)
}
//MARK: UICollectionViewDataSource
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
guard let momentsViewCell = collectionView.dequeueReusableCellWithReuseIdentifier(MomentsViewCell.reuseIdentifier, forIndexPath: indexPath) as? MomentsViewCell, moment = moments[safe:indexPath.row] else {
return MomentsViewCell()
}
[![enter image description here][1]][1]
[![enter image description here][2]][2]