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

Activity

'tabViewBottomAccessory' leaves an empty accessory area when conditionally hidden
We use SwiftUI's .tabViewBottomAccessory in our iOS apps for displaying an Audio MiniPlayer View (like in the Apple Music App). TabView(selection: $viewModel.selectedTab) { // Tabs here } .tabViewBottomAccessory { if viewModel.showAudioMiniPlayer { MiniPlayerView() } } The Problem This code works perfectly on iOS 26.0. When viewModel.showAudioMiniPlayer is false, the accessory is completely hidden. However, on iOS 26.1 (23B5059e), when 'viewModel.showAudioMiniPlayer' becomes false, the MiniPlayerView disappears, but an empty container remains, leaving a blank space above the tab bar. Is this a known Bug in iOS 26.1 and are there any effective workarounds?
Topic: UI Frameworks SubTopic: SwiftUI
8
5
941
Nov ’25
A basic DocumentGroup App presents two back "<" buttons on the navigation bar.
Please has anyone found a workaround for duplicate back buttons appearing on the toolbar of a ContentView launched from a DocumentGroup? The problem occurs with Xcode 14.0 running a basic DocumentGroup App on iOS 16.0. To reproduce, simply build a new project using the "Document App" template. Build and run in the iOS/iPadOS simulator or on an iOS/iPadOS device. Two back buttons appear. Only one functions. I've not found a way to eliminate the dud. This problem has occurred throughout the Xcode 14.0 beta program.
8
6
2k
May ’25
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
May ’25
Looking for advice on app architecture
I have an existing Mac app which has evolved through over twenty years of development. It is currently written in a mixture of C++ and Objective-C, with a bit of C and Swift in a few places. For a few years now, I have been tinkering with replacing the UI with SwiftUI. The model has been completely rewritten in Swift and works fine. After a few tries, no version has been working acceptably, so I'm thinking that I need to rethink the architecture. The UI consists of a window with a master-detail view. The detail view is what users spend most of their time with. It contains a lot of subviews, around 100 typically. Keyboard events affect the display, so I've had a dedicated data structure to hold the state that is needed for displaying all the subviews. Using Instruments, I see that the view seems to recreate the subviews three times per keyboard event, so I'm clearly doing something wrong. A second factor is that there are a couple of dozen commands that are applicable to the detail view, driven either by menu items, keyboard shortcut, or toolbar button. Adding all of those to the view makes for a massive SwiftUI View, which seems unlikely to be good practice. The current implementation has the controller class broken up with categories, but still they are big classes. Most SwiftUI stuff on the web is iOS-oriented, and typically has a focus on fairly simple apps, so the whole topic of dealing with menu commands doesn't get a lot of coverage, so I've been doing all that through my own solutions, which are probably nothing like optimal. What I've been able to find is not particularly helpful for a full-fledged application like mine, so I'm looking for advice on how to structure the app. The existing one is largely MVC, but I've tried a similar approach and a shot at MVVM, but I'm not getting good results so far. So, pointers, places I can read more, or samples of real-world apps is what I'm after. Anyone?
Topic: UI Frameworks SubTopic: SwiftUI Tags:
8
0
172
Sep ’25
macOS 26 toolbar has wrong tint color sometimes in Dark Appearance
I have a SwiftUI Mac Catalyst app. I create a toolbar like this NavigationSplitView(columnVisibility: $sceneModel.columnVisibility, preferredCompactColumn: $preferredColumn) { sidebarView() } detail: { contentView() .toolbar { ToolbarItemGroup(placement: .topBarTrailing) { HStack { Button { sceneModel.onMaps(sender: self) } label: { Image(systemName: "map") .font(.title2) } Button { sceneModel.onSearch(sender: self) } label: { Image(systemName: "magnifyingglass") .font(.title2) } ... } } } } When my Mac Appearance is set to dark mode and the content under the toolbar is dark the toolbar looks good like this. But then if I have light content under the toolbar, the glass effect changes to light, but the tint on the icons stays white instead of changing to black and it is hard to see the icon. It looks like this. When I set the Appearance on my Mac to light, then the toolbar works just fine on both dark and light colored backgrounds. Does anyone know how I can fix this when the appearance is Dark?
8
0
299
Sep ’25
Trying to use UIScrollEdgeElementContainerInteraction
I have a UIKit app with a custom navigation controller. I want my view title to go up into the navigation bar when the user scrolls down the screen. It looks like UIScrollEdgeElementContainerInteraction should do what I want, but I am having trouble using it. Below is a sample, where a header view represents a title. I added the interaction to the header view, but it seems to have no effect. Am I missing a step? Perhaps I misunderstand what this is supposed to do, or perhaps I do not understand the preconditions to make this work. I am hoping someone can tell me what I am doing wrong, or point me to some working sample code. Thank you. John class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { var headerView: UIVisualEffectView! var tableView: UITableView! var interaction: UIScrollEdgeElementContainerInteraction! override func viewDidLoad() { super.viewDidLoad() self.tableView = UITableView() self.tableView.translatesAutoresizingMaskIntoConstraints = false self.tableView.topEdgeEffect.style = .soft self.tableView.delegate = self self.tableView.dataSource = self self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell") self.view.addSubview(self.tableView) self.view.addConstraints([ self.tableView.topAnchor.constraint(equalTo: self.view.topAnchor), self.tableView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor), self.tableView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor), self.tableView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor), ]) self.headerView = UIVisualEffectView(effect: UIGlassEffect(style: .regular)) self.headerView.translatesAutoresizingMaskIntoConstraints = false self.headerView.backgroundColor = .green self.view.addSubview(self.headerView) self.view.addConstraints([ self.headerView.topAnchor.constraint(equalTo: self.view.topAnchor), self.headerView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor), self.headerView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor), self.headerView.heightAnchor.constraint(equalToConstant: 100.0), ]) let label = UILabel() label.translatesAutoresizingMaskIntoConstraints = false label.text = "my text" self.headerView.contentView.addSubview(label) self.headerView.contentView.addConstraints([ label.centerXAnchor.constraint(equalTo: self.headerView.contentView.centerXAnchor), label.centerYAnchor.constraint(equalTo: self.headerView.contentView.centerYAnchor), ]) self.interaction = UIScrollEdgeElementContainerInteraction() self.interaction.scrollView = self.tableView self.interaction.edge = .top self.headerView.addInteraction(self.interaction) } func numberOfSections(in tableView: UITableView) -> Int { return 1 } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 100 } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = self.tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) cell.textLabel?.text = "row \(indexPath.row + 1)" return cell } }
Topic: UI Frameworks SubTopic: UIKit Tags:
8
0
370
Oct ’25
NSCollectionLayoutBoundarySupplementaryItem background blur covering the entire layout section
My app has the following UI layout: NSSplitViewController as the windows contentViewController NSPageController in the content (right) split item NSTabViewController as the root items of the NSPageController NSViewController with a collection view in the first tab of that NSTabViewController The collection view is using a NSCollectionViewCompositionalLayout in which the sections are set up to have a header using NSCollectionLayoutBoundarySupplementaryItem with pinToVisibleBounds=true and alignment=top With macOS 26, the pinned supplementary item automatically gets a blurred/semi-transparent background that seamlessly integrates with the toolbar. When the window's title bar has a NSTitlebarAccessoryViewController added, the said semi-transparent background gets a bottom hard edge and a hairline to provide more visual separation from the main content. During runtime, my NSPageController transitions from the NSTabViewController to another view controller. When transitioning back, the semi-transparent blur bleeds into the entire section. This happens no matter if there's a NSTitlebarAccessoryViewController added or not. It doesn't happen 100% of the cases, it seems to depend on section size, header visibility and/or scroll position. But it happens more often than not. Most of the time, a second or so after the back transition - shortly after pageControllerDidEndLiveTransition: of the NSPageControllerDelegate is called - the view updates and the supplementary views are back to normal. Sometimes, the issue also appears not when transitioning using NSPageController, but simply by scrolling through the collection view. Anyone has an idea what is happening here? Below are two screenshots of both the "ok" and "not ok" state I'm on macOS 26.0.1 and I'm using XCode 26.0.1
Topic: UI Frameworks SubTopic: AppKit Tags:
8
0
417
Nov ’25
SwiftData & CloudKit: Arrays of Codable Structs Causing NSKeyedUnarchiveFromData Error
I have SwiftData models containing arrays of Codable structs that worked fine before adding CloudKit capability. I believe they are the reason I started seeing errors after enabling CloudKit. Example model: @Model final class ProtocolMedication { var times: [SchedulingTime] = [] // SchedulingTime is Codable // other properties... } After enabling CloudKit, I get this error logged to the console: 'NSKeyedUnarchiveFromData' should not be used to for un-archiving and will be removed in a future release CloudKit Console shows this times data as "plain text" instead of "bplist" format. Other struct/enum properties display correctly (I think) as "bplist" in CloudKit Console. The local SwiftData storage handled these arrays fine - this issue only appeared with CloudKit integration. What's the recommended approach for storing arrays of Codable structs in SwiftData models that sync with CloudKit?
8
1
931
Jan ’26
tvOS 18.0 Siri back button behavior bug
On testing my app with tvOS 18, I have noticed the Siri Remote back button no longer provides system-provided behavior when interacting with tab bar controller pages. Instead of moving focus back to the tab bar when pressed, the back button will close the app, as if the Home button was pressed. This occurs both on device and in the Simulator. Create tvOS project with a tab bar controller. Create pages/tabs which contain focusable items (ie. buttons) Scroll down to any focusable item (ie. a button or UICollectionView cell) Hit the Siri Remote back button. See expect behavior below: Expected behavior: System-provided behavior should move focus back to the tab bar at the top of the screen. Actual results: App is closed and user is taken back to the Home Screen. Has anyone else noticed this behavior?
8
3
1.3k
Aug ’25
SwiftUI TextField with optional value working for @State but not @Binding properties
I've encountered a potential bug where a TextField connected to an optional value (not a string) works as expected when bound to a @State property, but won't update a @Binding property. Here is some example code import SwiftUI struct ContentView: View { @Binding var boundValue: Double? @State private var stateValue: Double? = 55 var body: some View { TextField("Bound value", value: $boundValue, format: .number) Text("\(boundValue ?? .nan)") TextField("State value", value: $stateValue, format: .number) Text("\(stateValue ?? .nan)") } } #Preview { ContentView(boundValue: .constant(42.00)) } It's as though the optional value stored externally is preventing the value updating. Can anyone confirm whether this is intentional, or a bug? This is in Xcode 26b6, on macOS Tahoe 26b8, but from this query it looks like the problem has existed for years.
Topic: UI Frameworks SubTopic: SwiftUI
8
0
381
Aug ’25
Keep ScrollView position when adding items on the top
I am using a LayzVStack embedded into a ScrollView. The list items are fetched from a core data store by using a @FetchResult or I tested it also with the new @Query command coming with SwiftData. The list has one hundred items 1, 2, 3, ..., 100. The user scrolled the ScrollView so that items 50, 51, ... 60 are visible on screen. Now new data will be fetched from the server and updates the CoreData or SwiftData model. When I add new items to the end of the list (e.g 101, 102, 103, ...) then the ScrollView is keeping its position. Opposite to this when I add new items to the top (0, -1, -2, -3, ...) then the ScrollView scrolls down. Is there a way with the new SwiftData and SwiftUI ScrollView modifiers to update my list model without scrolling like with UIKit where you can query and set the scroll offset pixel wise?
8
1
9.9k
May ’25
Accessibility Permission In Sandbox For Keyboard
Hello! My question is about 1) if we can use any and or all accessibility features within a sandboxed app and 2) what steps we need to take to do so. Using accessibility permissions, my app was working fine in Xcode. It used NSEvent.addGlobalMonitorForEvents and localMoniter, along with CGEvent.tapCreate. However, after downloading the same app from the App Store, the code was not working. I believe this was due to differences in how permissions for accessibility are managed in Xcode compared to production. Is it possible for my app to get access to all accessibility features, while being distributed on the App Store though? Do I need to add / request any special entitlements like com.apple.security.accessibility? Thanks so much for the help. I have done a lot of research on this online but found some conflicting information, so wanted to post here for a clear answer.
8
0
515
Oct ’25
Swipe to go back still broken with Zoom navigation transition.
When you use .navigationTransition(.zoom(sourceID: "placeholder", in: placehoder)) for navigation animation, going back using the swipe gesture is still very buggy on IOS26. I know it has been mentioned in other places like here: https://developer.apple.com/forums/thread/796805?answerId=856846022#856846022 but nothing seems to have been done to fix this issue. Here is a video showing the bug comparing when the back button is used vs swipe to go back: https://imgur.com/a/JgEusRH I wish there was a way to at least disable the swipe back gesture until this bug is fixed.
8
2
593
Feb ’26
macOS SwiftUI Sheets are no longer resizable (Xcode 16 beta2)
For whatever reason SwiftUI sheets don't seem to be resizable anymore. The exact same code/project produces resizable Sheets in XCode 15.4 but unresizable ones with Swift included in Xcode 16 beta 2. Tried explicitly providing .fixedSize(horizontal false, vertical: false) everywhere humanly possible hoping for a fix but sheets are still stuck at an awkward size (turns out be the minWidth/minHeight if I provide in .frame).
7
1
2.7k
Jan ’26
SwiftUI state is maddening
I honestly thought I was getting somewhere with this, but alas, no. Every time I do anything in my List of ItemRows it jumps back to the top. Here's the setup: DataService.swift: final class DataService { static let shared = DataService() private init() {} let coreData: CoreData = CoreData() let modelData: ModelData = ModelData() } ModelData.swift: @Observable class ModelData: ObservableObject { var allItems: [ItemDetails] var standardItems: [ItemDetails] var archivedItems: [ItemDetails] init() { allItems = [] standardItems = [] archivedItems = [] } func getInitialData() { // Get all items, then split them into archived and non-archived sets, because you can't use `.filter` in a view... allItems = dataService.coreData.getAllItems() standardItems.append(contentsOf: allItems.filter { !$0.archived }) archivedItems.append(contentsOf: allItems.filter { $0.archived }) } } MainApp.swift: // Get access to the data; this singleton is a global as non-view-based functions, including the `Scene`, need to access the model data let dataService: DataService = DataService.shared @main struct MainApp: App { // Should this be @ObservedObject or @StateObject? @ObservedObject private var modelData: ModelData = dataService.modelData // I would use @StateObject if the line was... //@StateObject private var modelData: ModelData = ModelData() // right? // But then I couldn't use modelData outside of the view hierarchy var body: some Scene { WindowGroup { ZStack { MainView() .environment(modelData) } } .onAppear { modelData.getInitialData() } } } MainView.swift: struct MainView: View { @Environment(ModelData.self) private var modelData: ModelData var body: some View { ... ForEach(modelData.standardItems) { item in ItemRow(item) } ForEach(modelData.archivedItems) { item in ItemRow(item) } } } ItemRow.swift: struct ItemRow: View { @Environment(\.accessibilityDifferentiateWithoutColor) private var accessibilityDifferentiateWithoutColor var item: ItemDetails @State private var showDeleteConfirmation: Bool = false var body: some View { // Construct the row view // `accessibilityDifferentiateWithoutColor` is used within the row to change colours if DWC is enabled, e.g. use different symbols instead of different colours for button images. // Add the .leftSwipeButtons, .rightSwipeButtons, and .contextMenu // Add the .confirmationDialog for when I want to ask for confirmation before deleting an item } } Now, the problems: Swipe an item row, tap one of the buttons, e.g. edit, and the list refreshes and jumps back to the top. In the console I see: ItemRow: @self, @identity, _accessibilityDifferentiateWithoutColor changed. Why did accessibilityDifferentiateWithoutColor change? The setting in Settings > Accessibility > Display & Text Size has not been changed, so why does the row's view think it changed? With a .confirmationDialog attached to the end of the ItemRow (as seen in the code above), if I swipe and tap the delete button the list refreshes and jumps back to the top again. In the console I see: ItemRow: @self, @identity, _accessibilityDifferentiateWithoutColor, _showDeleteConfirmation changed. Right, it changed for the one row that I tapped the button for. Why does every row get redrawn? I already had to shift from using the colorScheme environment variable to add new asset colours with light and dark variants to cover this, but you can't do that with DWC. Honestly, managing state in SwiftUI is a nightmare. I had zero problems until iOS 26 started removing one or two rows when I scrolled, and the fix for that - using @Statebject/@ObservedObject - has introduced multiple further annoying, mind-bending problems, and necessitated massive daily refactorings. And, of course, plenty of my time islost trying to figure out where a problem is in the code because "The compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions"...
7
0
328
Oct ’25
Navigation title in master splitview panel wrong colour with dark mode and hard scrollEdgeEffect
The navigation title color is wrong (dark on dark background) when applying hard scrolledgeeffectstyle. Illustrated by code example. In simulator or physical device. Changing scrollEdgeEffectStyle to soft resolves the issue. As does changing the listStyle. @main struct AppView: App { var body: some Scene { WindowGroup { NavigationSplitView { NavigationStack { Section { NavigationLink(destination: OptionsView()) { Label("More Options", systemImage: "gearshape") } .isDetailLink(false) } header: { Text("WITH OPTIONS").bold() } .navigationTitle("TESTING") } } detail: { Text("DETAIL") } .preferredColorScheme(.dark) } } } struct OptionsView: View { var body: some View { List { Text("List Item") } .listStyle(.plain) .navigationTitle("MORE OPTIONS TITLE") .scrollEdgeEffectStyle(.hard, for: .all) } } Submittted FB20811402 Bug appears in Landscape mode for iPhone only. Any orientation for iPad.
Topic: UI Frameworks SubTopic: SwiftUI
7
0
198
Oct ’25
NSLayoutManager Bug -- layout manager re-laying out overlapping text into the same container.
I've posted a couple times now about major issues I'm having with NSLayoutManager and have written to Apple for code-level support, but no one at Apple has responded to me in more than two weeks. So I'm turning to the community again for any help whatsoever. I'm fairly certain it's a real bug in TextKit. If I'm right about that, I'd love for anyone at Apple to take an interest. And better yet, if I'm wrong (and I hope I am), I'd be incredibly grateful to anyone who can point out where my mistake lies! I've been stuck with this bug for weeks on end. The crux of the issue is that I'm getting what seemed to be totally incompatible results from back to back calls to textContainer(forGlyphAt:effectiveRange:) and lineFragmentRect(forGlyphAt:effectiveRange:withoutAdditionalLayout:)... I'd lay out my text into a fairly tall container of standard page width and then query the layout manager for the text container and line fragment rect for a particular glyph (a glyph that happens to fall after many newlines). Impossibly, the layout manager would report that that glyph was in said very tall container, but that the maxY of its lineFragmentRect was only at 14 points (my NSTextView's isFlipped is true, so that's 14 points measuring from the top down). After investigating, it appears that what is happening under the hood is NSLayoutManager is for some reason laying out text back into the first container in my series of containers, rather than overflowing it into the next container(s) and/or giving me a nil result for textContainer(forGlyphAt:...) I've created a totally stripped down version of my project that recreates this issue reliably and I'm hoping literally anyone at Apple will respond to me. In order to recreate the bug, I've had to build a very specific set of preview data - namely some NSTextStorage content and a unique set of NSTextViews / NSTextContainers. Because of the unique and particular setup required to recreate this bug, the code is too much to paste here (my preview data definition is a little unwieldy but the code that actually processes/parses it is not). I can share the project if anyone is able and willing to look into this with me. It seems I'm not able to share a .zip of the project folder here but am happy to email or share a dropbox link.
7
0
318
May ’25
Migrating SwiftUI lifecycle to UIKit's
Hi, everyone I have an app already in production that uses SwiftUI's lifecycle (paired with an AppDelegate). Due to some specific behaviour of the app, we decided to migrate the app to use UIKit's lifecycle, adding the corresponding SceneDelegate to the app, as well as modifying the Info.plist file accordingly to accommodate to these new changes. Although everything seems to work when installing the app from zero, when installing it on top of another version, the screen goes black and the user cannot interact with the app at all unless they reinstall it completely. As I've read online, iOS is reusing the window configuration from the previous execution of the app. I know this because the AppDelegate's application(application:connectingSceneSession:options) is not being called when coming from a previous version of the app. I would love to know what can I do to make this work because, as you may understand, we cannot ask our user base to reinstall the application. Thank you very much.
7
2
1.9k
Apr ’25
.disabled() doesn't VISUALLY disable buttons inside ToolbarItem on iOS 26 devices
[Also submitted as FB19313064] The .disabled() modifier doesn't visually disable buttons inside a ToolbarItem container on iOS 26.0 (23A5297i) devices. The button looks enabled, but tapping it doesn't trigger the action. When deployment target is lowered to iOS 18 and deployed to an iOS 18 device, it works correctly. It still fails on an iOS 26 device, even with an iOS 18-targeted build. This occurs in both the Simulator and on a physical device. Screen Recording Code struct ContentView: View { @State private var isButtonDisabled = false private var osTitle: String { let version = ProcessInfo.processInfo.operatingSystemVersion return "iOS \(version.majorVersion)" } var body: some View { NavigationStack { VStack { Button("Body Button") { print("Body button tapped") } .buttonStyle(.borderedProminent) .disabled(isButtonDisabled) Toggle("Disable buttons", isOn: $isButtonDisabled) Spacer() } .padding() .navigationTitle("Device: \(osTitle)") .navigationBarTitleDisplayMode(.large) .toolbar { ToolbarItem { Button("Toolbar") { print("Toolbar button tapped") } .buttonStyle(.borderedProminent) .disabled(isButtonDisabled) } } } } }
7
3
480
Oct ’25
SwiftUI TextField corrupts selection when inserting utf16
The example code below shows what I am trying to achieve: When the user types a '*', it should be replaced with a '×'. It looks like it works, but the cursor position is corrupted, even though it looks OK, and the diagnostics that is printed below shows a valid index. If you type "12*34" you get "12×43" because the cursor is inserting before the shown cursor instead of after. How can I fix this? struct ContentView: View { @State private var input: String = "" @State private var selection: TextSelection? = nil var body: some View { VStack { TextField("Type 12*34", text: $input, selection: $selection) .onKeyPress(action: {keyPress in handleKeyPress(keyPress) }) Text("Selection: \(selectionAsString())") }.padding() } func handleKeyPress(_ keyPress: KeyPress) -> KeyPress.Result { if (keyPress.key.character == "*") { insertAtCursor(text: "×") moveCursor(offset: 1) return KeyPress.Result.handled } return KeyPress.Result.ignored } func moveCursor(offset: Int) { guard let selection else { return } if case let .selection(range) = selection.indices { print("Moving cursor from \(range.lowerBound)") let newIndex = input.index(range.lowerBound, offsetBy: offset, limitedBy: input.endIndex)! let newSelection : TextSelection.Indices = .selection(newIndex..<newIndex) if case let .selection(range) = newSelection { print("Moved to \(range.lowerBound)") } self.selection!.indices = newSelection } } func insertAtCursor(text: String) { guard let selection else { return } if case let .selection(range) = selection.indices { input.insert(contentsOf: text, at: range.lowerBound) } } func selectionAsString() -> String { guard let selection else { return "None" } switch selection.indices { case .selection(let range): if (range.lowerBound == range.upperBound) { return ("No selection, cursor at \(range.lowerBound)") } let lower = range.lowerBound.utf16Offset(in: input) let upper = range.upperBound.utf16Offset(in: input) return "\(lower) - \(upper)" case .multiSelection(let rangeSet): return "Multi selection \(rangeSet)" @unknown default: fatalError("Unknown selection") } } }
Topic: UI Frameworks SubTopic: SwiftUI
7
0
171
Sep ’25
'tabViewBottomAccessory' leaves an empty accessory area when conditionally hidden
We use SwiftUI's .tabViewBottomAccessory in our iOS apps for displaying an Audio MiniPlayer View (like in the Apple Music App). TabView(selection: $viewModel.selectedTab) { // Tabs here } .tabViewBottomAccessory { if viewModel.showAudioMiniPlayer { MiniPlayerView() } } The Problem This code works perfectly on iOS 26.0. When viewModel.showAudioMiniPlayer is false, the accessory is completely hidden. However, on iOS 26.1 (23B5059e), when 'viewModel.showAudioMiniPlayer' becomes false, the MiniPlayerView disappears, but an empty container remains, leaving a blank space above the tab bar. Is this a known Bug in iOS 26.1 and are there any effective workarounds?
Topic: UI Frameworks SubTopic: SwiftUI
Replies
8
Boosts
5
Views
941
Activity
Nov ’25
A basic DocumentGroup App presents two back "<" buttons on the navigation bar.
Please has anyone found a workaround for duplicate back buttons appearing on the toolbar of a ContentView launched from a DocumentGroup? The problem occurs with Xcode 14.0 running a basic DocumentGroup App on iOS 16.0. To reproduce, simply build a new project using the "Document App" template. Build and run in the iOS/iPadOS simulator or on an iOS/iPadOS device. Two back buttons appear. Only one functions. I've not found a way to eliminate the dud. This problem has occurred throughout the Xcode 14.0 beta program.
Replies
8
Boosts
6
Views
2k
Activity
May ’25
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.
Replies
8
Boosts
2
Views
2.5k
Activity
May ’25
Looking for advice on app architecture
I have an existing Mac app which has evolved through over twenty years of development. It is currently written in a mixture of C++ and Objective-C, with a bit of C and Swift in a few places. For a few years now, I have been tinkering with replacing the UI with SwiftUI. The model has been completely rewritten in Swift and works fine. After a few tries, no version has been working acceptably, so I'm thinking that I need to rethink the architecture. The UI consists of a window with a master-detail view. The detail view is what users spend most of their time with. It contains a lot of subviews, around 100 typically. Keyboard events affect the display, so I've had a dedicated data structure to hold the state that is needed for displaying all the subviews. Using Instruments, I see that the view seems to recreate the subviews three times per keyboard event, so I'm clearly doing something wrong. A second factor is that there are a couple of dozen commands that are applicable to the detail view, driven either by menu items, keyboard shortcut, or toolbar button. Adding all of those to the view makes for a massive SwiftUI View, which seems unlikely to be good practice. The current implementation has the controller class broken up with categories, but still they are big classes. Most SwiftUI stuff on the web is iOS-oriented, and typically has a focus on fairly simple apps, so the whole topic of dealing with menu commands doesn't get a lot of coverage, so I've been doing all that through my own solutions, which are probably nothing like optimal. What I've been able to find is not particularly helpful for a full-fledged application like mine, so I'm looking for advice on how to structure the app. The existing one is largely MVC, but I've tried a similar approach and a shot at MVVM, but I'm not getting good results so far. So, pointers, places I can read more, or samples of real-world apps is what I'm after. Anyone?
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Replies
8
Boosts
0
Views
172
Activity
Sep ’25
macOS 26 toolbar has wrong tint color sometimes in Dark Appearance
I have a SwiftUI Mac Catalyst app. I create a toolbar like this NavigationSplitView(columnVisibility: $sceneModel.columnVisibility, preferredCompactColumn: $preferredColumn) { sidebarView() } detail: { contentView() .toolbar { ToolbarItemGroup(placement: .topBarTrailing) { HStack { Button { sceneModel.onMaps(sender: self) } label: { Image(systemName: "map") .font(.title2) } Button { sceneModel.onSearch(sender: self) } label: { Image(systemName: "magnifyingglass") .font(.title2) } ... } } } } When my Mac Appearance is set to dark mode and the content under the toolbar is dark the toolbar looks good like this. But then if I have light content under the toolbar, the glass effect changes to light, but the tint on the icons stays white instead of changing to black and it is hard to see the icon. It looks like this. When I set the Appearance on my Mac to light, then the toolbar works just fine on both dark and light colored backgrounds. Does anyone know how I can fix this when the appearance is Dark?
Replies
8
Boosts
0
Views
299
Activity
Sep ’25
Trying to use UIScrollEdgeElementContainerInteraction
I have a UIKit app with a custom navigation controller. I want my view title to go up into the navigation bar when the user scrolls down the screen. It looks like UIScrollEdgeElementContainerInteraction should do what I want, but I am having trouble using it. Below is a sample, where a header view represents a title. I added the interaction to the header view, but it seems to have no effect. Am I missing a step? Perhaps I misunderstand what this is supposed to do, or perhaps I do not understand the preconditions to make this work. I am hoping someone can tell me what I am doing wrong, or point me to some working sample code. Thank you. John class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { var headerView: UIVisualEffectView! var tableView: UITableView! var interaction: UIScrollEdgeElementContainerInteraction! override func viewDidLoad() { super.viewDidLoad() self.tableView = UITableView() self.tableView.translatesAutoresizingMaskIntoConstraints = false self.tableView.topEdgeEffect.style = .soft self.tableView.delegate = self self.tableView.dataSource = self self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell") self.view.addSubview(self.tableView) self.view.addConstraints([ self.tableView.topAnchor.constraint(equalTo: self.view.topAnchor), self.tableView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor), self.tableView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor), self.tableView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor), ]) self.headerView = UIVisualEffectView(effect: UIGlassEffect(style: .regular)) self.headerView.translatesAutoresizingMaskIntoConstraints = false self.headerView.backgroundColor = .green self.view.addSubview(self.headerView) self.view.addConstraints([ self.headerView.topAnchor.constraint(equalTo: self.view.topAnchor), self.headerView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor), self.headerView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor), self.headerView.heightAnchor.constraint(equalToConstant: 100.0), ]) let label = UILabel() label.translatesAutoresizingMaskIntoConstraints = false label.text = "my text" self.headerView.contentView.addSubview(label) self.headerView.contentView.addConstraints([ label.centerXAnchor.constraint(equalTo: self.headerView.contentView.centerXAnchor), label.centerYAnchor.constraint(equalTo: self.headerView.contentView.centerYAnchor), ]) self.interaction = UIScrollEdgeElementContainerInteraction() self.interaction.scrollView = self.tableView self.interaction.edge = .top self.headerView.addInteraction(self.interaction) } func numberOfSections(in tableView: UITableView) -> Int { return 1 } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 100 } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = self.tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) cell.textLabel?.text = "row \(indexPath.row + 1)" return cell } }
Topic: UI Frameworks SubTopic: UIKit Tags:
Replies
8
Boosts
0
Views
370
Activity
Oct ’25
NSCollectionLayoutBoundarySupplementaryItem background blur covering the entire layout section
My app has the following UI layout: NSSplitViewController as the windows contentViewController NSPageController in the content (right) split item NSTabViewController as the root items of the NSPageController NSViewController with a collection view in the first tab of that NSTabViewController The collection view is using a NSCollectionViewCompositionalLayout in which the sections are set up to have a header using NSCollectionLayoutBoundarySupplementaryItem with pinToVisibleBounds=true and alignment=top With macOS 26, the pinned supplementary item automatically gets a blurred/semi-transparent background that seamlessly integrates with the toolbar. When the window's title bar has a NSTitlebarAccessoryViewController added, the said semi-transparent background gets a bottom hard edge and a hairline to provide more visual separation from the main content. During runtime, my NSPageController transitions from the NSTabViewController to another view controller. When transitioning back, the semi-transparent blur bleeds into the entire section. This happens no matter if there's a NSTitlebarAccessoryViewController added or not. It doesn't happen 100% of the cases, it seems to depend on section size, header visibility and/or scroll position. But it happens more often than not. Most of the time, a second or so after the back transition - shortly after pageControllerDidEndLiveTransition: of the NSPageControllerDelegate is called - the view updates and the supplementary views are back to normal. Sometimes, the issue also appears not when transitioning using NSPageController, but simply by scrolling through the collection view. Anyone has an idea what is happening here? Below are two screenshots of both the "ok" and "not ok" state I'm on macOS 26.0.1 and I'm using XCode 26.0.1
Topic: UI Frameworks SubTopic: AppKit Tags:
Replies
8
Boosts
0
Views
417
Activity
Nov ’25
SwiftData & CloudKit: Arrays of Codable Structs Causing NSKeyedUnarchiveFromData Error
I have SwiftData models containing arrays of Codable structs that worked fine before adding CloudKit capability. I believe they are the reason I started seeing errors after enabling CloudKit. Example model: @Model final class ProtocolMedication { var times: [SchedulingTime] = [] // SchedulingTime is Codable // other properties... } After enabling CloudKit, I get this error logged to the console: 'NSKeyedUnarchiveFromData' should not be used to for un-archiving and will be removed in a future release CloudKit Console shows this times data as "plain text" instead of "bplist" format. Other struct/enum properties display correctly (I think) as "bplist" in CloudKit Console. The local SwiftData storage handled these arrays fine - this issue only appeared with CloudKit integration. What's the recommended approach for storing arrays of Codable structs in SwiftData models that sync with CloudKit?
Replies
8
Boosts
1
Views
931
Activity
Jan ’26
tvOS 18.0 Siri back button behavior bug
On testing my app with tvOS 18, I have noticed the Siri Remote back button no longer provides system-provided behavior when interacting with tab bar controller pages. Instead of moving focus back to the tab bar when pressed, the back button will close the app, as if the Home button was pressed. This occurs both on device and in the Simulator. Create tvOS project with a tab bar controller. Create pages/tabs which contain focusable items (ie. buttons) Scroll down to any focusable item (ie. a button or UICollectionView cell) Hit the Siri Remote back button. See expect behavior below: Expected behavior: System-provided behavior should move focus back to the tab bar at the top of the screen. Actual results: App is closed and user is taken back to the Home Screen. Has anyone else noticed this behavior?
Replies
8
Boosts
3
Views
1.3k
Activity
Aug ’25
SwiftUI TextField with optional value working for @State but not @Binding properties
I've encountered a potential bug where a TextField connected to an optional value (not a string) works as expected when bound to a @State property, but won't update a @Binding property. Here is some example code import SwiftUI struct ContentView: View { @Binding var boundValue: Double? @State private var stateValue: Double? = 55 var body: some View { TextField("Bound value", value: $boundValue, format: .number) Text("\(boundValue ?? .nan)") TextField("State value", value: $stateValue, format: .number) Text("\(stateValue ?? .nan)") } } #Preview { ContentView(boundValue: .constant(42.00)) } It's as though the optional value stored externally is preventing the value updating. Can anyone confirm whether this is intentional, or a bug? This is in Xcode 26b6, on macOS Tahoe 26b8, but from this query it looks like the problem has existed for years.
Topic: UI Frameworks SubTopic: SwiftUI
Replies
8
Boosts
0
Views
381
Activity
Aug ’25
Keep ScrollView position when adding items on the top
I am using a LayzVStack embedded into a ScrollView. The list items are fetched from a core data store by using a @FetchResult or I tested it also with the new @Query command coming with SwiftData. The list has one hundred items 1, 2, 3, ..., 100. The user scrolled the ScrollView so that items 50, 51, ... 60 are visible on screen. Now new data will be fetched from the server and updates the CoreData or SwiftData model. When I add new items to the end of the list (e.g 101, 102, 103, ...) then the ScrollView is keeping its position. Opposite to this when I add new items to the top (0, -1, -2, -3, ...) then the ScrollView scrolls down. Is there a way with the new SwiftData and SwiftUI ScrollView modifiers to update my list model without scrolling like with UIKit where you can query and set the scroll offset pixel wise?
Replies
8
Boosts
1
Views
9.9k
Activity
May ’25
Accessibility Permission In Sandbox For Keyboard
Hello! My question is about 1) if we can use any and or all accessibility features within a sandboxed app and 2) what steps we need to take to do so. Using accessibility permissions, my app was working fine in Xcode. It used NSEvent.addGlobalMonitorForEvents and localMoniter, along with CGEvent.tapCreate. However, after downloading the same app from the App Store, the code was not working. I believe this was due to differences in how permissions for accessibility are managed in Xcode compared to production. Is it possible for my app to get access to all accessibility features, while being distributed on the App Store though? Do I need to add / request any special entitlements like com.apple.security.accessibility? Thanks so much for the help. I have done a lot of research on this online but found some conflicting information, so wanted to post here for a clear answer.
Replies
8
Boosts
0
Views
515
Activity
Oct ’25
Swipe to go back still broken with Zoom navigation transition.
When you use .navigationTransition(.zoom(sourceID: "placeholder", in: placehoder)) for navigation animation, going back using the swipe gesture is still very buggy on IOS26. I know it has been mentioned in other places like here: https://developer.apple.com/forums/thread/796805?answerId=856846022#856846022 but nothing seems to have been done to fix this issue. Here is a video showing the bug comparing when the back button is used vs swipe to go back: https://imgur.com/a/JgEusRH I wish there was a way to at least disable the swipe back gesture until this bug is fixed.
Replies
8
Boosts
2
Views
593
Activity
Feb ’26
macOS SwiftUI Sheets are no longer resizable (Xcode 16 beta2)
For whatever reason SwiftUI sheets don't seem to be resizable anymore. The exact same code/project produces resizable Sheets in XCode 15.4 but unresizable ones with Swift included in Xcode 16 beta 2. Tried explicitly providing .fixedSize(horizontal false, vertical: false) everywhere humanly possible hoping for a fix but sheets are still stuck at an awkward size (turns out be the minWidth/minHeight if I provide in .frame).
Replies
7
Boosts
1
Views
2.7k
Activity
Jan ’26
SwiftUI state is maddening
I honestly thought I was getting somewhere with this, but alas, no. Every time I do anything in my List of ItemRows it jumps back to the top. Here's the setup: DataService.swift: final class DataService { static let shared = DataService() private init() {} let coreData: CoreData = CoreData() let modelData: ModelData = ModelData() } ModelData.swift: @Observable class ModelData: ObservableObject { var allItems: [ItemDetails] var standardItems: [ItemDetails] var archivedItems: [ItemDetails] init() { allItems = [] standardItems = [] archivedItems = [] } func getInitialData() { // Get all items, then split them into archived and non-archived sets, because you can't use `.filter` in a view... allItems = dataService.coreData.getAllItems() standardItems.append(contentsOf: allItems.filter { !$0.archived }) archivedItems.append(contentsOf: allItems.filter { $0.archived }) } } MainApp.swift: // Get access to the data; this singleton is a global as non-view-based functions, including the `Scene`, need to access the model data let dataService: DataService = DataService.shared @main struct MainApp: App { // Should this be @ObservedObject or @StateObject? @ObservedObject private var modelData: ModelData = dataService.modelData // I would use @StateObject if the line was... //@StateObject private var modelData: ModelData = ModelData() // right? // But then I couldn't use modelData outside of the view hierarchy var body: some Scene { WindowGroup { ZStack { MainView() .environment(modelData) } } .onAppear { modelData.getInitialData() } } } MainView.swift: struct MainView: View { @Environment(ModelData.self) private var modelData: ModelData var body: some View { ... ForEach(modelData.standardItems) { item in ItemRow(item) } ForEach(modelData.archivedItems) { item in ItemRow(item) } } } ItemRow.swift: struct ItemRow: View { @Environment(\.accessibilityDifferentiateWithoutColor) private var accessibilityDifferentiateWithoutColor var item: ItemDetails @State private var showDeleteConfirmation: Bool = false var body: some View { // Construct the row view // `accessibilityDifferentiateWithoutColor` is used within the row to change colours if DWC is enabled, e.g. use different symbols instead of different colours for button images. // Add the .leftSwipeButtons, .rightSwipeButtons, and .contextMenu // Add the .confirmationDialog for when I want to ask for confirmation before deleting an item } } Now, the problems: Swipe an item row, tap one of the buttons, e.g. edit, and the list refreshes and jumps back to the top. In the console I see: ItemRow: @self, @identity, _accessibilityDifferentiateWithoutColor changed. Why did accessibilityDifferentiateWithoutColor change? The setting in Settings > Accessibility > Display & Text Size has not been changed, so why does the row's view think it changed? With a .confirmationDialog attached to the end of the ItemRow (as seen in the code above), if I swipe and tap the delete button the list refreshes and jumps back to the top again. In the console I see: ItemRow: @self, @identity, _accessibilityDifferentiateWithoutColor, _showDeleteConfirmation changed. Right, it changed for the one row that I tapped the button for. Why does every row get redrawn? I already had to shift from using the colorScheme environment variable to add new asset colours with light and dark variants to cover this, but you can't do that with DWC. Honestly, managing state in SwiftUI is a nightmare. I had zero problems until iOS 26 started removing one or two rows when I scrolled, and the fix for that - using @Statebject/@ObservedObject - has introduced multiple further annoying, mind-bending problems, and necessitated massive daily refactorings. And, of course, plenty of my time islost trying to figure out where a problem is in the code because "The compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions"...
Replies
7
Boosts
0
Views
328
Activity
Oct ’25
Navigation title in master splitview panel wrong colour with dark mode and hard scrollEdgeEffect
The navigation title color is wrong (dark on dark background) when applying hard scrolledgeeffectstyle. Illustrated by code example. In simulator or physical device. Changing scrollEdgeEffectStyle to soft resolves the issue. As does changing the listStyle. @main struct AppView: App { var body: some Scene { WindowGroup { NavigationSplitView { NavigationStack { Section { NavigationLink(destination: OptionsView()) { Label("More Options", systemImage: "gearshape") } .isDetailLink(false) } header: { Text("WITH OPTIONS").bold() } .navigationTitle("TESTING") } } detail: { Text("DETAIL") } .preferredColorScheme(.dark) } } } struct OptionsView: View { var body: some View { List { Text("List Item") } .listStyle(.plain) .navigationTitle("MORE OPTIONS TITLE") .scrollEdgeEffectStyle(.hard, for: .all) } } Submittted FB20811402 Bug appears in Landscape mode for iPhone only. Any orientation for iPad.
Topic: UI Frameworks SubTopic: SwiftUI
Replies
7
Boosts
0
Views
198
Activity
Oct ’25
NSLayoutManager Bug -- layout manager re-laying out overlapping text into the same container.
I've posted a couple times now about major issues I'm having with NSLayoutManager and have written to Apple for code-level support, but no one at Apple has responded to me in more than two weeks. So I'm turning to the community again for any help whatsoever. I'm fairly certain it's a real bug in TextKit. If I'm right about that, I'd love for anyone at Apple to take an interest. And better yet, if I'm wrong (and I hope I am), I'd be incredibly grateful to anyone who can point out where my mistake lies! I've been stuck with this bug for weeks on end. The crux of the issue is that I'm getting what seemed to be totally incompatible results from back to back calls to textContainer(forGlyphAt:effectiveRange:) and lineFragmentRect(forGlyphAt:effectiveRange:withoutAdditionalLayout:)... I'd lay out my text into a fairly tall container of standard page width and then query the layout manager for the text container and line fragment rect for a particular glyph (a glyph that happens to fall after many newlines). Impossibly, the layout manager would report that that glyph was in said very tall container, but that the maxY of its lineFragmentRect was only at 14 points (my NSTextView's isFlipped is true, so that's 14 points measuring from the top down). After investigating, it appears that what is happening under the hood is NSLayoutManager is for some reason laying out text back into the first container in my series of containers, rather than overflowing it into the next container(s) and/or giving me a nil result for textContainer(forGlyphAt:...) I've created a totally stripped down version of my project that recreates this issue reliably and I'm hoping literally anyone at Apple will respond to me. In order to recreate the bug, I've had to build a very specific set of preview data - namely some NSTextStorage content and a unique set of NSTextViews / NSTextContainers. Because of the unique and particular setup required to recreate this bug, the code is too much to paste here (my preview data definition is a little unwieldy but the code that actually processes/parses it is not). I can share the project if anyone is able and willing to look into this with me. It seems I'm not able to share a .zip of the project folder here but am happy to email or share a dropbox link.
Replies
7
Boosts
0
Views
318
Activity
May ’25
Migrating SwiftUI lifecycle to UIKit's
Hi, everyone I have an app already in production that uses SwiftUI's lifecycle (paired with an AppDelegate). Due to some specific behaviour of the app, we decided to migrate the app to use UIKit's lifecycle, adding the corresponding SceneDelegate to the app, as well as modifying the Info.plist file accordingly to accommodate to these new changes. Although everything seems to work when installing the app from zero, when installing it on top of another version, the screen goes black and the user cannot interact with the app at all unless they reinstall it completely. As I've read online, iOS is reusing the window configuration from the previous execution of the app. I know this because the AppDelegate's application(application:connectingSceneSession:options) is not being called when coming from a previous version of the app. I would love to know what can I do to make this work because, as you may understand, we cannot ask our user base to reinstall the application. Thank you very much.
Replies
7
Boosts
2
Views
1.9k
Activity
Apr ’25
.disabled() doesn't VISUALLY disable buttons inside ToolbarItem on iOS 26 devices
[Also submitted as FB19313064] The .disabled() modifier doesn't visually disable buttons inside a ToolbarItem container on iOS 26.0 (23A5297i) devices. The button looks enabled, but tapping it doesn't trigger the action. When deployment target is lowered to iOS 18 and deployed to an iOS 18 device, it works correctly. It still fails on an iOS 26 device, even with an iOS 18-targeted build. This occurs in both the Simulator and on a physical device. Screen Recording Code struct ContentView: View { @State private var isButtonDisabled = false private var osTitle: String { let version = ProcessInfo.processInfo.operatingSystemVersion return "iOS \(version.majorVersion)" } var body: some View { NavigationStack { VStack { Button("Body Button") { print("Body button tapped") } .buttonStyle(.borderedProminent) .disabled(isButtonDisabled) Toggle("Disable buttons", isOn: $isButtonDisabled) Spacer() } .padding() .navigationTitle("Device: \(osTitle)") .navigationBarTitleDisplayMode(.large) .toolbar { ToolbarItem { Button("Toolbar") { print("Toolbar button tapped") } .buttonStyle(.borderedProminent) .disabled(isButtonDisabled) } } } } }
Replies
7
Boosts
3
Views
480
Activity
Oct ’25
SwiftUI TextField corrupts selection when inserting utf16
The example code below shows what I am trying to achieve: When the user types a '*', it should be replaced with a '×'. It looks like it works, but the cursor position is corrupted, even though it looks OK, and the diagnostics that is printed below shows a valid index. If you type "12*34" you get "12×43" because the cursor is inserting before the shown cursor instead of after. How can I fix this? struct ContentView: View { @State private var input: String = "" @State private var selection: TextSelection? = nil var body: some View { VStack { TextField("Type 12*34", text: $input, selection: $selection) .onKeyPress(action: {keyPress in handleKeyPress(keyPress) }) Text("Selection: \(selectionAsString())") }.padding() } func handleKeyPress(_ keyPress: KeyPress) -> KeyPress.Result { if (keyPress.key.character == "*") { insertAtCursor(text: "×") moveCursor(offset: 1) return KeyPress.Result.handled } return KeyPress.Result.ignored } func moveCursor(offset: Int) { guard let selection else { return } if case let .selection(range) = selection.indices { print("Moving cursor from \(range.lowerBound)") let newIndex = input.index(range.lowerBound, offsetBy: offset, limitedBy: input.endIndex)! let newSelection : TextSelection.Indices = .selection(newIndex..<newIndex) if case let .selection(range) = newSelection { print("Moved to \(range.lowerBound)") } self.selection!.indices = newSelection } } func insertAtCursor(text: String) { guard let selection else { return } if case let .selection(range) = selection.indices { input.insert(contentsOf: text, at: range.lowerBound) } } func selectionAsString() -> String { guard let selection else { return "None" } switch selection.indices { case .selection(let range): if (range.lowerBound == range.upperBound) { return ("No selection, cursor at \(range.lowerBound)") } let lower = range.lowerBound.utf16Offset(in: input) let upper = range.upperBound.utf16Offset(in: input) return "\(lower) - \(upper)" case .multiSelection(let rangeSet): return "Multi selection \(rangeSet)" @unknown default: fatalError("Unknown selection") } } }
Topic: UI Frameworks SubTopic: SwiftUI
Replies
7
Boosts
0
Views
171
Activity
Sep ’25