Post

Replies

Boosts

Views

Activity

Reply to macOS app icon not updating. Cached?
Answer by @shenlong2210 worked for me, though for zsh I needed slightly different syntax: sudo rm -rfv /Library/Caches/com.apple.iconservices.store sudo find /private/var/folders/ \( -name com.apple.dock.iconcache -or -name com.apple.iconservices \) -exec rm -rfv {} \; sleep 3 sudo touch /Applications/* touch ~/Applications/* killall Dock killall Finder (I didn't use the touch commands and it seemed to work for my situation.)
Nov ’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 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.
Sep ’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.
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) }
Jul ’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() }
Jul ’24