Post

Replies

Boosts

Views

Activity

Reply to Share arbitrary struct from HostApp to Extension and get arbitrary result back
Second, SwiftUIs ShareLink is able to use the transferRepresentation of my ShareInput and ShareResult structs. I can call itemProvider.loadTransferable to extract my data. However, when using an ItemProvider for NSItemProviderWriting, NSItemProviderReading for the UIActivityItemsConfiguration, the transferred item is not a ShareInput or ShareResult struct, but the ItemProvider as wrapper around my struct. Probably because the transferred item must inherit from NSObject. Thus I cannot use UTType.shareInput.identifier, but must export and import the wrapper UTType.inputItemSource.identifier instead. While this works, it is ugly! Is there a possibility to transfer a struct with a UIActivityItemsConfiguration, or do I need to convert my ShareInput and ShareResult structs into classes, and then add NSItemProviderWriting, NSItemProviderReading with an extension? How does ShareLink do this? It transfers the struct (which is no descendant of NSObject) and not a wrapper...
Topic: App & System Services SubTopic: General Tags:
Aug ’24
Reply to Share arbitrary struct from HostApp to Extension and get arbitrary result back
Third, I played around with the NSExtensionActivationRule. While I can easily hide my extension (and thus the ContainingApp) from being shown in the ShareSheet, I cannot prevent Freeform and Threema in the application row, and “New Quick Note”, "Save to Files”, and “Airdrop” being shown when the HostApp opens the ShareSheet: How can the HostApp limit the possible share targets to just my ActionExtension and nothing else?
Topic: App & System Services SubTopic: General Tags:
Aug ’24
Reply to UserDefaults.didChangeNotification not firing
Hi Quinn, sadly this doesn't work for my SwiftUI iOS app. I added the startObserving() routine to my controller class, and called it from init, but it neither triggers the print(change) closure when I switch to iOS Settings->Apps->myApp and toggle the Toggle, nor when I return back to my app... Is this because the UserDefaults property is a string and my Settings.bundle Root.plist defines a PSToggleSwitchSpecifier? How can I observe a Toggle (instead of a string)?
Topic: App & System Services SubTopic: General Tags:
Mar ’25
Reply to UserDefaults.didChangeNotification not firing
OK, got it running: extension UserDefaults { private static let myBoolKey = "myBool" @objc dynamic var myBool: Bool { get { bool(forKey: UserDefaults.myBoolKey) } set { set(newValue, forKey: UserDefaults.myBoolKey) } } } (note that the value of the key "myBool" MUST BE identical to the name of the dynamic myBool, otherwise it doesn't work) and then call it in the init function of the (singleton) class: self.myBoolObservation = UserDefaults.standard.observe(\.myBool, options: [.new, .old,.prior,.initial]) { [weak self](_, _) in self?.myBool = UserDefaults.standard.myBool } However, this works only once on iOS 18, or rather only while the app keeps running. When you kill the app and launch it again, the closure is never called again. On iOS 17 I can quit and relaunch my app, and this code continues to work fine... Thus, still the problem of the thread starter (aolguin) -and mine too- is unsolved: since iOS 18 we don't get notifications in the app when the user changes app settings in Settings->Apps->myApp.
Topic: App & System Services SubTopic: General Tags:
Mar ’25
Reply to Animate layout change
Adapting sample code for a "Flow Layout", I built this Layout, to be able to animate changes: (see code block below) struct MyLayout: Layout { func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize { guard !subviews.isEmpty else { return .zero } // ask subviews for their ideal size let sizes = subviews.map { $0.sizeThatFits(.unspecified) } var totalHeight: CGFloat = 0 var totalWidth: CGFloat = 0 var lineWidth: CGFloat = 0 var lineHeight: CGFloat = 0 for size in sizes { if lineWidth > 0 { if let propWidth = proposal.width { print("sizeThatFits: proposal.width: \(propWidth)") if lineWidth + horzSpacing + size.width > propWidth { totalHeight += lineHeight + vertSpacing // next line + spacing lineWidth = size.width lineHeight = size.height } else { lineWidth += size.width + horzSpacing // next item + spacing lineHeight = max(lineHeight, size.height) } } else { lineWidth += size.width + horzSpacing // next item + spacing lineHeight = max(lineHeight, size.height) } } else { // first subview, just place it lineWidth = size.width lineHeight = size.height } totalWidth = max(totalWidth, lineWidth) } totalHeight += lineHeight // first line, zero if there is no first return .init(width: totalWidth, height: totalHeight) } func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) { let sizes = subviews.map { $0.sizeThatFits(.unspecified) } var lineX = bounds.minX var lineY = bounds.minY var lineHeight: CGFloat = 0 for index in subviews.indices { let size = sizes[index] if lineX > bounds.minX { if let propWidth = proposal.width { print("placeSubviews: proposal.width: \(propWidth)") if lineX + size.width > propWidth { lineY += lineHeight + vertSpacing lineHeight = 0 lineX = bounds.minX } } } subviews[index].place(at: .init(x: lineX, y: lineY), anchor: .zero, proposal: ProposedViewSize(size) ) lineHeight = max(lineHeight, size.height) lineX += size.width + horzSpacing } } } But this is never called with the space available in proposal, thus it cannot calculate whether the circles would fit adjacent to the squares, or whether they should be moved to the next row. I wonder how the "Flow Layout" should work at all... Then I found this explanation about sizeThatFits: /// the parent view can call this method more than once with different proposals: /// .zero for the layout’s minimum size /// .infinity for the layout’s maximum size /// .unspecified for the layout’s ideal size which absolutely makes no sense IMHO. I need the exact (horizontal) space available as input to be able to calculate my layout, and place the subviews... Of course, I could return oneLine both as ideal and maximum size, and twoLines as minimum size, but it really depends on how much space there is which layout should be chosen, and whether I need to put twoLines into a (horizontal) scrollView or not. I remember having the same problem 15 years ago when implementing heightForTableViewCell: You tell me how much space I have horizontally, then I can return how much space I need vertically to place my content. Was tricky then, seems still not easy now :-( The sample code Apple provides for layout: "Composing custom layouts with SwiftUI" is completely useless. sizeThatFits should measure the box around the circle layout where the Avatars are placed, but it always returns the same size since that doesn't change at all, only the subview placement changes based on the user input. So, any tips how I could solve my animation problem?
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Mar ’25