Post

Replies

Boosts

Views

Activity

Reply to Using KeyFrameTimeline as value for .keyFrameAnimator view modifier
Here is a more complete example of what I'm trying for: import SwiftUI struct Mark { var offset: CGSize var angle: Angle init(offset: CGSize, angle: Angle) { self.offset = offset self.angle = angle } init(x: CGFloat, y: CGFloat, angle: Angle) { self.offset = CGSize(width: x, height: y) self.angle = angle } } struct ContentView: View { static let blackMarks: [Mark] = [ Mark(x: 0, y: 0, angle: .degrees(0)), Mark(x: 50, y: 0, angle: .degrees(-90)), Mark(x: 50, y: -100, angle: .degrees(-180)), Mark(x: 0, y: -100, angle: .degrees(-270)), Mark(x: 0, y: 0, angle: .degrees(-360)) ] static let redMarks: [Mark] = [ Mark(x: 0, y: 0, angle: .degrees(0)), Mark(x: -50, y: 0, angle: .degrees(90)), Mark(x: -50, y: 100, angle: .degrees(180)), Mark(x: 0, y: 100, angle: .degrees(270)), Mark(x: 0, y: 0, angle: .degrees(360)) ] var body: some View { ZStack { Rectangle().fill(.black).frame(width: 20, height: 30) .keyframeAnimator(initialValue: Self.blackMarks[0]) { content, value in content .rotationEffect(value.angle) .offset(value.offset) } keyframes: { _ in KeyframeTrack(\.offset) { LinearKeyframe(Self.blackMarks[1].offset, duration: 1.0) LinearKeyframe(Self.blackMarks[2].offset, duration: 1.0) LinearKeyframe(Self.blackMarks[3].offset, duration: 1.0) LinearKeyframe(Self.blackMarks[4].offset, duration: 1.0) } KeyframeTrack(\.angle) { LinearKeyframe(Self.blackMarks[1].angle, duration: 1.0) LinearKeyframe(Self.blackMarks[2].angle, duration: 1.0) LinearKeyframe(Self.blackMarks[3].angle, duration: 1.0) LinearKeyframe(Self.blackMarks[4].angle, duration: 1.0) } } Rectangle().fill(.red).frame(width: 20, height: 30) .keyframeAnimator(initialValue: Self.blackMarks[0]) { content, value in content .rotationEffect(value.angle) .offset(value.offset) } keyframes: { _ in /* Now, generate the equivalent tracks and keyframes as a function of redMarks */ } } } } #Preview { ContentView() }
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Jul ’24
Reply to Using KeyFrameTimeline as value for .keyFrameAnimator view modifier
That doesn't hide all the KeyframeTrack items as well. It helps a bit, though: Rectangle().fill(.red).frame(width: 20, height: 30) .keyframeAnimator(initialValue: Self.blackMarks[0]) { content, value in content .rotationEffect(value.angle) .offset(value.offset) } keyframes: { _ in /* f(redMarks) */ KeyframeTrack(\.offset) { for value in Self.redMarks.dropFirst() { LinearKeyframe(value.offset, duration: 1.0) } } KeyframeTrack(\.angle) { for value in Self.redMarks.dropFirst() { LinearKeyframe(value.angle, duration: 1.0) } } That's still a lot of code for the keyframes parameter. The ideal solution would keep the code very tight in the view's body, and look like something this for readability: Rectangle().fill(.red).frame(width: 20, height: 30) .keyframeAnimator(initialValue: Self.blackMarks[0]) { content, value in content .rotationEffect(value.angle) .offset(value.offset) } keyframes: { _ in tracks(Self.redMarks) }
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Jul ’24
Reply to Using KeyFrameTimeline as value for .keyFrameAnimator view modifier
Here is a solution I'm quite happy with. It involves making the custom ViewModifier, which is fine since that pretty much what f(redMarks) is: struct CartoonMotion: ViewModifier { let marks: [Mark] func body(content: Content) -> some View { content .keyframeAnimator(initialValue: marks[0]) { content, value in content .rotationEffect(value.angle) .offset(value.offset) } keyframes: { _ in KeyframeTrack(\.offset) { for value in marks.dropFirst() { LinearKeyframe(value.offset, duration: 1.0) } } KeyframeTrack(\.angle) { for value in marks.dropFirst() { LinearKeyframe(value.angle, duration: 1.0) } } } } } extension View { func cartoonMotion(_ marks: [Mark]) -> some View { modifier(CartoonMotion(marks: marks)) } } In the main cartoon scene each "sprite" looks like this: Rectangle().fill(.red).frame(width: 20, height: 30) .cartoonMotion(Self.redMarks) This gives. Avery compact scene view that hides the details of converting Marks to KeyframeTrack and xxxKeyframes. Thanks to DTS Engineer for contributing to this solution.
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Jul ’24
Reply to searchable: Add an auxilliary search view
The purpose of the aux view is to give access to a set of semi-advanced search controls. The controls I want to present are more in the line of filter presets, like, say, an Amazon search's filter sidebar, but using Picker popups or toggles. In the screen GIF below, you'll see me tapping the "primary" search field and how it animates into search mode: The field moves up and shrinks to make space for the Cancel button. When canceling or submitting the search, search mode ends and it animates in reverse. My hacky aux view here is just a VStack with a couple of TextFields, for testing purposes. When I add it to MyListView it animates in nicely when isSearching becomes true, but animates out badly when it becomes false. To wit, the removal transition works correctly until it's done, and then the scroll view snaps up, hiding the primary search field. Without putting in the conditional aux view the primary search field stays visible. For an example of that, try the Notes app's search field. @Claude31, I want the aux view to only be visible only while the .searchable primary search field is active. When it's not active the aux view hides away. @DTS Engineer, the goal is a few critical preset filters. I believe using search suggestions (or tokens) would be worse as they would fill up/clog up the search text field and make it hard for a user to refine the search with their own typed text. Thanks for your replies and ideas. If I can't avoid the animation problem, I'll abandon this design goal and go with an "Advanced Search" sheet instead. Not as good as a compact live search, but OK.
Topic: UI Frameworks SubTopic: SwiftUI
Sep ’24
Reply to Custom struct Codable for SwiftData
Ditto here. All the other advice on the subject of struct-typed model attributes asserts the struct must be Codable and it stored in a single column. The evidence shows the struct's properties are stored as individual columns, spreading the structure out and using some truly unexpected Encoder/Decoder that's not JSON. For instance, I defined struct ComplexNumber: Codable { var real: Double var imaginary: Double } and @Model final class Item { var timestamp: Date var number1: ComplexNumber var number2: ComplexNumber and got this in SQLite: This works fine when fetching the records, but in my app it fails to decode when some of the struct's properties are Optional. I can write decoder() to deal with the optionals, but apparently SwiftData's Decoder can't cope. I suppose there's some similar limitation for Dictionary properties.
Sep ’24
Reply to Enabling undo with multi-model schema
(Don't post while sleepy) Right in the docs... .modelContainer(for: [MyModel1.self, MyModel2.self], isUndoEnabled: true)
Replies
Boosts
Views
Activity
Aug ’23
Reply to Optionally use iCloud with SwiftData
My trouble was stale cache in the simulator or Mac. Restarting/rebooting a couple times helped.
Replies
Boosts
Views
Activity
Sep ’23
Reply to TipKit: showing a popover tip on a SwiftUI toolbar button
Just don't use .automatic.
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Replies
Boosts
Views
Activity
Oct ’23
Reply to NavigationSplitView detail view with safearea
Would safeAreaInset, applied to the button you want to be tappable, help? (I have not dealt a lot with safe areas so I am guessing.) How to inset the safe area with custom content: https://www.hackingwithswift.com/quick-start/swiftui/how-to-inset-the-safe-area-with-custom-content
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Replies
Boosts
Views
Activity
Oct ’23
Reply to Unexpected intents
Solved. I had stupid dead code.
Topic: App & System Services SubTopic: General Tags:
Replies
Boosts
Views
Activity
Oct ’23
Reply to Tipkit Tips keep popping up after invalidation
I've tried these same steps and can't get the tip from popping up each new app launch.
Topic: App & System Services SubTopic: General Tags:
Replies
Boosts
Views
Activity
Nov ’23
Reply to Tipkit Tips keep popping up after invalidation
I just tried this: struct createTabTipHomePage: Tip { let id = "MyViewName.createTabTipHomePage" And now the tip will remember its state properly. And yet my other tips don't need this, so I don't know what's going on.
Topic: App & System Services SubTopic: General Tags:
Replies
Boosts
Views
Activity
Nov ’23
Reply to ShareLink FileRepresentation exports message as a file
FB13378024
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Replies
Boosts
Views
Activity
Nov ’23
Reply to tvOS play/pause gesture and non-av views
Solved my problem. I needed to add focusable: MyView() .focusable() .onPlayPauseCommand {...}
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Replies
Boosts
Views
Activity
Apr ’24
Reply to Using KeyFrameTimeline as value for .keyFrameAnimator view modifier
Here is a more complete example of what I'm trying for: import SwiftUI struct Mark { var offset: CGSize var angle: Angle init(offset: CGSize, angle: Angle) { self.offset = offset self.angle = angle } init(x: CGFloat, y: CGFloat, angle: Angle) { self.offset = CGSize(width: x, height: y) self.angle = angle } } struct ContentView: View { static let blackMarks: [Mark] = [ Mark(x: 0, y: 0, angle: .degrees(0)), Mark(x: 50, y: 0, angle: .degrees(-90)), Mark(x: 50, y: -100, angle: .degrees(-180)), Mark(x: 0, y: -100, angle: .degrees(-270)), Mark(x: 0, y: 0, angle: .degrees(-360)) ] static let redMarks: [Mark] = [ Mark(x: 0, y: 0, angle: .degrees(0)), Mark(x: -50, y: 0, angle: .degrees(90)), Mark(x: -50, y: 100, angle: .degrees(180)), Mark(x: 0, y: 100, angle: .degrees(270)), Mark(x: 0, y: 0, angle: .degrees(360)) ] var body: some View { ZStack { Rectangle().fill(.black).frame(width: 20, height: 30) .keyframeAnimator(initialValue: Self.blackMarks[0]) { content, value in content .rotationEffect(value.angle) .offset(value.offset) } keyframes: { _ in KeyframeTrack(\.offset) { LinearKeyframe(Self.blackMarks[1].offset, duration: 1.0) LinearKeyframe(Self.blackMarks[2].offset, duration: 1.0) LinearKeyframe(Self.blackMarks[3].offset, duration: 1.0) LinearKeyframe(Self.blackMarks[4].offset, duration: 1.0) } KeyframeTrack(\.angle) { LinearKeyframe(Self.blackMarks[1].angle, duration: 1.0) LinearKeyframe(Self.blackMarks[2].angle, duration: 1.0) LinearKeyframe(Self.blackMarks[3].angle, duration: 1.0) LinearKeyframe(Self.blackMarks[4].angle, duration: 1.0) } } Rectangle().fill(.red).frame(width: 20, height: 30) .keyframeAnimator(initialValue: Self.blackMarks[0]) { content, value in content .rotationEffect(value.angle) .offset(value.offset) } keyframes: { _ in /* Now, generate the equivalent tracks and keyframes as a function of redMarks */ } } } } #Preview { ContentView() }
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Replies
Boosts
Views
Activity
Jul ’24
Reply to Using KeyFrameTimeline as value for .keyFrameAnimator view modifier
That doesn't hide all the KeyframeTrack items as well. It helps a bit, though: Rectangle().fill(.red).frame(width: 20, height: 30) .keyframeAnimator(initialValue: Self.blackMarks[0]) { content, value in content .rotationEffect(value.angle) .offset(value.offset) } keyframes: { _ in /* f(redMarks) */ KeyframeTrack(\.offset) { for value in Self.redMarks.dropFirst() { LinearKeyframe(value.offset, duration: 1.0) } } KeyframeTrack(\.angle) { for value in Self.redMarks.dropFirst() { LinearKeyframe(value.angle, duration: 1.0) } } That's still a lot of code for the keyframes parameter. The ideal solution would keep the code very tight in the view's body, and look like something this for readability: Rectangle().fill(.red).frame(width: 20, height: 30) .keyframeAnimator(initialValue: Self.blackMarks[0]) { content, value in content .rotationEffect(value.angle) .offset(value.offset) } keyframes: { _ in tracks(Self.redMarks) }
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Replies
Boosts
Views
Activity
Jul ’24
Reply to Using KeyFrameTimeline as value for .keyFrameAnimator view modifier
Here is a solution I'm quite happy with. It involves making the custom ViewModifier, which is fine since that pretty much what f(redMarks) is: struct CartoonMotion: ViewModifier { let marks: [Mark] func body(content: Content) -> some View { content .keyframeAnimator(initialValue: marks[0]) { content, value in content .rotationEffect(value.angle) .offset(value.offset) } keyframes: { _ in KeyframeTrack(\.offset) { for value in marks.dropFirst() { LinearKeyframe(value.offset, duration: 1.0) } } KeyframeTrack(\.angle) { for value in marks.dropFirst() { LinearKeyframe(value.angle, duration: 1.0) } } } } } extension View { func cartoonMotion(_ marks: [Mark]) -> some View { modifier(CartoonMotion(marks: marks)) } } In the main cartoon scene each "sprite" looks like this: Rectangle().fill(.red).frame(width: 20, height: 30) .cartoonMotion(Self.redMarks) This gives. Avery compact scene view that hides the details of converting Marks to KeyframeTrack and xxxKeyframes. Thanks to DTS Engineer for contributing to this solution.
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Replies
Boosts
Views
Activity
Jul ’24
Reply to Xcode 16 beta 4 "devices and simulators" blank except for "No selection"
Workaround: Perform this command in Terminal... defaults delete com.apple.dt.Xcode
Replies
Boosts
Views
Activity
Aug ’24
Reply to searchable: Add an auxilliary search view
The purpose of the aux view is to give access to a set of semi-advanced search controls. The controls I want to present are more in the line of filter presets, like, say, an Amazon search's filter sidebar, but using Picker popups or toggles. In the screen GIF below, you'll see me tapping the "primary" search field and how it animates into search mode: The field moves up and shrinks to make space for the Cancel button. When canceling or submitting the search, search mode ends and it animates in reverse. My hacky aux view here is just a VStack with a couple of TextFields, for testing purposes. When I add it to MyListView it animates in nicely when isSearching becomes true, but animates out badly when it becomes false. To wit, the removal transition works correctly until it's done, and then the scroll view snaps up, hiding the primary search field. Without putting in the conditional aux view the primary search field stays visible. For an example of that, try the Notes app's search field. @Claude31, I want the aux view to only be visible only while the .searchable primary search field is active. When it's not active the aux view hides away. @DTS Engineer, the goal is a few critical preset filters. I believe using search suggestions (or tokens) would be worse as they would fill up/clog up the search text field and make it hard for a user to refine the search with their own typed text. Thanks for your replies and ideas. If I can't avoid the animation problem, I'll abandon this design goal and go with an "Advanced Search" sheet instead. Not as good as a compact live search, but OK.
Topic: UI Frameworks SubTopic: SwiftUI
Replies
Boosts
Views
Activity
Sep ’24
Reply to Custom struct Codable for SwiftData
Ditto here. All the other advice on the subject of struct-typed model attributes asserts the struct must be Codable and it stored in a single column. The evidence shows the struct's properties are stored as individual columns, spreading the structure out and using some truly unexpected Encoder/Decoder that's not JSON. For instance, I defined struct ComplexNumber: Codable { var real: Double var imaginary: Double } and @Model final class Item { var timestamp: Date var number1: ComplexNumber var number2: ComplexNumber and got this in SQLite: This works fine when fetching the records, but in my app it fails to decode when some of the struct's properties are Optional. I can write decoder() to deal with the optionals, but apparently SwiftData's Decoder can't cope. I suppose there's some similar limitation for Dictionary properties.
Replies
Boosts
Views
Activity
Sep ’24