Post

Replies

Boosts

Views

Activity

Reply to SwiftUI previews not loading
Thank you for responding despite my vagueness. Apple MacBook Air M1, 8 GB, MacOS Sonoma 14.2.1, Xcode 15.2--I forgot to paste it from the TSI message. After a few hours, I got the "Cannot preview in this file" error. I’ve noticed that my derived data folder has a different location according to Xcode and Finder. According to Xcode, the root path is /Users/filippo. But, according to Finder, it's /Users/Filippo. I changed my username a year ago. If you think this issue could be related to my problem, please tell me how to solve it.
Feb ’24
Reply to Swift 5.10: Cannot access property '*' with a non-sendable type '*' from non-isolated deinit; this is an error in Swift 6
https://stackoverflow.com/questions/78284030/swift-5-10-cannot-access-property-with-a-non-sendable-type-from-non-iso/78285030#78285030 In a nutshell: call signInAnonymouslyIfNecessary() in viewDidDisappear(): avoid in general dealing with class deinitializers. But if you insist on calling signInAnonymouslyIfNecessary() in the deinitializer, you can: Mark AuthController as @MainActor Mark signInAnonymouslyIfNecessary as nonisolated Wrap signInAnonymouslyIfNecessary in a Task: @MainActor final class AuthController { nonisolated func signInAnonymouslyIfNecessary() { Task { @MainActor in /// ... } } }
Apr ’24
Reply to Update collection view supplementary view content
https://stackoverflow.com/questions/78311570/how-do-i-update-a-collection-view-supplementary-view-without-giving-up-on-animat At this time the answer is: To directly update a supplementaryView you need to know its kind and section. With your example, you could add a following didSet code to a footerText: var footerText = "Initial footer text" { didSet { guard let footer = collectionView.supplementaryView(forElementKind: UICollectionView.elementKindSectionFooter, at: IndexPath(row: 0, section: 0)) as? UICollectionViewListCell else { return } var contentConfiguration = UIListContentConfiguration.cell() contentConfiguration.text = self.footerText footer.contentConfiguration = contentConfiguration } } This way each time you change footerText, your footer will be updated. You could also move contentConfiguration update to a separate fuction, I've just copied it from your configureSupplementaryViewProvider
Apr ’24
Reply to Lags in UICollectionView accessories when animatedly applying a snapshot unless .disclosureIndicator() is also an accessory
Make a custom cell: class SwitchCell: UICollectionViewListCell { let _switch = UISwitch() override init(frame: CGRect) { super.init(frame: frame) accessories = [ .customView(configuration: .init( customView: _switch, placement: .trailing()) ) ] } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } And edit cellRegistration accordingly: let cellRegistration = UICollectionView.CellRegistration<SwitchCell, String> { [weak self] cell, indexPath, itemIdentifier in guard let self else { return } cell._switch.isOn = bool cell._switch.addTarget(self, action: #selector(toggleBool), for: .valueChanged) }
Apr ’24
Reply to UISearchBar resigns first responder when typing in UICollectionView
If you use cell registrations instead of supplementary registrations, the keyboard doesn't dismiss for any reason and you can also get rid of the dispatch blocks: import UIKit class ViewController: UIViewController { let words = ["foo", "bar"] var filteredWords = ["foo", "bar"] { didSet { dataSource.apply(self.snapshot) } } var collectionView: UICollectionView! var dataSource: UICollectionViewDiffableDataSource<String, String>! var snapshot: NSDiffableDataSourceSnapshot<String, String> { var snapshot = NSDiffableDataSourceSnapshot<String, String>() snapshot.appendSections(["main"]) snapshot.appendItems(["search bar id"]) snapshot.appendItems(filteredWords) return snapshot } override func viewDidLoad() { super.viewDidLoad() navigationItem.rightBarButtonItem = .init(title: "Apply", style: .plain, target: self, action: #selector(apply)) configureHierarchy() configureDataSource() } @objc func apply() { dataSource.apply(self.snapshot) } func configureHierarchy() { collectionView = .init(frame: .zero, collectionViewLayout: createLayout()) view.addSubview(collectionView) collectionView.frame = view.bounds collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight] } func createLayout() -> UICollectionViewLayout { UICollectionViewCompositionalLayout { section, layoutEnvironment in var config = UICollectionLayoutListConfiguration(appearance: .insetGrouped) config.headerMode = .firstItemInSection return NSCollectionLayoutSection.list(using: config, layoutEnvironment: layoutEnvironment) } } func configureDataSource() { let cellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, String> { _, _, _ in } let searchBarCellRegistration = UICollectionView.CellRegistration<SearchBarCell, String>{ cell, indexPath, itemIdentifier in cell.searchBar.delegate = self } dataSource = .init(collectionView: collectionView) { collectionView, indexPath, itemIdentifier in if indexPath.row == 0 { collectionView.dequeueConfiguredReusableCell(using: searchBarCellRegistration, for: indexPath, item: itemIdentifier) } else { collectionView.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item: itemIdentifier) } } dataSource.apply(self.snapshot, animatingDifferences: false) } } extension ViewController: UISearchBarDelegate { func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { filteredWords = words.filter { $0.hasPrefix(searchText) } } func searchBarTextDidEndEditing(_ searchBar: UISearchBar) { filteredWords = words } } class SearchBarCell: UICollectionViewListCell { let searchBar = UISearchBar() override init(frame: CGRect) { super.init(frame: frame) contentView.addSubview(searchBar) searchBar.pinToSuperview() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } extension UIView { func pin( to object: CanBePinnedTo, top: CGFloat = 0, bottom: CGFloat = 0, leading: CGFloat = 0, trailing: CGFloat = 0 ) { self.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ self.topAnchor.constraint(equalTo: object.topAnchor, constant: top), self.bottomAnchor.constraint(equalTo: object.bottomAnchor, constant: bottom), self.leadingAnchor.constraint(equalTo: object.leadingAnchor, constant: leading), self.trailingAnchor.constraint(equalTo: object.trailingAnchor, constant: trailing), ]) } func pinToSuperview( top: CGFloat = 0, bottom: CGFloat = 0, leading: CGFloat = 0, trailing: CGFloat = 0, file: StaticString = #file, line: UInt = #line ) { guard let superview = self.superview else { print(">> \(#function) failed in file: \(String.localFilePath(from: file)), at line: \(line): could not find \(Self.self).superView.") return } self.pin(to: superview, top: top, bottom: bottom, leading: leading, trailing: trailing) } func pinToSuperview(constant c: CGFloat = 0, file: StaticString = #file, line: UInt = #line) { self.pinToSuperview(top: c, bottom: -c, leading: c, trailing: -c, file: file, line: line) } } @MainActor protocol CanBePinnedTo { var topAnchor: NSLayoutYAxisAnchor { get } var bottomAnchor: NSLayoutYAxisAnchor { get } var leadingAnchor: NSLayoutXAxisAnchor { get } var trailingAnchor: NSLayoutXAxisAnchor { get } } extension UIView: CanBePinnedTo { } extension UILayoutGuide: CanBePinnedTo { } extension String { static func localFilePath(from fullFilePath: StaticString = #file) -> Self { URL(fileURLWithPath: "\(fullFilePath)").lastPathComponent } } Basically don't use supplementary views unless absolutely necessary: it also seems that there's no declarative way to update them without losing animations (Apple's guided project "Modern Collection Views" doesn't seem to provide a solution, nor do these posts: https://stackoverflow.com/questions/78311570/how-do-i-update-a-collection-view-supplementary-view-without-giving-up-on-animat, https://forums.developer.apple.com/forums/thread/749847).
Apr ’24
Reply to UICollectionViewListCell not resizing
Use UIListContentConfiguration: import UIKit class ViewController: UIViewController { var collectionView: UICollectionView! var dataSource: UICollectionViewDiffableDataSource<String, String>! override func viewDidLoad() { super.viewDidLoad() configureHierarchy() configureDataSource() } func configureHierarchy() { collectionView = .init(frame: .zero, collectionViewLayout: createLayout()) view.addSubview(collectionView) collectionView.frame = view.bounds } func createLayout() -> UICollectionViewLayout { UICollectionViewCompositionalLayout { section, layoutEnvironment in let config = UICollectionLayoutListConfiguration(appearance: .insetGrouped) return NSCollectionLayoutSection.list(using: config, layoutEnvironment: layoutEnvironment) } } func configureDataSource() { let cellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, String> { cell, indexPath, itemIdentifier in var contentConfig = CustomListContentConfiguration() contentConfig.placeholder = "Placeholder" cell.contentConfiguration = contentConfig } dataSource = .init(collectionView: collectionView) { collectionView, indexPath, itemIdentifier in collectionView.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item: itemIdentifier) } var snapshot = NSDiffableDataSourceSnapshot<String, String>() snapshot.appendSections(["main"]) snapshot.appendItems(["demo"]) dataSource.apply(snapshot, animatingDifferences: false) } } class CustomListContentConfiguration: UIContentConfiguration { var placeholder: String? func makeContentView() -> UIView & UIContentView { return CustomListContentView(configuration: self) } func updated(for state: UIConfigurationState) -> Self { // Not handling state changes in this example, so just return self return self } } class CustomListContentView: UIView, UIContentView { var configuration: UIContentConfiguration init(configuration: UIContentConfiguration) { self.configuration = configuration super.init(frame: .zero) configureSubviews() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } func configureSubviews() { guard let config = configuration as? CustomListContentConfiguration else { return } let leadingView = UIView() leadingView.backgroundColor = .systemRed let textField = UITextField() textField.placeholder = config.placeholder textField.font = .systemFont(ofSize: 100) addSubview(leadingView) addSubview(textField) leadingView.translatesAutoresizingMaskIntoConstraints = false textField.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ leadingView.centerYAnchor.constraint(equalTo: layoutMarginsGuide.centerYAnchor), leadingView.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor), leadingView.widthAnchor.constraint(equalTo: layoutMarginsGuide.heightAnchor), leadingView.heightAnchor.constraint(equalTo: layoutMarginsGuide.heightAnchor), textField.topAnchor.constraint(equalTo: topAnchor), textField.bottomAnchor.constraint(equalTo: bottomAnchor), textField.leadingAnchor.constraint(equalTo: leadingView.trailingAnchor, constant: 16), textField.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor), textField.heightAnchor.constraint(greaterThanOrEqualToConstant: 44) ]) } }
Apr ’24