Explore the various UI frameworks available for building app interfaces. Discuss the use cases for different frameworks, share best practices, and get help with specific framework-related questions.

All subtopics
Posts under UI Frameworks topic

Post

Replies

Boosts

Views

Created

String catalogs in packages
We have separated much of our UI into different packages to reduce complexity and compile time. When we recently tested using new .xcstrings string catalogs, we hit an unexpected problem. Strings extracted from SwiftUI components like Text or Button are extracted into the Localizable.xcstrings in the same package, but the default behaviour of Text(_ key:tableName:bundle:comment:) is to use Bundle.main. When the default behaviour of the string extraction isn't to extract to the main app target, this introduces a very fragile system where it's easy to add code that looks localised, but ends up failing lookup at runtime. I don't feel comfortable that we will always remember to define the correct module every time we create a Text. Also, other components like Button doesn't have an init that takes a Bundle, so we would also have to remember that Button(_ titleKey:action:) can now only be used in a package if we make sure that the main bundle contains a matching key. Is there a way for us to make sure that strings are always extracted to the same place as they are resolved against by default? Either by having strings in packages extracted to an xcstrings file in the main app or having Text default to resolving against the module bundle by default?
4
4
2.3k
Sep ’23
How to support foregroundColor (deprecated) and foregroundStyle in watchOS 9/10?
In my Watch app on watchOS 9 I was using .foregroundColor(myColour) to colour the text in a widgetLabel on a corner complication like this: let myColour: Color = functionThatReturnsAColorObjectConstructedLike Color.init(...) // green .widgetLabel { Text(myText) .foregroundColor(myColour) } It worked fine; the widget label was green. Now, in watchOS 10, I see that foregroundColor() is being deprecated in favour of foregroundStyle(), and I can use .foregroundStyle(.green), and - importantly - foregroundStyle() is only available on watchOS 10 and newer. myColour is calculated depending on some other info, so I can't just write .green, and when I use .foregroundStyle(myColour) the widget label comes out as white every time, even if I set myColour = .green. I think I have to use some sort of extension to pick the right combination, something like: extension View { func foregroundType(colour: Colour, style: any ShapeStyle) -> some THING? { if #available(watchOS 10.0, *) { return foregroundStyle(style) } else { return foregroundColor(colour) } } } // Usage let myStyle: any ShapeStyle = SOMETHING? ... .widgetLabel { Text(myText) .foregroundType(colour: myColour, style: myStyle) It doesn't work. I just can't figure out what should be returned, nor how to return it. Any ideas?
3
2
3.6k
Sep ’23
Disabled button in SwiftUI .alert not working
I found an issue when implementing an alert with a TextField to input a name. I want the action button to be disabled until a name has been entered, but the action block is never executed when the button has become enabled and pressed. The problem seems to appear only when name is initially an empty string. Tested with iOS 17.0. struct MyView: View { @State private var name = "" var body: some View { SomeView() .alert(...) { TextField("Name", text: $name) Button("Action") { // Action }.disabled(name.isEmpty) Button("Cancel", role: .cancel) {} } } }
14
6
4.4k
Sep ’23
Invalid Numeric Value (NaN) Error in SwiftUI's TextField on Long-Press
I'm experiencing a peculiar issue with SwiftUI's TextField. Whenever I long-press on the TextField, the console outputs an error about passing an invalid numeric value (NaN, or not-a-number) to the CoreGraphics API. This issue persists even in a new Xcode project with minimal code. Code Snippet: import SwiftUI struct ContentView: View { @State private var text: String = "" var body: some View { TextField("Placeholder", text: $text) } } Error: this application, or a library it uses, has passed an invalid numeric value (NaN, or not-a-number) to CoreGraphics API and this value is being ignored. Please fix this problem. Steps to Reproduce: Create a new SwiftUI project in Xcode. Add a TextField to the ContentView. Run the app on a device or simulator. Long-press inside the TextField. What I've Tried: Updating to the latest version of Xcode and iOS. Using UIViewRepresentable to wrap a UIKit UITextField. Creating a new Xcode project to isolate the issue. None of these steps have resolved the issue. Questions: Has anyone else encountered this problem? Are there any known workarounds for this issue? Is this a known bug, and if so, has it been addressed in any updates?
21
34
14k
Oct ’23
Mac Catalyst: Presenting view controller <UIAlertController:> from detached view controller <MyViewController:> is not supported, and may result in incorrect safe area insets and a corrupt root presentation on Sonoma
Okay so I'm getting this log every time I present a UIAlertController: Mac Catalyst: Presenting view controller <UIAlertController: 0x10f027000> from detached view controller <MyViewController: 0x10d104080> is not supported, and may result in incorrect safe area insets and a corrupt root presentation. Make sure <MyViewController: 0x10d104080> is in the view controller hierarchy before presenting from it. Will become a hard exception in a future release. A few points: MyViewController is not detached and the presentation shows just fine. I specifically check for this before presenting the alert controller like so: BOOL okayToPresentError = (self.isViewLoaded && self.view.window != nil); if (okayToPresentError) { [self presentErrorInAlertController:error]; } else { //Wait until view did appear. self.errorToPresentInViewDidAppear = error; } It spews out every time an error is fed back to my app and I present the alert controller (I can turn off the network connection and I show an alert controller with a "retry" button in it which will loop the error back so I can replay the error alert presentation over and over again) . Every time the alert controller is presented, I get this spewing in the console. Please don't start throwing hard exceptions because the check is faulty.
4
1
2.3k
Oct ’23
visionOS Window launch size
Before visionOS Beta 4 it was possible to define the launch size in the Info.plist using PreferredLaunchSize like so: <key>UILaunchPlacementParameters</key> <dict> <key>PreferredLaunchSize</key> <dict> <key>Height</key> <integer>750</integer> <key>Width</key> <integer>750</integer> </dict> </dict> In visionOS Beta 4 this now doesn't work anymore and the window opens in a 16:9 format and then will scale down to the .defaultSize of the WindowGroup with an animation. Settings, Notes, Safari still open with a different default size though, including the launch screen. How are we supposed to do this now?
2
1
1.2k
Oct ’23
New UIButton title left aligned
Hello There, I was trying to do a Button with title left aligned. With the old UIButton, I would have used the title-/contentEdgeInsets to achieve this, but with the new button these values are deprecated and do not work anymore. As far as I understood, with the new button only the spacing between the elements (sub-, title and image) is controllable. I also tried just setting the buttons width to the same width as the label, what would be possible in my case since it should be kind of a "hyperlink-button" which has no background or border. This works so far, but once the user interacts with the button, the title label gets multiline, which is on the one hand not wanted and on the other hand of course does not look left aligned anymore. (Besides the buttons height is fix in my case which leads to clipping of the title) So here again in short: Is there any way to make the title of the new UIButton left aligned inside of the button? Thank you very much in advance.
2
0
1.7k
Oct ’23
Infinite loop getting "_dismiss changed"
I'm working on a NavigationStack based app. Somewhere I'm using: @Environment(\.dismiss) private var dismiss and when trying to navigate to that view it gets stuck. I used Self._printChanges() and discovered the environment variable dismiss is changing repeatedly. Obviously I am not changing that variable explicitly. I wasn't able to reproduce this in a small project so far, but does anybody have any idea what kind of thing I could be doing that might be causing this issue? iOS 17.0.3
11
7
2.3k
Oct ’23
NavigationSplitView detail view with safearea
Hi, I have a NavigationSplitView with a view in the detail section: NavigationSplitView { ZStack { Color.black.ignoresSafeArea() gradientBlack2Blue.opacity(0.25) .ignoresSafeArea() GeometryReader { p in VStack { List { SidebarViewCell(id: "1", text: "Steuersätze" , type: .TAX_MASTERDATA , selectedMasterdataType: $selectedMasterdataType) }.listRowSpacing(size.height * 1.25 / 100 ) .scrollContentBackground(.hidden) .toolbar(.hidden, for: .navigationBar) .frame(width: p.size.width * 98 / 100 , height: p.size.height, alignment: .topLeading). }alignment: .topLeading) } } } detail: { MasterdataDetailView().ignoresSafeArea() } }.navigationSplitViewStyle(.balanced) When I place a Button-Control in the MasterdataDetailView it cannot be clicked because it is in the safe area. How can I make it clickable? Best Regards, Frank
8
0
1.7k
Oct ’23
Help with trailing closure errors
I am new to swiftui and have a very small project I am working on to practice passing data between views. I have this error on a form "Trailing closure passed to parameter of type 'FormStyleConfiguration' that does not accept a closure" If I comment the first section in the form then I get three errors in the ForEach. If anyone can help explain what is going on and what steps I could take to determine where the problem is coming from I would appreciate the help. This is the model I created: import Observation import SwiftUI @Model final class Client { var id = UUID() var name: String var location: String var selectedJob: Person init(id: UUID = UUID(), name: String, location: String, selectedJob: Person) { self.id = id self.name = name self.location = location self.selectedJob = selectedJob } } extension Client { enum Person: String, CaseIterable, Codable { case homeOwner = "Home Owner" case contractor = "Contractor" case designer = "Designer" } } @Model class Enclosure { var id = UUID() var room: String = "" var unitType: String = "" init(id: UUID = UUID(), room: String, unitType: String) { self.id = id self.room = room self.unitType = unitType } } This is the detail view where the error is happening: import SwiftData import SwiftUI struct DetailView: View { @Environment(\.modelContext) private var modelContext @Environment(\.dismiss) private var dismiss @Query(sort: \Enclosure.room, order: .forward, animation: .default) private var enclosures: [Enclosure] @State private var showingAddEnclosure = false @State private var showingAddMirror = false @State private var name: String = "" @State private var location: String = "" @State private var selectedJob = Client.Person.homeOwner @State var clients: Client var body: some View { NavigationStack { Form { Section("Details") { TextField("Full Name", text: Client.$name) TextField("Location", text: Client.location) Picker("Job Type", selection: $selectedJob) { ForEach(Client.Person.allCases, id: \.self) { selected in Text(selected.rawValue).tag(selected) } } } Section("Enclosures") { List { ForEach($clients.enclosures) { enclosure in NavigationLink(destination: EnclosureDetailView()) { VStack { Text(enclosure.room) Text(enclosure.unitType) } } .swipeActions { Button("Delete", role: .destructive) { modelContext.delete(enclosure) } } } } } } .navigationTitle("Project Details") .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .topBarTrailing) { Menu { Button { showingAddEnclosure.toggle() } label: { Text("Add Enclosure") } Button { showingAddMirror.toggle() } label: { Text("Add Mirror") } } label: { Label("Add", systemImage: "ellipsis.circle") } } } .sheet(isPresented: $showingAddEnclosure) { EnclosureView() } .sheet(isPresented: $showingAddMirror) { EnclosureView() } } } }
3
0
969
Nov ’23
EditMode & EditButton not working in a way I expect
I have something that looks like: NavigationStack { List(self.items, id: \.self, selection: self.$selectedItems) { item in NavigationLink { ItemView(item: item) .environment(\.managedObjectContext, self.viewContext) } label: { LabelWithMenuView(object: item) { ptr in self.labelHandler(item: item, newName: ptr) } } } if self.editMode?.wrappedValue == .active { editButtons } else { TextField("Add Item", text: self.$newItem) .onSubmit { self.addItem() self.newItem = "" } .padding() } } #if os(iOS) .toolbar { EditButton() } .onChange(of: self.editMode?.wrappedValue) { old, new in print("editMode \(old) -> \(new)") } #endif With that layout, the edit button doesn't show up at all; if I put it as part of the List, it does show up, but the first click doesn't do anything; after that, it works, but the onChange handler doesn't show it getting changed, and the editButtons don't go away.
8
2
2.5k
Nov ’23
NavigationSplitView, tvOS, and view focus
Hello, I have a SwiftUI application that uses NavigationSplitView. It's working great on iOS, iPad, and macOS. I decided to give it a try on tvOS. After it builds, it will not allow user interaction on the NavigationSplitView's sidebar. I've tried various view focus modifiers without any success. I'd also expect this to "just work" as default behavior. I have filed FB13447961 on this issue. Here is a distillation of the code that demonstrates the problem. Any ideas? Thank you. enum Category : String, CaseIterable { case first case second case third } enum Detail : String, CaseIterable { case one case two case three } struct DetailView : View { let category : Category? var body: some View { if let category { Text(category.rawValue) List(Detail.allCases, id: \.self) { detail in NavigationLink(value: detail) { Text(detail.rawValue) } } } else { Text("Select Category") } } } struct ContentView: View { // NOTE: If this category is set to something, it will show that category's detail. // The problem is that the NavigationSplitView sidebar does not have, nor does not // seem to be able to get focus. @State var category: Category? @State var path : [Detail] = [] var body: some View { NavigationSplitView { List(Category.allCases, id: \.self, selection: $category) { category in Text(category.rawValue) } } detail: { NavigationStack(path: $path) { DetailView(category: category) .navigationDestination(for: Detail.self) { detail in Text("\(detail.rawValue)") } } } } } #wwdc2023-10162 #wwdc20-10042
1
0
904
Dec ’23
NavigationSplitView: Make button clickable in detail-section safe area
Hi, I've tried to find a solution for this problem for weeks now but it seems no one knows how to solve it and Apple doesn't seem to care. I have a NavigationSplitView with two columns. In the detail column I have a button - or any other clickable control - which is placed in the very top where usually the safe area resides. The button is NOT clickable when he is in the safe area and I have NO idea why. I know I can place buttons in safe areas of other views and they are clickable. Please have a look at the code: `struct NavTestView: View { var body: some View { GeometryReader { p in VStack(spacing: 0) { NavigationSplitView { List(names) { Text($0.name).frame(width: p.size.width) .background(Color.green) }.listRowSpacing(p.size.height * 0.15 / 100 ) .toolbar(.hidden, for: .navigationBar) } detail: { TestView().ignoresSafeArea() }.frame(width: p.size.width, height: p.size.height, alignment: .topLeading) .background(Color.yellow) } } } } struct TestView: View { var body: some View { GeometryReader { p in let plusButton = IconButton(imageName: "plus.circle.fill", color: Color(uiColor: ThemeColor.SeaFoam.color), imageWidth: p.size.width * 5 / 100, buttonWidth: p.size.width * 5 / 100) let regularAddButton = Button(action: { log.info("| Regular Add Button pressed") } ) { plusButton } VStack { regularAddButton }.frame(width: p.size.width , height: p.size.height, alignment: .top) .background(Color.yellow) } } } ` this code produces the following screen: Any help would be really greatly appreciated! Thank you! Frank
1
0
687
Dec ’23
Scroll TextEditor to cursor position
Hello. Is there a good SwiftUI approach on getting the TextEditor cursor position? I have a TextEditor and sometimes when we have a longer text inside it, the cursor is not seen because the keyboard is above covering the bottom of the TextEditor. I would like to somehow detect the position of the cursor, and if it's on the last line of the TextEditor, scroll to the bottom. I've already checked a bit and didn't find any good method of doing this in SwiftUI. If you have any ideas on how to do this, or even a different method any help would be highly appreciated. Thank you!
1
3
821
Jan ’24
AppClip+TipKit: Tips status stuck at .pending and don't display
Hi - I use TipKit in my App and AppClip. TipKit is configured with the app group's datastore. The tips show in the App, but on the AppClip, with the same rules/state, the tips do not display. Is this expected? TipKit is not listed as one of the frameworks unavailable to AppClips. try? Tips.configure([ Tips.ConfigurationOption.displayFrequency(.hourly), Tips.ConfigurationOption.datastoreLocation(.groupContainer(identifier: BuildConfiguration.shared.userDefaultsSuite)) ])
2
1
832
Jan ’24
SwiftUi Picker in WatchOS throws Scrollview Error when using Digital Crown
The following WatchOs App example is very short, but already not functioning as it is expected, when using Digital Crown (full code): import SwiftUI struct ContentView: View { let array = ["One","Two","Three","Four"] @State var selection = "One" var body: some View { Picker("Array", selection: $selection) { ForEach(array, id: \.self) { Text($0) } } } } The following 2 errors are thrown, when using Digital Crown for scrolling: ScrollView contentOffset binding has been read; this will cause grossly inefficient view performance as the ScrollView's content will be updated whenever its contentOffset changes. Read the contentOffset binding in a view that is not parented between the creator of the binding and the ScrollView to avoid this. Error: Error Domain=NSOSStatusErrorDomain Code=-536870187 "(null)" Any help appreciated. Thanks a lot.
6
5
1.2k
Jan ’24
SwiftUI Sheet race condition
Hi! While working on my Swift Student Challenge submission it seems that I found a race condition (TOCTOU) bug in SwiftUI when using sheets, and I'm not sure if this is expected behaviour or not. Here's an example code: import SwiftUI struct ContentView: View { @State var myVar: Int? @State private var presentSheet: Bool = false var body: some View { VStack { // Uncommenting the following Text() view will "fix" the bug (kind of, see a better workaround below). // Text("The value is \(myVar == nil ? "nil" : "not nil")") Button { myVar = nil } label: { Text("Set value to nil.") } Button { myVar = 1 presentSheet.toggle() } label: { Text("Set value to 1 and open sheet.") } } .sheet(isPresented: $presentSheet, content: { if myVar == nil { Text("The value is nil") .onAppear { print(myVar) // prints Optional(1) } } else { Text("The value is not nil") } }) } } When opening the app and pressing the open sheet button, the sheet shows "The value is nil", even though the button sets myVar to 1 before the presentSheet Bool is toggled. Thankfully, as a workaround to this bug, I found out you can change the sheet's view to this: .sheet(isPresented: $presentSheet, content: { if myVar == nil { Text("The value is nil") .onAppear { if myVar != nil { print("Resetting View (TOCTOU found)") let mySwap = myVar myVar = nil myVar = mySwap } } } else { Text("The value is not nil") } }) This triggers a view refresh by setting the variable to nil and then to its non-nil value again if the TOCTOU is found. Do you think this is expected behaivor? Should I report a bug for this? This bug also affects .fullScreenCover() and .popover().
5
0
1.8k
Feb ’24