Make blazing fast lists and collection views

RSS for tag

Discuss the WWDC21 session Make blazing fast lists and collection views.

View Session

Posts under wwdc21-10252 tag

13 Posts
Sort by:
Post not yet marked as solved
1 Replies
2.4k Views
How to remove the spacing between List plain style section in swiftui iOS 15 struct ContentView: View {   var body: some View {     List {       Section {         Text("Hello, World!")         Text("Hello, World!")         Text("Hello, World!")       }       Section {         Text("Hello, World!")       }       Section {         Text("Hello, World!")         Text("Hello, World!")       } header: {         Text("section")       }     }.listStyle(.plain)   } }
Posted
by
Post marked as solved
1 Replies
788 Views
I was trying to have a secondary collection view with circle images above (y axis) the functioning collection view but its crashing and throwing this error Error code: could not dequeue a view of kind: UICollectionElementKindCell with identifier CircleCollectionViewCell - must register a nib or a class for the identifier or connect a prototype cell in a storyboard ViewController code: // //  ViewController.swift //  Main_app // //  Created by Kushagr Agarwal on 23/08/21. // import UIKit class ViewController: UIViewController {     @IBOutlet var collectionView: UICollectionView!          @IBOutlet var topCollectionView: UICollectionView!          @IBOutlet weak var Leading: NSLayoutConstraint!          @IBOutlet weak var Trailing: NSLayoutConstraint!          @IBOutlet private weak var Menubutton: UIButton!     var menuOut = false               override func viewDidLoad() {         super.viewDidLoad()         // Do any additional setup after loading the view.         //self.navigationItem.titleView = UIImageView(image: UIImage(named: "Logo"))                  collectionView.register(MiddleCollectionViewCell.nib(), forCellWithReuseIdentifier: MiddleCollectionViewCell.identifier)                  collectionView.delegate=self         collectionView.dataSource=self                  topCollectionView.register(CircleCollectionViewCell.self, forCellWithReuseIdentifier: CircleCollectionViewCell.identifier)         topCollectionView.delegate=self         topCollectionView.dataSource=self //       Menu //       start                  let destructiveAction = UIAction(title: "Delete",image: UIImage(systemName: "nosign") , attributes: .destructive) { (_) in             print("Delete")         }                  let menu = UIMenu(title: "", children: [             UIAction (title: "Add New", image: UIImage(systemName: "plus.circle"), handler: { _ in  }),             UIAction (title: "Manage", image: UIImage(systemName: "hammer"), handler: { _ in  }),             destructiveAction         ])         self.Menubutton.menu = menu //    Menu end     } // Menu animation     @IBAction func MenuTap(_ sender: Any) {         if menuOut == false {             Leading.constant = -150             Trailing.constant = 150             menuOut = true         }         else{             Leading.constant = 0             Trailing.constant = 0             menuOut = false         }         UIView.animate(withDuration: 0.2, delay: 0.0 , options: .curveEaseIn,animations: {             self.view.layoutIfNeeded()         }){(animationComplete) in             print("Animation Completed")         }     } // Menu animation ends } // used for upper collection view extension ViewController: UICollectionViewDelegate {          func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {         collectionView.deselectItem(at: indexPath, animated: true )         print("btn tapped")              }           } extension ViewController: UICollectionViewDataSource {     func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {         return 12     }     func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {         let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MiddleCollectionViewCell.identifier, for: indexPath) as! MiddleCollectionViewCell         let topcell = collectionView.dequeueReusableCell(withReuseIdentifier: CircleCollectionViewCell.identifier, for: indexPath) as! CircleCollectionViewCell         topcell.configure(with: "")                  cell.configure(with: UIImage(named: "four")!)         return cell; topcell     } } //extension ViewController: UICollectionViewDelegateFlowLayout { // //}
Posted
by
Post marked as solved
3 Replies
596 Views
What follows is about tvOS 15 but may also apply to iOS 15. Our app may display several types of UICollectionView cells and it depends on the content sent by our backend. Prior to tvOS 15, we used to lazily instantiate UICollectionView cell registrations when needed for the first time while building a UICollectionViewDiffableDataSource. Eventually, each cell registration is only instantiated once (or never). Now tvOS 15 throws an exception when doing so, pretending to prevent the app from creating a new cell registration each time. And it does not care whether it's actually a new cell registration each time or one that is lazily instantiated once. Here is the exception: *** Assertion failure in -[UICollectionView dequeueConfiguredReusableCellWithRegistration:forIndexPath:item:], UICollectionView.m:7413 *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Attempted to dequeue a cell using a registration that was created inside -collectionView:cellForItemAtIndexPath: or inside a UICollectionViewDiffableDataSource cell provider. Creating a new registration each time a cell is requested will prevent reuse and cause created cells to remain inaccessible in memory for the lifetime of the collection view. Registrations should be created up front and reused. Registration: <UICollectionViewCellRegistration: 0x6000000f8c60>' I understand the concern but lazy instantiation is a thing and it should not be for forbidden for the sake of best practices that are not related to it. In my humble opinion, this assert should be removed and apps should be allowed to create cell registrations on the fly.
Posted
by
Post not yet marked as solved
3 Replies
537 Views
When I use UICollectionView on iPhone13 and the latest devices, [collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:section] atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES] will cause caton. animated:YES has no animation effect, but this method works well on other devices, They are IOS 15.0.2 systems - (void)topClick:(UIButton *)sender{         if (sender.selected) {         return;     }          NSArray *array = [NSArray array];     if (sender == self.topLuck) {         array = self.luckList;     } else if (sender == self.topLuxurious){         array = self.luxuriousList;     } else if (sender == self.topVip){         array = self.vipList;     } else if (sender == self.topPk){         array = self.pkList;     } else if (sender == self.packetBtn){         array = self.packetList;     }     NSInteger section = [self.datas indexOfObject:array]; //    [self didScrollToSection:section];          if (section < self.datas.count) {         [self.giftCollectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:section] atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];     } } - (UICollectionView *)giftCollectionView{     if (!_giftCollectionView) {         LiveGiftPickerLayout *layout = [LiveGiftPickerLayout new];         layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;         layout.sectionInset = UIEdgeInsetsMake(0, 10, 0, 10);         CGFloat width = floor((kScreen_Width - 20)/4);         layout.itemSize = CGSizeMake(width, width);         layout.minimumLineSpacing = 0;         layout.minimumInteritemSpacing = 0;         _giftCollectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];         _giftCollectionView.dataSource = self;         _giftCollectionView.delegate = self;         _giftCollectionView.backgroundColor = [UIColor clearColor];         _giftCollectionView.showsHorizontalScrollIndicator = NO;         _giftCollectionView.pagingEnabled = YES;         [_giftCollectionView registerClass:[GiftPickerCollectionViewCell class] forCellWithReuseIdentifier:[GiftPickerCollectionViewCell currentIdentifier]];     }     return _giftCollectionView; }
Posted
by
Post not yet marked as solved
0 Replies
302 Views
Assertion failure in -[UICollectionView _setNeedsVisibleCellsUpdate:withLayoutAttributes:], UICollectionView.m:1511  I have the following structure here -| UITableView -| UITableViewCell   -| UICollectionView           -| UICollectionViewCell           -| UICollectionViewCell ... when I override func systemLayoutSizeFitting in TableViewCell, The crashed occurred.
Posted
by
Post not yet marked as solved
0 Replies
291 Views
Xcode 13.1, iOS 25 SDK I have a UICollectionView (with a custom UICollectionViewLayout) which I am updating with a DiffableDataSource. This CollectionView displays a particular data object (with a given number of sections and items). The user can choose to display a different object (with a different number of sections and items), reusing this CollectionView. When this occurs, I do the following: (pass the new data object to the custom layout) let layout = collectionView.collectionViewLayout layout.invalidateLayout() collectionView.contentSize = layout.collectionViewContentSize (calculate a new snapshot and apply it) When the new data object has FEWER sections and/or items as the first, I get a series of warning messages (one for each of the cells that are no longer in the collection view) as follows: 2021-12-07 14:29:02.239301-0600 WineCorner[82916:1921357] [CollectionView] Layout attributes <UICollectionViewLayoutAttributes: 0x7f77b3047e00> index path: (<NSIndexPath: 0xa0f0b1eb018e754a> {length = 2, path = 0 - 4}); frame = (578.118 6; 160 160); transform = [0.70710678118654757, -0.70710678118654746, 0.70710678118654746, 0.70710678118654757, 0, 0]; zIndex = 1; were received from the layout <WineCorner.WineRackLayout: 0x7f77b1f1a0c0> but are not valid for the data source counts. Attributes will be ignored. Obviously, I am not handling the situation correctly. What should I do so that these warning messages are not issued?
Posted
by
Post not yet marked as solved
0 Replies
294 Views
I spoke to an Apple Engineer at WWDC 2021 about how to implement an EPG (Electronic Program Guide, see image below) in a collectionView. I was recommended to use a UICollectionViewCompositionalLayout where each program corresponds to an Item and each channel corresponds to a Section (the section being provided by a SectionProvider). Furthermore, I was told to override preferredLayoutAttributesFitting to set the size of each cell. However, I have two difficult issues that I have not been able to come around: I cannot figure out how to ensure the CollectionView displays all cells correctly at the right spot, such that the time is aligned vertically for all channels. I am using NSCollectionLayoutItem(layoutSize: NSCollectionLayoutSize(widthDimension: .estimated(estimatedWidth), heightDimension: .fractionalHeight(1))), which provides an estimated width, but this estimated width is apparently used for all cells not in the view, causing the cells to be rendered at the wrong spot as soon as I scroll to the right. The performance when scrolling is terrible, probably due to my overriding of preferredLayoutAttributesFitting on the cell, which causes the cell to change its width. I was instructed not to use "self sizing cells" as this would cause performance problems. But I cannot figure a way out of this. How do I tell the CollectionView how to render the cells at the proper spot and at the same time avoid "self sizing cells"? Any help would be highly appreciated!
Posted
by
Post not yet marked as solved
0 Replies
187 Views
I will briefly explain my view. My goal is to make a list where you can enter all personal information like a kind of emergency passport. There should also be text fields where you can tap on the plus button and then there is a new text field. On the top right I want to make an edit button that you can delete text fields again. I will connect this view with another SwiftUI view file. Does anyone have an idea how to solve this problem with the list? import SwiftUI struct ListeDaten: Identifiable { let id = UUID() let Name: String let Adresse: String var Telefonnummern: [String] = [] var Something: String = "" } struct ListeDaten: View {     var body: some View {                  TextField(Name: "Name/Vorname")         TextField(Adresse: "Adresse")         ForEach(id: \.self) {Telefonnummern in TextField("Telefonnummer")}         Button(action: {             Telefonnummern.append("")         })         {             Image(systemName: "plus.circle.fill")                 .foregroundColor(Color(.systemGreen))         }     } } struct ListeDaten_Previews: PreviewProvider {     static var previews: some View {         ListeDaten()     } }
Posted
by
Post not yet marked as solved
0 Replies
158 Views
I have a collection view with custom layout. and it has different kinds of dynamic contents. suddenly when i tap on an item, go to next viewController and move back to collection view, there is a large gap between navigation controller and collection view content. could someone please share how to resolve this issue or how to find out the cause? please note that, collection view resides in a pageview controller
Posted
by
Post not yet marked as solved
0 Replies
205 Views
Hello, how should I perform two next two operations with UICompositionalLayout? Scroll To Specific offset Change layout of the item, I'm scrolling to. Whenever I try to call collectionView.collectionViewLayout.invalidateLayout(), I get reset: Content Offset of UICollectionView The layout change of the item is not updated Here is the example of the layout I try to use.    let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),                                           heightDimension: .estimated(100))     let item = NSCollectionLayoutItem(layoutSize: itemSize)     item.edgeSpacing = NSCollectionLayoutEdgeSpacing(leading: nil, top: .fixed(8), trailing: nil, bottom: .fixed(8))     let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),                                            heightDimension: .estimated(100))     let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])     let section = NSCollectionLayoutSection(group: group)     return section And here is the example of cell class ReelTextCellView: UICollectionViewCell {   private var textView: UITextView!   private var infoHolderView: UIView!   private var copyButton: UIButton!   private var scrollView: UIScrollView!   private var heightConstraint: NSLayoutConstraint!   var isExpanded = false   override init(frame: CGRect) {     super.init(frame: .zero)     buildView()   }   required init?(coder: NSCoder) {     fatalError("init(coder:) has not been implemented")   }   override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {     let attributes = super.preferredLayoutAttributesFitting(layoutAttributes)     attributes.frame.size = .init(width: contentView.frame.width, height: isExpanded ? 300 : 100)     return attributes   } // Called with the cellWillAppear and manually for indexPathsForVisibleItems   func update(with appeareance: Appearance) {     isExpanded = appeareance.isExpanded     ///    heightConstraint.constant = appeareance.isExpanded ? 300 : 100   } private extension ReelTextCellView {   func buildView() {     infoHolderView = UIView()     contentView.addSubview(infoHolderView) {       contentView.bottomAnchor.constraint(equalTo: infoHolderView.bottomAnchor)       contentView.leadingAnchor.constraint(equalTo: infoHolderView.leadingAnchor)       contentView.trailingAnchor.constraint(equalTo: infoHolderView.trailingAnchor)       infoHolderView.heightAnchor.constraint(equalToConstant: 30)     }     let separatorView = UIView()     separatorView.backgroundColor = Assets.Colors.separator.color     infoHolderView.addSubview(separatorView) {       separatorView.topAnchor.constraint(equalTo: infoHolderView.topAnchor)       separatorView.leadingAnchor.constraint(equalTo: infoHolderView.leadingAnchor)       separatorView.trailingAnchor.constraint(equalTo: infoHolderView.trailingAnchor)       separatorView.heightAnchor.constraint(equalToConstant: 0.5)     }     textView = UITextView()     contentView.addSubview(textView) {       textView.topAnchor.constraint(equalTo: contentView.topAnchor)       textView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor)       textView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor)       textView.bottomAnchor.constraint(equalTo: infoHolderView.topAnchor)     }   } }
Posted
by
Post not yet marked as solved
0 Replies
255 Views
I'm trying to display a list of songs using UICollectionView's List API. I would also like them grouped by the first letter of the song. At first, I thought I'd have something like this: let sections = [ LibrarySection(name: A, songs: [ Song(name: "A Song"), Song(name: "Another Song"), ]), LibrarySection(name: B, songs: [ Song(name: "B Song"), Song(name: "B another Song"), ]), ] var snapshot = NSDiffableDataSourceSnapshot<LibrarySection, LibraryRow>() snapshot.appendSections(sections) sections.forEach { section in snapshot.appendItems(section.songs, toSection: section) } dataSource.apply(snapshot, animatingDifferences: true) But I facepalmed when I realized the smart thing to do is this: let items = [ Song(name: "A Song"), Song(name: "Another Song"), Song(name: "B Song"), Song(name: "B another Song") ] The question I have is, what would the snapshot logic look like such that I am minimizing the amount of times I have to instantiate items and sections? var snapshot = NSDiffableDataSourceSnapshot<LibrarySection, LibraryRow>() ??? dataSource.apply(snapshot, animatingDifferences: true) Thanks for any feedback you may have!!!
Posted
by
Post not yet marked as solved
0 Replies
127 Views
In iOS15, we have an efficient way to update items cell, by using reconfigureItems. Here's the code snippet to perform such efficient update. Update items cell efficiently using reconfigureItems private func reconfigureRecordingRow(_ recording: Recording) { var snapshot = dataSource.snapshot() snapshot.reconfigureItems([recording]) dataSource.apply(snapshot) } private func makeDataSource() -> DataSource { let dataSource = DataSource( collectionView: collectionView, cellProvider: { [weak self] (collectionView, indexPath, anyHashable) -> UICollectionViewCell? in guard let self = self else { return nil } guard let recordingCell = collectionView.dequeueReusableCell( withReuseIdentifier: "recording", for: indexPath) as? RecordingCell else { return nil } When reconfigureRecordingRow is called, cellProvider's function will be executed. collectionView.dequeueReusableCell is able to re-use existing UICollectionViewCell, without constructing new UICollectionViewCell However, I was wondering, how can I achieve a similar efficiency, if I have a section, with header supplementary view, and without any item? For instance Not able to update supplementary view efficiently private func reloadAttachmentRow() { var snapshot = dataSource.snapshot() let sectionIdentifiers = snapshot.sectionIdentifiers if sectionIdentifiers.contains(.attachment) { snapshot.reloadSections([.attachment]) } else { snapshot.insertSections([.attachment], beforeSection: .title) } dataSource.apply(snapshot) } dataSource.supplementaryViewProvider = { [weak self] collectionView, kind, indexPath in guard let self = self else { return nil } if kind == UICollectionView.elementKindSectionHeader { let section = indexPath.section let sectionIdentifier = self.sectionIdentifier(section) switch sectionIdentifier { case .attachment: guard let collageViewHeader = collectionView.dequeueReusableSupplementaryView( ofKind: kind, withReuseIdentifier: "attachment", for: indexPath) as? CollageViewHeader else { return nil } When reloadSections is called, dataSource.supplementaryViewProvider will be executed. As per my testing, collectionView.dequeueReusableSupplementaryView will return a new instance of UICollectionReusableView each time. As a result, I can visually observe the entire section is "flickering", when reloadAttachmentRow is called. I was wondering, how can we update supplementary view efficiently?
Posted
by