Hi,
I've come across this weird issue which I think is probably a bug, but before I file a radar I'll ask here just in case I'm doing something in a weird way. I found that on macOS if I have a Toggle inside a GeometryReader inside a Tab, and I have an onChange handler for some property of the geometry (even if the onChange doesn't do anything) then the second time I switch to that tab, I get a whole lot of 'AttributeGraph: cycle detected through attribute' logs and the app hangs. It doesn't matter whether it's on the first or second tab, but I've put it in the first tab here.
This only happens on macOS… at first I thought it also happened on iOS, but it turned out that was a similar symptom caused by an unrelated issue.
Here is some code that reproduces the issue:
TabView {
Tab("tab 1", systemImage: "rainbow") {
Toggle("This toggle is fine", isOn: .constant(true))
}
Tab("tab 2", systemImage: "checkmark") {
GeometryReader { geometry in
VStack {
//but with this toggle here, combined with the onChange handler,
//the second time we switch to this tab we get the hang
Toggle("This toggle causes a hang the second time we switch to this tab", isOn: .constant(true))
//if we comment out the toggle and uncomment the text instead,
//it's fine.
//Text("This text does not cause a hang")
}.onChange(of: geometry.size.height) {
//even if this is empty, having it here makes
//the app hang when switching to this tab for the second time.
//and emit 'AttributeGraph: cycle detected through attribute' in the log
}
}
}
}
.padding()
If I remove either the Toggle or the onChange handler, there is no problem. I can put all sorts of other things in the tab, but as soon as I put a toggle there, I get this hang. For now I've worked around it by putting Toggles in a settings sheet rather than directly on the tab, but since there's plenty of space on macOS it would be nice to have them directly on the tab.
One thing that's weird is that if I put this same code in the Settings window of an app, it doesn't seem to have the problem — maybe because the tabs are a different style there.
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Created
Hi,
I've wrapped AVRoutePickerView in SwiftUI using pretty much the code given here, with a few changes:
func makeUIView(context: Context) -> UIView {
let routePickerView = AVRoutePickerView()
// Configure the button's color.
//routePickerView.delegate = context.coordinator
//routePickerView.backgroundColor = .secondarySystemBackground
routePickerView.tintColor = .accent
routePickerView.activeTintColor = .accent
// Indicate whether your app prefers video content.
routePickerView.prioritizesVideoDevices = false
return routePickerView
}
I commented out routePickerView.delegate = context.coordinator because it doesn't compile; context.coordinator is of type Void and I'm not sure how to fix that. I'm not sure if that has anything to do with the issue.
Anyway, this works fine without VoiceOver; if I tap the button, I get the AirPlay popover. But in VoiceOver, if I select the button and double-tap, nothing happens… it just reads the button's accessibilityLabel again. How can I get the AirPlay popover to show in VoiceOver?
Hi,
I have a document-based SwiftUI multiplatform app, where the document is saved as JSON. Obviously I don't want Quick Look to show the JSON of my file, so I made a Quick Look Preview extension for each platform.
The macOS one works… okay, sometimes it's tricky to test and I need to use qlmanage to empty the cache or to show the preview, but it does work. I can even debug it.
But the iOS one just never seems to be run. If I show Quick Look in the Files app on iOS, or if I AirDrop a file from my app to my iPhone, it shows as JSON, with an option to open it in my app. If I run the iOS Preview Extension in the debugger and launch the Files app, and then try to use Quick Look in a file there, it shows the JSON and the debugger just stays in the state 'Waiting to Attach'.
The preview extensions are not data based; in both of them I have implemented
func preparePreviewOfFile(at url: URL) async throws
in PreviewViewController.swift. Pretty much the same code except one is using a UIHostingController and the other is using an NSHostingController. The only difference in the Info.plists for the two extensions is that the iOS one uses NSExtensionMainStoryboard while the macOS one uses NSExtensionPrincipalClass. This is how they were set up when I created them from the relevant Quick Look Preview Extension templates.
I made a sample project with a much simpler document, UI, etc. and I have the same issue there. The macOS preview works, the iOS one never runs. I have checked that the correct preview extension is embedded in the target for each OS (under Embed Foundation Extensions in the Build Phases)
Is there anything I need to do differently in iOS, or anything I might have inadvertently got wrong? Is there a way to run something similar to qlmanage on iOS, since that sometimes seems to help on macOS?
Incidentally, I have also added Quick Look Thumbnail extensions, and they work on both platforms.
Hi,
I'm adding tabs to the iOS version of my multiplatform app using TabView. I want the individual tabs to have names and icons. In iOS 17 and below, I have to do this using:
tabContent().tabItem {
Label(titleKey, systemImage: systemImage)
}
but this is deprecated, so in iOS 18 I would like to use the new version:
Tab(titleKey, image: systemImage) {
content()
}
It would be annoying to have to have the two cases for each individual tab, so I'm trying to abstract it into a custom SwiftUI view like this:
var body: some View {
if #available(iOS 18.0, *) {
Tab(titleKey, image: systemImage) {
content()
}
} else {
content().tabItem {
Label(titleKey, systemImage: systemImage)
}
}
}
There's a bit more to the custom view because I also have cases for iPad and macOS where I just have the views next to each other without tabs, but that's not really relevant to the question other than providing further motivation for abstracting this.
However, with this code, I get the error:
'buildExpression' is unavailable: this expression does not conform to 'View'
on the Tab line, because Tab isn't a view, and it can only be used directly inside a TabView.
For now at least, I can just use tabItem on all iOS versions and it works, but I'd prefer not to in case it is removed some time soon. I do want to support iOS 17 because that's what my iPad runs. Is there any clean way to do this?