Crash in iOS18

I have a UITableView which contains a UICollectionView in the first row. It used to work fine in iOS17, but now I get a crash when running with Xcode 16 / iOS18 beta:

Expected dequeued view to be returned to the collection view in preparation for display. When the collection view's data source is asked to provide a view for a given index path, ensure that a single view is dequeued and returned to the collection view. Avoid dequeuing views without a request from the collection view. For retrieving an existing view in the collection view, use -[UICollectionView cellForItemAtIndexPath:] or -[UICollectionView supplementaryViewForElementKind:atIndexPath:]

This is my UITableView delegate call:

    AddEditDataCell *cell = nil;
    if (indexPath.section == 0) {
        
        if (indexPath.row == 0) {
            AddEditDataContactsCell *contactNameCell = (AddEditDataContactsCell *)[self cellForContactNamesCollectionAtIndexPath:indexPath tableView:tableView];
            return contactNameCell; 


- (AddEditDataContactsCell *)cellForContactNamesCollectionAtIndexPath:(NSIndexPath *)indexPath tableView:(UITableView *)tableView {
    AddEditDataContactsCell *contactsCell = (AddEditDataContactsCell *)[self.tableView dequeueReusableCellWithIdentifier:@"ContactsCell" forIndexPath:indexPath];
    if (self.collectionNameCell == nil) {
        self.collectionNameCell = [contactsCell.collectionView dequeueReusableCellWithReuseIdentifier:@"LogContactNameCollectionCellIdentifier" forIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
        contactsCell.nameCellDelegate = self;
    }

    contactsCell.frame = CGRectZero;
    [contactsCell setNeedsLayout];
    [contactsCell.collectionView reloadData];
    
    contactsCell.collectionViewHeightConstraint.constant = contactsCell.collectionView.collectionViewLayout.collectionViewContentSize.height;
    
    [contactsCell.collectionView.collectionViewLayout invalidateLayout];
    return contactsCell;
}

@zulfishah Could you create a test project and share a link to the project. I'd be happy to take a look. Creating a test project, goes over the criteria for a focused test project.

We had a similar issue. Turns out we were calling dequeReusableCellWithIdentifier: to create a cell in another method which we used for size estimates, and it seems like on ios 18 the UICollectionView really does not like that. If you deque a cell it expects you to give it that cell, eg in response to cellForItemAtIndexPath.

Changing that other cell creation to just alloc/init fixed the crash for us.

Try "self.collectionNameCell = [ .... alloc] init];" instead of deque.

12

I have experiences a similar issue where when running the code using Xcode 16.0 / iOS 18, the app will crash. The trigger point is where instead of dequeueReusableCell, I pull from the previously saved "self.bannerItem" and the app crash with the follow error.

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'The collection view's data source returned a cell that is in the reuse queue. Cells must be retrieved by calling -dequeueConfiguredReusableCellWithRegistration:forIndexPath:item: or -dequeueReusableCellWithReuseIdentifier:forIndexPath:. Collection view: <UICollectionView: 0x107824200; frame = (0 59; 393 759); clipsToBounds = YES; gestureRecognizers = <NSArray: 0x600000c6eca0>; backgroundColor = <UIDynamicSystemColor: 0x600001767040; name = systemBackgroundColor>; layer = <CALayer: 0x600000248c60>; contentOffset: {0, 766.33333333333337}; contentSize: {393, 2340}; adjustedContentInset: {0, 0, 0, 0}; layout: <UICollectionViewFlowLayout: 0x10690c380>; dataSource: <AppTestiOS18.ViewController: 0x10690cd20>>; index path: (0-0); cell: <AppTestiOS18.ItemCollectionViewCell: 0x105707530; baseClass = UICollectionViewCell; frame = (46.6667 1740; 300 600); hidden = YES; layer = <CALayer: 0x60000024e6a0>>'

The actual code can run using Xcode 15.4 and iOS 17 without any issues

class ItemCollectionViewCell:UICollectionViewCell {
    static let cellIdentifier = "\(type(of: ItemCollectionViewCell.self))"
}

class ViewController: UIViewController {
    
    struct ContentCellItem {
        let height:CGFloat
    }
    
    enum ContentSection {
        case TopBanner(ContentCellItem)
        case FeatureBanner(ContentCellItem)
        case Title(ContentCellItem)
        case Webconent(ContentCellItem)
        case RelatedArticle(ContentCellItem)
    }
    
    let dataSrc:[ContentSection] = {
        var v:[ContentSection] = []
        v.append(.TopBanner(.init(height: 200)))
        v.append(.FeatureBanner(.init(height: 300)))
        v.append(.Title(.init(height: 400)))
        v.append(.Webconent(.init(height: 800)))
        v.append(.RelatedArticle(.init(height: 600)))
        return v
        
    }()

    let collectionView:UICollectionView = {
        let t =  UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())
        
        t.register(ItemCollectionViewCell.classForCoder(), forCellWithReuseIdentifier: ItemCollectionViewCell.cellIdentifier)
        return t
    }()
    
    private var bannerItem:ItemCollectionViewCell!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
        collectionView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(collectionView)
        view.leadingAnchor.constraint(equalTo: collectionView.leadingAnchor).isActive = true
        view.trailingAnchor.constraint(equalTo: collectionView.trailingAnchor).isActive = true
        view.safeAreaLayoutGuide.topAnchor.constraint(equalTo: collectionView.topAnchor).isActive = true
        view.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: collectionView.bottomAnchor).isActive = true
        
        collectionView.layer.borderColor = UIColor.red.cgColor
        collectionView.layer.borderWidth = 3.0
        
        collectionView.delegate = self
        collectionView.dataSource = self
    }
}

extension ViewController: UICollectionViewDataSource {
    
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return dataSrc.count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        
        let section = dataSrc[indexPath.row]
        switch section {
        case .TopBanner(_):
            
            if self.bannerItem == nil {
                guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier:ItemCollectionViewCell.cellIdentifier , for: indexPath) as? ItemCollectionViewCell else {
                    return UICollectionViewCell()
                }
                self.bannerItem = cell
            }
            
            self.bannerItem.layer.borderColor = UIColor.green.cgColor
            self.bannerItem.layer.borderWidth = 1.0
            return bannerItem
            
        default:
            guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier:ItemCollectionViewCell.cellIdentifier , for: indexPath) as? ItemCollectionViewCell else {
                return UICollectionViewCell()
            }
            cell.layer.borderColor = UIColor.blue.cgColor
            cell.layer.borderWidth = 1.0
            return cell
        }
    }
    
}


extension ViewController: UICollectionViewDelegateFlowLayout {
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        
        let cellWidth:CGFloat = 300
        
        guard indexPath.row < dataSrc.count else {
            return CGSize(width: cellWidth, height: 1)
        }

        let section = dataSrc[indexPath.row]
        
        
        switch section {
        case .TopBanner(let item):
            return CGSize(width: cellWidth, height: item.height)
        case.FeatureBanner(let item):
            return CGSize(width: cellWidth, height: item.height)
        case .Title(let item):
            return CGSize(width: cellWidth, height: item.height)
        case .Webconent(let item):
            return CGSize(width: cellWidth, height: item.height)
        case .RelatedArticle(let item):
            return CGSize(width: cellWidth, height: item.height)
        }
    }
}

I had similar issue with iOS18.(issue exists in below code)

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "FTShelfTagsPageCell", for: indexPath) as? FTShelfTagsPageCell else { return UICollectionViewCell() } cell.selectionBadge?.isHidden = viewState == .none ? true : false

    if indexPath.section == 0 {
        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "FTShelfTagsBooksCell", for: indexPath) as? FTShelfTagsBooksCell else {
            return UICollectionViewCell()
        }
        return cell
    } else if indexPath.section == 1 {
        let item = pages[indexPath.row]
        cell.updateTagsItemCellContent(tagsItem: item, isRegular: self.traitCollection.isRegular)
    }
    cell.isSelected = true
    return cell
}

It is dequeuing 2 cells though I return single cell.

After simplification like below, issue got fixed. (only 1 cell ll be dequeued at a time.)

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    if indexPath.section == 0 {
        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "FTShelfTagsBooksCell", for: indexPath) as? FTShelfTagsBooksCell else {
            return UICollectionViewCell()
        }
        return cell
    } else  {
        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "FTShelfTagsPageCell", for: indexPath) as? FTShelfTagsPageCell else {
            return UICollectionViewCell()
        }
        let item = pages[indexPath.row]
        cell.selectionBadge?.isHidden = viewState == .none ? true : false
        cell.updateTagsItemCellContent(tagsItem: item, isRegular: self.traitCollection.isRegular)
        cell.isSelected = true
        return cell
    }
}

Can confirm that I had this issue and did exactly what @Narayana-M_7 did, I was dequeuing two times for the same returned cell. Once I made sure there is only one dequeueing happening, the crash vanished.

I came across this crash as well. It seemed to be because we were manually dequeuing supplementary view in the func collectionView(collectionView:layout:referenceSizeForHeaderInSection:) -> CGSize.

Once I found a way to use supplementaryView instead of dequeueReusableSupplementaryView, the issue was resolved

let headerView = collectionView.supplementaryView(forElementKind: UICollectionView.elementKindSectionHeader, at: indexPath)

Xcode 16 was also complaining/suggesting to use dequeueConfiguredReusableSupplementary instead of dequeueReusableSupplementaryView(ofKind:withReuseIdentifier:for:).

We had a mix of iOS 14+ registration APIs for regular cells but older registration style for supplementary views. I updated to use UICollectionView.SupplementaryRegistration at the same time, which might've helped the issue as well

Hope these help

I've the same problem, but as far I can see I don't dequeue twice. Can someone help me out here?

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "NieuwsCollectionViewControllerCell", for: indexPath) as! NieuwsCollectionViewCell
                
        let feedRow = MyNewsLoader.shared.returnNews()[indexPath.row]
        
        let feedTitle = feedRow.title
        let feedDescription = feedRow.description?.trimHTMLTags()
        let feedLink = feedRow.link
        
        cell.feedTitle?.text = feedTitle
        cell.feedDescription?.text = feedDescription
              
        return cell
            
    }

What going wrong here?

indeed, for my case, iOS18 the crash point is here,,,

  • more than 1 dequeue
  • fix by recheck logic/code-flow
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> MyCell {
 
 	let cell1 = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as? MyCell
 	let cell2 = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as? MyCell
 	let cell3 = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as? MyCell
 	let cell4 = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as? MyCell

return cell1
}

We are also experiencing an app crash issue in the viewForSupplementaryElementOfKind method:

func collectionView(_ collectionView: UICollectionView,

                    viewForSupplementaryElementOfKind kind: String,

                    at indexPath: IndexPath) -> UICollectionReusableView {

    switch kind {

    case UICollectionView.elementKindSectionHeader:

        let headerView = collectionView.dequeueReusableSupplementaryView(

            ofKind: kind,

            withReuseIdentifier: headerViewIdentifier, for: indexPath)

        return headerView

    case UICollectionView.elementKindSectionFooter:

        let footerView = collectionView.dequeueReusableSupplementaryView(

            ofKind: kind,

            withReuseIdentifier: footerViewIdentifier, for: indexPath)

        return footerView
   default:
        break
    }
}

As @KSmith95 mentioned, we also tried using supplementaryView, but it always returns nil and leading the app crash.

In case this helps anyone else. I was seeing the same crash. I eventually solved it by setting a symbolic break point on:

-[UICollectionView dequeueReusableSupplementaryViewOfKind:withReuseIdentifier:forIndexPath:]

Turns out the app was dequeuing a cell to calculate size elsewhere in the app.

We came across this same crash recently, but it was slightly different to all the above cases I think, so hopefully this helps someone.

We were actually already using cellForItemAtIndexPath: rather than directly dequeuing a cell, but were calling the UICollectionViewDataSource's implementation in our viewController instead of directly on the UICollectionView, and so of course calling the data-source's method will in turn call dequeueReusableCell e.g.

[self collectionView:self.collectionView cellForItemAtIndexPath:indexPath];

instead of:

[self.collectionView cellForItemAtIndexPath:indexPath];

Changing to the latter fixed the issue.

Our app had the same crash. We were trying to access a mockCell used for calculating the height of the cell before the collection view was accessible. After some research, we realized we don't need the heightForContent() anymore. Here is the code change:

❌ Before:

func size(for indexPath: IndexPath) -> CGSize {
  let mockCell = settingItemCell(collectionView, indexPath: indexPath) as? SettingButtonCell {
                height = mockCell.heightForContent(withWidth: contentWidth)
}

✅ After:

func size(for indexPath: IndexPath) -> CGSize {
  height = 40
}

The following line was causing the crash: let mockCell = settingItemCell(collectionView, indexPath: indexPath) as? SettingButtonCell

Can anyone help me to resolve this issue? I have two collectionviews in a tableview and each time you try to scroll the collecctionview after clicking a cell, it crashes with the following error:

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Expected dequeued view to be returned to the collection view in preparation for display. When the collection view's data source is asked to provide a view for a given index path, ensure that a single view is dequeued and returned to the collection view. Avoid dequeuing views without a request from the collection view. For retrieving an existing view in the collection view, use -[UICollectionView cellForItemAtIndexPath:] or -[UICollectionView supplementaryViewForElementKind:atIndexPath:]

I'm using

let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! NearbyNTOcolViewCell

I am gettin same error but I cant figure out whats wrong here. The above solutions dont even apply because I have only 1 cell to dequeue. It was working until I upgraded to iOS 18 / Xcode 16

I am using UICollectionViewCompositionalLayout for estimated height and 1.0 fractional width.

Theres only 1 section in the collection view with 7 items in that section. And again, only one line of collectionView.dequeueReusableCell.

When I run, the first two cell display fine, but the moment I scroll to load more cells, its breaks with below error.

Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Expected dequeued view to be returned to the collection view in preparation for display. When the collection view's data source is asked to provide a view for a given index path, ensure that a single view is dequeued and returned to the collection view. Avoid dequeuing views without a request from the collection view. For retrieving an existing view in the collection view, use -[UICollectionView cellForItemAtIndexPath:] or -[UICollectionView supplementaryViewForElementKind:atIndexPath:]

My case doesn't fall under all these. I've multiple collectionviews in my storyboard and assigned delegate & datasource to my class there and over riding the same in my class via code. Now when i remove all those unnessary datasource & delegates in storyboard, its working fine.

Crash in iOS18
 
 
Q