Discuss the different user interface frameworks available for your app.

All subtopics
Posts under UI Frameworks topic

Post

Replies

Boosts

Views

Activity

Leaving/Stopping CKShare using CoreData
Hello, I am working with sharing over Core Data. I would like to implement my own UI to share records and manage participants. In the CKShare's documentation we can read: However, only the owner can delete a shared hierarchy’s root record. If a participant attempts to delete the share, CloudKit removes the participant. The share remains active for all other participants (https://developer.apple.com/documentation/cloudkit/ckshare). However, when I delete a shared NSManagedObject (as a participant), using self.moc.delete(sharedProject), the object is deleted even for its owner. On the other hand, accoring to the "Sharing Core Data objects between iCloud users" documentation (https://developer.apple.com/documentation/coredata/sharing_core_data_objects_between_icloud_users), to delete a share, we should use the purgeObjectsAndRecordsInZone(with:in:completion:) method. And that works, however, NSManagedObjectContext doesn't merge changes. Moreover, in the same documentation we can read: Manages the participants of the share from the owner side using addParticipant(:) and removeParticipant(:), or stops the sharing by calling purgeObjectsAndRecordsInZone(with:in:completion:). Unfortunately, calling that method from the owner side leads to deletion of the shared record. Do you have any ideas how to receive a similar effect like in the UICloudSharingController?
0
2
1.6k
Jan ’23
Multiple errors 5 minutes into Build a Workout App for Apple Watch
Below is the exact code (though I changed the sports) in the video at https://developer.apple.com/videos/play/wwdc2021/10009/ Why all of these errors? import SwiftUI import HealthKit struct StartView: View {   var workoutTypes: [HKWorkoutActivityType] = [.paddleSports, .sailing, .swimming]       var body: some View {     List(workoutTypes) { workoutType in       NavigationLink(         workoutType.name,         destination: Text(workoutType.nam e)**       ).padding(         EdgeInsets(top: 15, leading: 5, bottom: 15, trailing: 5)       )   }     .listStyle(.carousel)     .navigationBarTitle("Activity") } struct ContentView_Previews: PreviewProvider {   static var previews: some View {     StartView()   } } extension HKWorkoutActivityType: Identifiable {   public var id: UInt {     rawValue   }       var name: String {     switch self {     case .paddleSports:       return "Paddle"     case .sailing:       return "Sail"     case .swimming:       return "Swim"     default:       return ""     }   } } }
0
0
1.1k
Nov ’22
Search Suggestions Limitations (new feature in iOS 16)
I've tried adding 'search suggestions' to my app using iOS 16's new 'searchSuggestions' property but I'm finding it quite limited/frustrating to work with. Maybe I'm missing something, but it seems even though you can set searchSuggestions on a UISearchBar that is not part of a UISearchController, and these suggestions will show just fine, there's no way to get any information about which suggestion has been selected. To do that you need to use a search controller, which I'd rather not use. However to proceed further I added a search controller to my app. There now seems to be further limitations: With a search controller added to a navigation item (seemingly the only way to get the search suggestions to show with a search controller), iOS will display the suggestions in different ways depending on the navigation item's 'searchBarPlacement' value - inline will display 'floating' suggestions, while stacked will show a 'list'. The problem is I much prefer the floating style (looks much better/modern), but this affects the search bar style - the search bar is replaced with a search icon that expands to a search bar when pressed. If I want a normal search bar without a search icon, then I'm forced to use the list style, which also appears to completely hide everything underneath it. Any way around these issues? To recap there's three things: Selection status can't be determined when showing search suggestions without a search controller 'Floating' search suggestions can't be shown with a stacked search bar placement 'List' search suggestions hide everything underneath
0
0
1.7k
Sep ’22
What if they tap the submit button instead of hitting Done or Enter?
For https://developer.apple.com/videos/play/wwdc2021/10023/?time=411 I have a similar case in my app, but SwiftUI doesn't seem to be able to sanely handle the case where the user hits the submit button after typing instead of hitting Return or the "Done" button like they do in the video. In my case I have a focus listener on the Textfield and want to perform a custom action when the Textfield loses focus. If they manually hit the submit button in the UI instead of using the keyboard, the textfield doesn't lose focus or at least the handler isn't called. Is there some way to make sure that the lose focus handler is called on the text field when they tap the submit without a bunch of kludgey code creating dependencies between the components?
0
0
1.1k
Sep ’22
SKOverlay frame size
Does anyone know if the frame size of SKOverlay - https://developer.apple.com/documentation/storekit/skoverlay is made available as a read-only variable? I would like to display the SKOverlay within my app, but adapt the UI of my app around it to size and fit perfectly. At the moment from the Apple documentation, properties of the overlay appear to be limited.
3
0
2.2k
Sep ’22
Cell should never be in update animation (UICollectionView crash)
I've got Cell should never be in update animation crash in UICollectionView reported by crashlytics This crash happens only for users on iOS 14. It wasn't noticeable when application used SDK iOS 13 (it affected less then 0.01 % of all users). But after supporting SDK iOS 14 crash frequency noticeably increased to almost 0.25 % of all users Internally UICollectionView, as far as I understand (from some UIKit disassembly journey), loops through visible cells and checks that cell animation counter is equal zero (else it crashes with the following stack trace). It's not for sure but presumably crash happens while performBatchUpdates(_:completion:) I wasn't able to reproduce this bug and I can't spend more time on digging into UIKitCore disassembled code trying to understand when and why this animation counter changes Fatal Exception: NSInternalInconsistencyException 0	CoreFoundation								 0x1915e9114 __exceptionPreprocess 1	libobjc.A.dylib								0x1a4e0fcb4 objc_exception_throw 2	CoreFoundation								 0x1914f8308 -[CFPrefsSearchListSource addManagedSourceForIdentifier:user:] 3	Foundation										 0x1927dc2c8 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] 4	UIKitCore											0x1935b8548 __43-[UICollectionView _updateVisibleCellsNow:]_block_invoke_4.1070 5	UIKitCore											0x1935b8000 __43-[UICollectionView _updateVisibleCellsNow:]_block_invoke.1053 6	libdispatch.dylib							0x191221298 _dispatch_call_block_and_release 7	libdispatch.dylib							0x191222280 _dispatch_client_callout 8	libdispatch.dylib							0x1911d123c _dispatch_main_queue_callback_4CF$VARIANT$mp 9	CoreFoundation								 0x191568c30 CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE 10 CoreFoundation								 0x1915630e8 __CFRunLoopRun 11 CoreFoundation								 0x191562200 CFRunLoopRunSpecific 12 GraphicsServices							 0x1a765d598 GSEventRunModal 13 UIKitCore											0x193e28004 -[UIApplication _run] 14 UIKitCore											0x193e2d5d8 UIApplicationMain 15 Joom													 0x100dd2d1c main + 49 (main.m:49) 16 libdyld.dylib									0x191241598 start Did anyone faced with the same issue? Were you able to reproduce it?
5
0
4.7k
Jul ’22
How to animate cell size change when using UIHostingConfiguration
I made an UICollectionView that uses a CompositionalLayout, DiffableDataSource, the new UIHostingConfiguration and an ObservableObject. You can resize cells by double tapping them (see gif + example code). The resizing is triggered by updating the ObservableObject. Now I want to set an animation mainly to animate the shrinking as well as the size change of the last cell but I can't seem to find the right way to do so. By default there is no animation when shrinking and on the last cell: While there are many guides on how to create dynamically resizeable cells they all either use changes in Autolayout constraints (which can't be used with UIHostingConfiguration - correct me if I'm wrong) or they add an optional part to the view inside UIHostingConfiguration with "if some condition {MyAdditionalView(); .transition(someTransition)}" instead of changing its frame. I also tried these different ways to manipulate the animation but with no success: setting an animation modifier .animation(): this makes a fading animation appear and the view inside the hosting configuration starts to jump (It seems to be anchored in the centre of the cell). setting a transition modifier .transition(): this has no effect on the animation. subclassing UICompositionalLayout and overriding initialLayoutAttributesForAppearingItem(at itemIndexPath: IndexPath) as well as finalLayoutAttributesForDisappearingItem(at itemIndexPath: IndexPath): this has no effect on the animation. subclassing UICollectionViewCell and overriding apply(_ layoutAttributes: UICollectionViewLayoutAttributes): this only effects the orange cell-background on initial appearance. using snapshot.reloadItems([ItemIdentifier]) or snapshot.reconfigureItems([ItemIdentifier]): they have no effect on the animation and lead to inconsistent behaviour when combined with ObservableObject. setting collectionView.selfSizingInvalidation = .enabledIncludingConstraints: no effect. Setting it to .disabled stops the orange cells from resizing altogether. Is there a way to customize the size change animation of a cell using UIHostingConfiguration and ObservableObject? Code without animation: struct CellContentModel {     var height: CGFloat? = 100 } class CellContentController: ObservableObject, Identifiable {     let id = UUID()     @Published var cellContentModel: CellContentModel     init(cellContentModel: CellContentModel) {         self.cellContentModel = cellContentModel     } } class DataStore {     var data: [CellContentController]     var dataById: [CellContentController.ID: CellContentController]     init(data: [CellContentController]) {         self.data = data         self.dataById = Dictionary(uniqueKeysWithValues: data.map { ($0.id, $0) } )     }     static let testData = [         CellContentController(cellContentModel: CellContentModel()),         CellContentController(cellContentModel: CellContentModel(height: 80)),         CellContentController(cellContentModel: CellContentModel())     ] } class CollectionViewController: UIViewController {     enum Section {         case first     }     var dataStore = DataStore(data: DataStore.testData)     private var layout: UICollectionViewCompositionalLayout!     private var collectionView: UICollectionView!     private var dataSource: UICollectionViewDiffableDataSource<Section, CellContentController.ID>!     override func loadView() {         createLayout()         createCollectionView()         createDataSource()         view = collectionView     } } // - MARK: Layout extension CollectionViewController {     func createLayout() {         let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .estimated(50))         let Item = NSCollectionLayoutItem(layoutSize: itemSize)         let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.8), heightDimension: .estimated(300))         let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [Item])         let section = NSCollectionLayoutSection(group: group)         layout = .init(section: section)     } } // - MARK: CollectionView extension CollectionViewController {     func createCollectionView() {         collectionView = .init(frame: .zero, collectionViewLayout: layout)         let doubleTapGestureRecognizer = DoubleTapGestureRecognizer()         doubleTapGestureRecognizer.doubleTapAction = { [unowned self] touch, _ in             let touchLocation = touch.location(in: collectionView)             guard let touchedIndexPath = collectionView.indexPathForItem(at: touchLocation) else { return }             let touchedItemIdentifier = dataSource.itemIdentifier(for: touchedIndexPath)!             dataStore.dataById[touchedItemIdentifier]!.cellContentModel.height = dataStore.dataById[touchedItemIdentifier]!.cellContentModel.height == 100 ? nil : 100 // <- this triggers the resizing }         collectionView.addGestureRecognizer(doubleTapGestureRecognizer)     } } // - MARK: DataSource extension CollectionViewController {     func createDataSource() {         let cellRegistration = UICollectionView.CellRegistration<C, CellContentController.ID>() { cell, indexPath, itemIdentifier in             let cellContentController = self.dataStore.dataById[itemIdentifier]!             cell.contentConfiguration = UIHostingConfiguration {                 TextView(cellContentController: cellContentController)             }             .background(.orange)         }         dataSource = .init(collectionView: collectionView) { collectionView, indexPath, itemIdentifier in             return collectionView.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item: itemIdentifier)         }         var initialSnapshot = NSDiffableDataSourceSnapshot<Section, CellContentController.ID>()         initialSnapshot.appendSections([Section.first])         initialSnapshot.appendItems(dataStore.data.map{ $0.id }, toSection: Section.first)         dataSource.applySnapshotUsingReloadData(initialSnapshot)     } } class DoubleTapGestureRecognizer: UITapGestureRecognizer {     var doubleTapAction: ((UITouch, UIEvent) -> Void)?     override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {         if touches.first!.tapCount == 2 {             doubleTapAction?(touches.first!, event)         }     } } struct TextView: View {     @StateObject var cellContentController: CellContentController     var body: some View { Text(cellContentController.cellContentModel.height?.description ?? "nil")             .frame(height: cellContentController.cellContentModel.height)             .background(.green)     } }
1
1
3k
Jul ’22
If you were writing Photos from scratch today, which framework(s) would you use?
A type of question I see often is "which UI framework is best? SwitUI, UIKit, AppKit, etc?" And the answer is, of course, usually "it depends" or "a mix, depending on what you need". I wanted to re-frame that question with specific parameters. Let's say you were writing Apple Photos for the Mac from the ground up, starting today. For the sake of discussion, it's going to be functionally identical to the Photos app that exists on the Mac today. Which means it: looks and feels like a true, native macOS app should be very performant in terms of rendering and scrolling through a library that may contain hundreds of thousands of photos maintains a large database which can be synced over iCloud etc When you go to write that first line of UI code, which UI framework are you reaching for, and why? If more than one, which ones would you use for each piece, and why? Especially interested to hear any viewpoints from Apple folks in this thought exercise!
3
0
1.3k
Jun ’22
Adding Segments to an HKWorkout
I want to add segments into a running HKWorkout. My understanding is that I should use the addWorkoutEvents method in my HKWorkoutBuilder. Here is a sample of my code: func saveSegment(forStep step: GymWorkoutEventStep) { let dateInterval = DateInterval(start: step.startDate ?? Date(), end: Date()) let metadata = [ HKMetadataKeyWasUserEntered: true ] let event = HKWorkoutEvent(type: .segment, dateInterval: dateInterval, metadata: metadata) builder?.addWorkoutEvents([event], completion: { success, error in print(success ? "Success saving segment" : error.debugDescription) }) } I’m trying to implement the same feature that the native Apple Watch “Workout” app has by adding a segment when a user double taps the screen. My code seems to work as I see “Success saving segment” in the console. However, once the workout is finished, the Apple Fitness app doesn’t show these segments. What am I missing? Also, is there a way to add distance and pace to each segment using MetadataKey’s or is this not an option? Thanks for any help you can provide.
0
1
1.3k
Jun ’22
SF Font for UI/UX.
Hi! I am working on the UI/UX project for Apple apps(iPhone/watchOS etc.) using FIGMA. I don't have any Apple device from where I could use 'SF Font' family natively. I have tried the alternative fonts but none of them appealed to me and my client too. I also downloaded their resources but still it changes the file after I edit the text. Thanks in Advance.
1
0
2.1k
May ’22
Conditionals and Identity
When using conditionals in view bodies, can I preserve identity between the true and false sides of the conditional by adding an explicit id? struct DogTreat: Identifiable { var expirationDate: Date var serialID: String var id: String { serialID } } ... struct WrapperView: View { ... var treat: DogTreat var isExpired: Bool { treat.expirationDate < .now } var body: some View { if isExpired { DogTreatView(treat) .id(treat.id) .opacity(0.75) else { DogTreatView(treat) .id(treat.id) } } ... } Does this perform / behave the same as struct WrapperView: View { ... var treat: DogTreat var isExpired: Bool { treat.expirationDate < .now } var body: some View { DogTreatView(treat) .opacity(isExpired ? 0.75 : 1.0) } ... }
2
0
2k
May ’22
Reservation form
I am building an app for my business, my business is pet care and I want my clients to be able to make a reservation from the app and for me to receive the form entries I only want to use storyboard in Xcode 13 how would I do this?
1
0
1.3k
May ’22
Leaving/Stopping CKShare using CoreData
Hello, I am working with sharing over Core Data. I would like to implement my own UI to share records and manage participants. In the CKShare's documentation we can read: However, only the owner can delete a shared hierarchy’s root record. If a participant attempts to delete the share, CloudKit removes the participant. The share remains active for all other participants (https://developer.apple.com/documentation/cloudkit/ckshare). However, when I delete a shared NSManagedObject (as a participant), using self.moc.delete(sharedProject), the object is deleted even for its owner. On the other hand, accoring to the "Sharing Core Data objects between iCloud users" documentation (https://developer.apple.com/documentation/coredata/sharing_core_data_objects_between_icloud_users), to delete a share, we should use the purgeObjectsAndRecordsInZone(with:in:completion:) method. And that works, however, NSManagedObjectContext doesn't merge changes. Moreover, in the same documentation we can read: Manages the participants of the share from the owner side using addParticipant(:) and removeParticipant(:), or stops the sharing by calling purgeObjectsAndRecordsInZone(with:in:completion:). Unfortunately, calling that method from the owner side leads to deletion of the shared record. Do you have any ideas how to receive a similar effect like in the UICloudSharingController?
Replies
0
Boosts
2
Views
1.6k
Activity
Jan ’23
Multiple errors 5 minutes into Build a Workout App for Apple Watch
Below is the exact code (though I changed the sports) in the video at https://developer.apple.com/videos/play/wwdc2021/10009/ Why all of these errors? import SwiftUI import HealthKit struct StartView: View {   var workoutTypes: [HKWorkoutActivityType] = [.paddleSports, .sailing, .swimming]       var body: some View {     List(workoutTypes) { workoutType in       NavigationLink(         workoutType.name,         destination: Text(workoutType.nam e)**       ).padding(         EdgeInsets(top: 15, leading: 5, bottom: 15, trailing: 5)       )   }     .listStyle(.carousel)     .navigationBarTitle("Activity") } struct ContentView_Previews: PreviewProvider {   static var previews: some View {     StartView()   } } extension HKWorkoutActivityType: Identifiable {   public var id: UInt {     rawValue   }       var name: String {     switch self {     case .paddleSports:       return "Paddle"     case .sailing:       return "Sail"     case .swimming:       return "Swim"     default:       return ""     }   } } }
Replies
0
Boosts
0
Views
1.1k
Activity
Nov ’22
"the host declined to create a scene" calling requestSceneSessionActivation
I have a multi-window app. Additional scenes can be properly created from the dock, although I'm not able to create a new scene programmatically via requestSceneSessionActivation. I get the following error: Open window errror: The operation couldn’t be completed. the host declined to create a scene. Any thoughts?
Replies
4
Boosts
0
Views
2.3k
Activity
Oct ’22
Search Suggestions Limitations (new feature in iOS 16)
I've tried adding 'search suggestions' to my app using iOS 16's new 'searchSuggestions' property but I'm finding it quite limited/frustrating to work with. Maybe I'm missing something, but it seems even though you can set searchSuggestions on a UISearchBar that is not part of a UISearchController, and these suggestions will show just fine, there's no way to get any information about which suggestion has been selected. To do that you need to use a search controller, which I'd rather not use. However to proceed further I added a search controller to my app. There now seems to be further limitations: With a search controller added to a navigation item (seemingly the only way to get the search suggestions to show with a search controller), iOS will display the suggestions in different ways depending on the navigation item's 'searchBarPlacement' value - inline will display 'floating' suggestions, while stacked will show a 'list'. The problem is I much prefer the floating style (looks much better/modern), but this affects the search bar style - the search bar is replaced with a search icon that expands to a search bar when pressed. If I want a normal search bar without a search icon, then I'm forced to use the list style, which also appears to completely hide everything underneath it. Any way around these issues? To recap there's three things: Selection status can't be determined when showing search suggestions without a search controller 'Floating' search suggestions can't be shown with a stacked search bar placement 'List' search suggestions hide everything underneath
Replies
0
Boosts
0
Views
1.7k
Activity
Sep ’22
What if they tap the submit button instead of hitting Done or Enter?
For https://developer.apple.com/videos/play/wwdc2021/10023/?time=411 I have a similar case in my app, but SwiftUI doesn't seem to be able to sanely handle the case where the user hits the submit button after typing instead of hitting Return or the "Done" button like they do in the video. In my case I have a focus listener on the Textfield and want to perform a custom action when the Textfield loses focus. If they manually hit the submit button in the UI instead of using the keyboard, the textfield doesn't lose focus or at least the handler isn't called. Is there some way to make sure that the lose focus handler is called on the text field when they tap the submit without a bunch of kludgey code creating dependencies between the components?
Replies
0
Boosts
0
Views
1.1k
Activity
Sep ’22
SKOverlay frame size
Does anyone know if the frame size of SKOverlay - https://developer.apple.com/documentation/storekit/skoverlay is made available as a read-only variable? I would like to display the SKOverlay within my app, but adapt the UI of my app around it to size and fit perfectly. At the moment from the Apple documentation, properties of the overlay appear to be limited.
Replies
3
Boosts
0
Views
2.2k
Activity
Sep ’22
Cell should never be in update animation (UICollectionView crash)
I've got Cell should never be in update animation crash in UICollectionView reported by crashlytics This crash happens only for users on iOS 14. It wasn't noticeable when application used SDK iOS 13 (it affected less then 0.01 % of all users). But after supporting SDK iOS 14 crash frequency noticeably increased to almost 0.25 % of all users Internally UICollectionView, as far as I understand (from some UIKit disassembly journey), loops through visible cells and checks that cell animation counter is equal zero (else it crashes with the following stack trace). It's not for sure but presumably crash happens while performBatchUpdates(_:completion:) I wasn't able to reproduce this bug and I can't spend more time on digging into UIKitCore disassembled code trying to understand when and why this animation counter changes Fatal Exception: NSInternalInconsistencyException 0&#9;CoreFoundation&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9; 0x1915e9114 __exceptionPreprocess 1&#9;libobjc.A.dylib&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;0x1a4e0fcb4 objc_exception_throw 2&#9;CoreFoundation&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9; 0x1914f8308 -[CFPrefsSearchListSource addManagedSourceForIdentifier:user:] 3&#9;Foundation&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9; 0x1927dc2c8 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] 4&#9;UIKitCore&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;0x1935b8548 __43-[UICollectionView _updateVisibleCellsNow:]_block_invoke_4.1070 5&#9;UIKitCore&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;0x1935b8000 __43-[UICollectionView _updateVisibleCellsNow:]_block_invoke.1053 6&#9;libdispatch.dylib&#9;&#9;&#9;&#9;&#9;&#9;&#9;0x191221298 _dispatch_call_block_and_release 7&#9;libdispatch.dylib&#9;&#9;&#9;&#9;&#9;&#9;&#9;0x191222280 _dispatch_client_callout 8&#9;libdispatch.dylib&#9;&#9;&#9;&#9;&#9;&#9;&#9;0x1911d123c _dispatch_main_queue_callback_4CF$VARIANT$mp 9&#9;CoreFoundation&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9; 0x191568c30 CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE 10 CoreFoundation&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9; 0x1915630e8 __CFRunLoopRun 11 CoreFoundation&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9; 0x191562200 CFRunLoopRunSpecific 12 GraphicsServices&#9;&#9;&#9;&#9;&#9;&#9;&#9; 0x1a765d598 GSEventRunModal 13 UIKitCore&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;0x193e28004 -[UIApplication _run] 14 UIKitCore&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;0x193e2d5d8 UIApplicationMain 15 Joom&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9; 0x100dd2d1c main + 49 (main.m:49) 16 libdyld.dylib&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;0x191241598 start Did anyone faced with the same issue? Were you able to reproduce it?
Replies
5
Boosts
0
Views
4.7k
Activity
Jul ’22
How to animate cell size change when using UIHostingConfiguration
I made an UICollectionView that uses a CompositionalLayout, DiffableDataSource, the new UIHostingConfiguration and an ObservableObject. You can resize cells by double tapping them (see gif + example code). The resizing is triggered by updating the ObservableObject. Now I want to set an animation mainly to animate the shrinking as well as the size change of the last cell but I can't seem to find the right way to do so. By default there is no animation when shrinking and on the last cell: While there are many guides on how to create dynamically resizeable cells they all either use changes in Autolayout constraints (which can't be used with UIHostingConfiguration - correct me if I'm wrong) or they add an optional part to the view inside UIHostingConfiguration with "if some condition {MyAdditionalView(); .transition(someTransition)}" instead of changing its frame. I also tried these different ways to manipulate the animation but with no success: setting an animation modifier .animation(): this makes a fading animation appear and the view inside the hosting configuration starts to jump (It seems to be anchored in the centre of the cell). setting a transition modifier .transition(): this has no effect on the animation. subclassing UICompositionalLayout and overriding initialLayoutAttributesForAppearingItem(at itemIndexPath: IndexPath) as well as finalLayoutAttributesForDisappearingItem(at itemIndexPath: IndexPath): this has no effect on the animation. subclassing UICollectionViewCell and overriding apply(_ layoutAttributes: UICollectionViewLayoutAttributes): this only effects the orange cell-background on initial appearance. using snapshot.reloadItems([ItemIdentifier]) or snapshot.reconfigureItems([ItemIdentifier]): they have no effect on the animation and lead to inconsistent behaviour when combined with ObservableObject. setting collectionView.selfSizingInvalidation = .enabledIncludingConstraints: no effect. Setting it to .disabled stops the orange cells from resizing altogether. Is there a way to customize the size change animation of a cell using UIHostingConfiguration and ObservableObject? Code without animation: struct CellContentModel {     var height: CGFloat? = 100 } class CellContentController: ObservableObject, Identifiable {     let id = UUID()     @Published var cellContentModel: CellContentModel     init(cellContentModel: CellContentModel) {         self.cellContentModel = cellContentModel     } } class DataStore {     var data: [CellContentController]     var dataById: [CellContentController.ID: CellContentController]     init(data: [CellContentController]) {         self.data = data         self.dataById = Dictionary(uniqueKeysWithValues: data.map { ($0.id, $0) } )     }     static let testData = [         CellContentController(cellContentModel: CellContentModel()),         CellContentController(cellContentModel: CellContentModel(height: 80)),         CellContentController(cellContentModel: CellContentModel())     ] } class CollectionViewController: UIViewController {     enum Section {         case first     }     var dataStore = DataStore(data: DataStore.testData)     private var layout: UICollectionViewCompositionalLayout!     private var collectionView: UICollectionView!     private var dataSource: UICollectionViewDiffableDataSource<Section, CellContentController.ID>!     override func loadView() {         createLayout()         createCollectionView()         createDataSource()         view = collectionView     } } // - MARK: Layout extension CollectionViewController {     func createLayout() {         let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .estimated(50))         let Item = NSCollectionLayoutItem(layoutSize: itemSize)         let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.8), heightDimension: .estimated(300))         let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [Item])         let section = NSCollectionLayoutSection(group: group)         layout = .init(section: section)     } } // - MARK: CollectionView extension CollectionViewController {     func createCollectionView() {         collectionView = .init(frame: .zero, collectionViewLayout: layout)         let doubleTapGestureRecognizer = DoubleTapGestureRecognizer()         doubleTapGestureRecognizer.doubleTapAction = { [unowned self] touch, _ in             let touchLocation = touch.location(in: collectionView)             guard let touchedIndexPath = collectionView.indexPathForItem(at: touchLocation) else { return }             let touchedItemIdentifier = dataSource.itemIdentifier(for: touchedIndexPath)!             dataStore.dataById[touchedItemIdentifier]!.cellContentModel.height = dataStore.dataById[touchedItemIdentifier]!.cellContentModel.height == 100 ? nil : 100 // <- this triggers the resizing }         collectionView.addGestureRecognizer(doubleTapGestureRecognizer)     } } // - MARK: DataSource extension CollectionViewController {     func createDataSource() {         let cellRegistration = UICollectionView.CellRegistration<C, CellContentController.ID>() { cell, indexPath, itemIdentifier in             let cellContentController = self.dataStore.dataById[itemIdentifier]!             cell.contentConfiguration = UIHostingConfiguration {                 TextView(cellContentController: cellContentController)             }             .background(.orange)         }         dataSource = .init(collectionView: collectionView) { collectionView, indexPath, itemIdentifier in             return collectionView.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item: itemIdentifier)         }         var initialSnapshot = NSDiffableDataSourceSnapshot<Section, CellContentController.ID>()         initialSnapshot.appendSections([Section.first])         initialSnapshot.appendItems(dataStore.data.map{ $0.id }, toSection: Section.first)         dataSource.applySnapshotUsingReloadData(initialSnapshot)     } } class DoubleTapGestureRecognizer: UITapGestureRecognizer {     var doubleTapAction: ((UITouch, UIEvent) -> Void)?     override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {         if touches.first!.tapCount == 2 {             doubleTapAction?(touches.first!, event)         }     } } struct TextView: View {     @StateObject var cellContentController: CellContentController     var body: some View { Text(cellContentController.cellContentModel.height?.description ?? "nil")             .frame(height: cellContentController.cellContentModel.height)             .background(.green)     } }
Replies
1
Boosts
1
Views
3k
Activity
Jul ’22
If you were writing Photos from scratch today, which framework(s) would you use?
A type of question I see often is "which UI framework is best? SwitUI, UIKit, AppKit, etc?" And the answer is, of course, usually "it depends" or "a mix, depending on what you need". I wanted to re-frame that question with specific parameters. Let's say you were writing Apple Photos for the Mac from the ground up, starting today. For the sake of discussion, it's going to be functionally identical to the Photos app that exists on the Mac today. Which means it: looks and feels like a true, native macOS app should be very performant in terms of rendering and scrolling through a library that may contain hundreds of thousands of photos maintains a large database which can be synced over iCloud etc When you go to write that first line of UI code, which UI framework are you reaching for, and why? If more than one, which ones would you use for each piece, and why? Especially interested to hear any viewpoints from Apple folks in this thought exercise!
Replies
3
Boosts
0
Views
1.3k
Activity
Jun ’22
Adding Segments to an HKWorkout
I want to add segments into a running HKWorkout. My understanding is that I should use the addWorkoutEvents method in my HKWorkoutBuilder. Here is a sample of my code: func saveSegment(forStep step: GymWorkoutEventStep) { let dateInterval = DateInterval(start: step.startDate ?? Date(), end: Date()) let metadata = [ HKMetadataKeyWasUserEntered: true ] let event = HKWorkoutEvent(type: .segment, dateInterval: dateInterval, metadata: metadata) builder?.addWorkoutEvents([event], completion: { success, error in print(success ? "Success saving segment" : error.debugDescription) }) } I’m trying to implement the same feature that the native Apple Watch “Workout” app has by adding a segment when a user double taps the screen. My code seems to work as I see “Success saving segment” in the console. However, once the workout is finished, the Apple Fitness app doesn’t show these segments. What am I missing? Also, is there a way to add distance and pace to each segment using MetadataKey’s or is this not an option? Thanks for any help you can provide.
Replies
0
Boosts
1
Views
1.3k
Activity
Jun ’22
SF Font for UI/UX.
Hi! I am working on the UI/UX project for Apple apps(iPhone/watchOS etc.) using FIGMA. I don't have any Apple device from where I could use 'SF Font' family natively. I have tried the alternative fonts but none of them appealed to me and my client too. I also downloaded their resources but still it changes the file after I edit the text. Thanks in Advance.
Replies
1
Boosts
0
Views
2.1k
Activity
May ’22
Conditionals and Identity
When using conditionals in view bodies, can I preserve identity between the true and false sides of the conditional by adding an explicit id? struct DogTreat: Identifiable { var expirationDate: Date var serialID: String var id: String { serialID } } ... struct WrapperView: View { ... var treat: DogTreat var isExpired: Bool { treat.expirationDate < .now } var body: some View { if isExpired { DogTreatView(treat) .id(treat.id) .opacity(0.75) else { DogTreatView(treat) .id(treat.id) } } ... } Does this perform / behave the same as struct WrapperView: View { ... var treat: DogTreat var isExpired: Bool { treat.expirationDate < .now } var body: some View { DogTreatView(treat) .opacity(isExpired ? 0.75 : 1.0) } ... }
Replies
2
Boosts
0
Views
2k
Activity
May ’22
Reservation form
I am building an app for my business, my business is pet care and I want my clients to be able to make a reservation from the app and for me to receive the form entries I only want to use storyboard in Xcode 13 how would I do this?
Replies
1
Boosts
0
Views
1.3k
Activity
May ’22