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

Critical Issue in iOS 18 Beta: UITabBarController Child View Controller Incorrectly Added as UITabBarItem, Leading to Application Crash
I am writing to report an issue I encountered with iOS 18 beta that affects my application, which has been available on the App Store for over two years and currently has over 60,000 active users. My application utilizes a UITabBarController to manage multiple tabs, where each tab hosts a UIViewController embedded within a UINavigationController. The application operates in two different states, where users may have either 5, 4, or 3 tabBarItems depending on their configuration. The issue arises when fewer than 5 tabs are present. In these cases, I add child view controllers to the UITabBarController to ensure they are displayed above the tab bar, rather than below it. The relevant code snippet is as follows: tabBarController.addChild(childController) tabBarController.view.addSubview(childController.view) Prior to iOS 18, this implementation functioned as expected. However, with the release of iOS 18, adding a child view controller to the UITabBarController results in the child being incorrectly added as a UITabBarItem. This misbehavior leads to an application crash when the unintended tab is selected. The crash trace is as follows: "Inconsistency in UITabBar items and view controllers detected. No view controller matches the UITabBarItem '<UITabBarItem: 0x142d9c480> selected'." I have attached screenshots from iOS 18 and previous versions to illustrate the issue, which compares the expected behavior in earlier iOS versions with the problematic behavior in iOS 18. I appreciate your attention to this matter and look forward to any guidance or resolution you can provide.
6
0
2.4k
Dec ’24
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
6
0
82
Jun ’25
SwiftUI View cannot conform custom Equatable protocol in Swift 6.
In Swift 6, stricter concurrency rules can lead to challenges when making SwiftUI views conform to Equatable. Specifically, the == operator required for Equatable must be nonisolated, which means it cannot access @MainActor-isolated properties. This creates an error when trying to compare views with such properties: Error Example: struct MyView: View, Equatable { let title: String let count: Int static func ==(lhs: MyView, rhs: MyView) -> Bool { // Accessing `title` here would trigger an error due to actor isolation. return lhs.count == rhs.count } var body: some View { Text(title) } } Error Message: Main actor-isolated operator function '==' cannot be used to satisfy nonisolated protocol requirement; this is an error in the Swift 6 language mode. Any suggestions? Thanks FB: FB15753655 (SwiftUI View cannot conform custom Equatable protocol in Swift 6.)
6
0
933
Mar ’25
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.1k
Mar ’25
perspectiveTransform causing large memory spike / app being killed
I have a PDF which contains geocoordinates. I'm extracting out that image with the following code (this is for an iOS application): guard let cgDocument = CGPDFDocument(overlay.pdfUrl as CFURL) else { return } guard let cgPage = cgDocument.page(at: 1) else { return } var boundingRect = self.rect(for: overlay.boundingMapRect) let pdfPageRect = cgPage.getBoxRect(.mediaBox) let renderer = UIGraphicsImageRenderer(size: pdfPageRect.size) var img = renderer.image { ctx in UIColor.white.set() ctx.fill(pdfPageRect) ctx.cgContext.translateBy(x: 0.0, y: pdfPageRect.size.height) ctx.cgContext.scaleBy(x: 1.0, y: -1.0) ctx.cgContext.drawPDFPage(cgPage) } Once I have that image, I then need to adjust it to fit the specific coordinate corners. For that, I'm doing the following using a perspectiveTransform: let ciImg = CIImage(image: img)! let perspectiveTransformFilter = CIFilter.perspectiveTransform() perspectiveTransformFilter.inputImage = ciImg perspectiveTransformFilter.topRight = cartesianForPoint(point: ur, extent: boundingRect) perspectiveTransformFilter.topLeft = cartesianForPoint(point: ul, extent: boundingRect) perspectiveTransformFilter.bottomRight = cartesianForPoint(point: lr, extent: boundingRect) perspectiveTransformFilter.bottomLeft = cartesianForPoint(point: ll, extent: boundingRect) let txImg = perspectiveTransformFilter.outputImage! img = UIImage(ciImage: txImg) The original image is 792 x 612 (a landscape PDF) but the boundingRect covering the coordinates is 25625 x 20030. Obviously when I try to draw the image into that bounding box the app runs out of memory (25625 x 20030 x 4 is around 2GB of memory). What I'm struggling with is - how do I correctly scale this image to fit into the bounding box even though the bounding box is, oh, 10x the resolution of the actual device? There's some key CoreGraphics thing I'm sure I'm missing here.
6
0
147
1w
Issue with UIActivityViewController Not Showing X (formerly Twitter) and Facebook Icons When Sharing Images on iOS 26 Beta
I have installed the iOS 26 Beta on my device and conducted a comprehensive functionality test of my iOS application, which I designed and developed. The application includes a feature that allows users to share images directly to X (formerly Twitter) and Facebook. During testing, I encountered an issue where the icons for X (formerly Twitter) and Facebook do not appear in the share dialog, despite both apps being installed on the device. This issue prevents users from sharing images to these platforms directly from the app. Steps to Reproduce: 1.Install iOS 26 Beta on a compatible device. 2.Ensure that both the X (formerly Twitter) and Facebook apps are installed and logged in on the device. 3.Open the iOS application and navigate to the image sharing feature. 4.Attempt to share an image using the share dialog. 5.Observe that the icons for X (formerly Twitter) and Facebook are missing from the share options. Expected Behavior: The share dialog should display icons for X (formerly Twitter) and Facebook, allowing users to share images directly to these platforms. Actual Behavior: The icons for X (formerly Twitter) and Facebook do not appear in the share dialog, preventing direct sharing to these platforms. Code Implementation: I have not implemented any code to exclude X (formerly Twitter) and Facebook from the share options. Below is the relevant code for controlling the share screen: let activityViewController = UIActivityViewController(activityItems: activityItems, applicationActivities: applicationActivities) let excludedTypes = [ UIActivity.ActivityType.assignToContact, UIActivity.ActivityType.print, ] activityViewController.excludedActivityTypes = excludedTypes activityViewController.completionWithItemsHandler = completion self.present(activityViewController, animated: true, completion: nil) As shown in the implementation, there is no exclusion of X (formerly Twitter) and Facebook, yet their icons do not appear in the share dialog.
6
4
507
2w
Swift Charts: How to prevent scroll position jump when loading more data dynamically
I'm implementing infinite scrolling with Swift Charts where additional historical data loads when scrolling near the beginning of the dataset. However, when new data is loaded, the chart's scroll position jumps unexpectedly. Current behavior: Initially loads 10 data points, displaying the latest 5 When scrolling backwards with only 3 points remaining off-screen, triggers loading of 10 more historical points After loading, the scroll position jumps to the 3rd position of the new dataset instead of maintaining the current view Expected behavior: Scroll position should remain stable when new data is loaded User's current view should not change during data loading Here's my implementation logic using some mock data: import SwiftUI import Charts struct DataPoint: Identifiable { let id = UUID() let date: Date let value: Double } class ChartViewModel: ObservableObject { @Published var dataPoints: [DataPoint] = [] private var isLoading = false init() { loadMoreData() } func loadMoreData() { guard !isLoading else { return } isLoading = true let newData = self.generateDataPoints( endDate: self.dataPoints.first?.date ?? Date(), count: 10 ) self.dataPoints.insert(contentsOf: newData, at: 0) self.isLoading = false print("\(dataPoints.count) data points.") } private func generateDataPoints(endDate: Date, count: Int) -> [DataPoint] { var points: [DataPoint] = [] let calendar = Calendar.current for i in 0..<count { let date = calendar.date( byAdding: .day, value: -i, to: endDate ) ?? endDate let value = Double.random(in: 0...100) points.append(DataPoint(date: date, value: value)) } return points.sorted { $0.date < $1.date } } } struct ScrollableChart: View { @StateObject private var viewModel = ChartViewModel() @State private var scrollPosition: Date @State private var scrollDebounceTask: Task<Void, Never>? init() { self.scrollPosition = .now.addingTimeInterval(-4*24*3600) } var body: some View { Chart(viewModel.dataPoints) { point in BarMark( x: .value("Time", point.date, unit: .day), y: .value("Value", point.value) ) } .chartScrollableAxes(.horizontal) .chartXVisibleDomain(length: 5 * 24 * 3600) .chartScrollPosition(x: $scrollPosition) .chartXScale(domain: .automatic(includesZero: false)) .frame(height: 300) .onChange(of: scrollPosition) { oldPosition, newPosition in scrollDebounceTask?.cancel() scrollDebounceTask = Task { try? await Task.sleep(for: .milliseconds(300)) if !Task.isCancelled { checkAndLoadMoreData(currentPosition: newPosition) } } } } private func checkAndLoadMoreData(currentPosition: Date?) { guard let currentPosition, let earliestDataPoint = viewModel.dataPoints.first?.date else { return } let timeInterval = currentPosition.timeIntervalSince(earliestDataPoint) if timeInterval <= 3 * 24 * 3600 { viewModel.loadMoreData() } } } I attempted to compensate for this jump by adding: scrollPosition = scrollPosition.addingTimeInterval(10 * 24 * 3600) after viewModel.loadMoreData(). However, this caused the chart to jump in the opposite direction by 10 days, rather than maintaining the current position. What's the problem with my code and how to fix it?
6
0
458
Jun ’25
A specific image fails to load using UIImageView on iOS 16, but loads normally on systems below iOS 16.
A specific image fails to load properly using UIImageView on iOS 16 and later systems, but loads normally on iOS 15 and earlier versions. Similarly, on Mac computers, this image cannot be opened on MacOS 13 and later, whereas it opens without issue on MacOS 12 and earlier. I am curious about the reasons behind this differing behavior on both iPhone and Mac.
6
0
440
Feb ’25
Why first View on my NavigationStack appears again when I switch branch?
Hello, In my app, I have an onboarding made of multiple steps in a NavigationStack. I also have a state variable that controls an if else root branch to show either the onboarding NavigationStack or the app content if the onboarding is finished. I noticed that when I end the onboarding (i.e. I switch to the other part of the if else root branch), the onAppear of the first View in the NavigationStack of the onboarding is called again. I don’t understand why. Is this a bug? Thanks, Axel enum Step { case one case two case three case four } struct ContentView: View { @State private var isFinished: Bool = false @State private var steps: [Step] = [] var body: some View { if isFinished { Button("Restart") { steps = [] isFinished = false } } else { NavigationStack(path: $steps) { VStack { Text("Start") .onAppear { print("onAppear: start") } Button("Go to step 1") { steps.append(.one) } } .navigationDestination(for: Step.self) { step in switch step { case .one: Button("Go to step 2") { steps.append(.two) } .onAppear { print("onAppear: step 1") } case .two: Button("Go to step 3") { steps.append(.three) } .onAppear { print("onAppear: step 2") } case .three: Button("Go to step 4") { steps.append(.four) } .onAppear { print("onAppear: step 3") } case .four: Button("End") { isFinished = true } .onAppear { print("onAppear: end") } } } } .onAppear { print("onAppear: NavigationStack") } } } }
6
0
742
Dec ’24
Should SwiftUI view models in Swift 6 be both @Observable and @MainActor?
Hi, In the WWDC25 session Elevate an app with Swift concurrency (timestamps: 8:04 and later), the StickerViewModel is shown annotated with @Observable but not @MainActor. The narration mentions that updates happen on the main thread, but that guarantee is left implicit in the calling code. In Swift 6, though, one of the major benefits is stronger compiler enforcement against data races and isolation rules. If a view model were also annotated with @MainActor, then the compiler could enforce that observable state is only updated on the main actor, preventing accidental background mutations or updates that can cause data races between nonisolated and main actor-isolated uses. Since @Observable already signals that state changes are intended to be observed (and in practice, usually by views), it seems natural that such types should also be main-actor isolated. Otherwise, we’re left with an implicit expectation that updates will always come from the main thread, but without the compiler’s help in enforcing that rule. This also ties into the concept of local reasoning that was emphasized in other Swift 6 talks (e.g. Beyond the basics of structured concurrency). With @MainActor, I can look at a view model and immediately know that all of its state is main-actor isolated. With only @Observable, that guarantee is left out, which feels like it weakens the clarity that Swift 6 is trying to promote. Would it be considered a best practice in Swift 6 to use both @Observable and @MainActor for UI-facing view models? Or is the intention that SwiftUI developers should rely on calling context to ensure main-thread updates, even if that means the compiler cannot enforce isolation? Thanks!
6
0
223
3w
How to get the frame or insets of the new iPadOS 26 window control buttons (close, minimize, fullscreen)?
In iPadOS 26, Apple introduced macOS-style window control buttons (close, minimize, fullscreen) for iPad apps running in a floating window. I'm working on a custom toolbar (UIView) positioned near the top of the window, and I'd like to avoid overlapping with these new controls. However, I haven't found any public API that exposes the frame, layout margins, or safe area insets related to this new UI region. I've checked the window's safeAreaInsets, additionalSafeAreaInsets, and UIWindowSceneDelegate APIs, but none of them seem to reflect the area occupied by these buttons. Is there an officially supported way to: Get the layout information (frame, insets, or margins) of the window control buttons on iPadOS 26? Or, is there a system-defined guideline or padding value we should use to avoid overlapping this new UI? Any clarification or guidance would be appreciated!
6
1
481
4w
SwiftUI Previews broken on local Swift Package with dependencies
I have a project with two local packages One client package with an interface and some models with dynamic type in the Package.Swift One feature package with the UI and a dependency to the client package When I try to preview a view that is not using any models or code from the client package, it loads just fine e.g. a view that is just a container to display things like a card But when I tried to preview any other view that actually uses models from the client package, it just fails the first few lines of the preview error display LinkDylibError: Failed to build <filename>.swift Linking failed: linker command failed with exit code 1 (use -v to see invocation) ld: warning: search path '/Applications/Xcode-15.4.0.app/Contents/SharedFrameworks-iphonesimulator' not found Undefined symbols for architecture arm64: Also, I'm using Xcode 15.4 and iOS 17 as the min version
6
1
1.9k
Oct ’24
TextKit 2 calls NSTextLayoutFragment's draw method too often
The following is verbatim of a feedback report (FB19809442) I submitted, shared here as someone else might be interested to see it (I hate the fact that we can't see each other's feedbacks). On iOS 16, TextKit 2 calls NSTextLayoutFragment's draw(at:in:) method once for the first paragraph, but for every other paragraph, it calls it continuously on every scroll step in the UITextView. (The first paragraph is not cached; its draw is called again when it is about to be displayed again, but then it is again called only once per its lifecycle.) On iOS 17, the behavior is similar; the draw method gets called once for the 1st and 2nd paragraph, and for every other paragraph it again gets called continuously as a user scrolls a UITextView. On iOS 18 (and iOS 26 beta 4), TextKit 2 calls the layout fragment's draw(at:in:) on every scroll step in the UITextView, for all paragraphs. This results in terrible performance. TextKit 2 is promised to bring many performance benefits by utilizing the viewport - a new concept that represents the visible area of a text view, along with a small overscroll. However, having the draw method being constantly called almost negates all the performance benefits that viewport brings. Imagine what could happen if someone needs to add just a bit of logic to that draw method. FPS drops significantly and UX is terribly degraded. I tried optimizing this by only rendering those text line fragments which are in the viewport, by using NSTextViewportLayoutController.viewportBounds and converting NSTextLineFragment.typographicBounds to the viewport-relative coordinate space (i.e. the coordinate space of the UITextView itself). However, this patch only works on iOS 18 where the draw method is called too many times, as the viewport changes. (I may have some other problems in my implementation, but I gave up on improving those, as this can't work reliably on all OS versions since the underlying framework isn't calling the method consistently.) Is this expected? What are our options for improving performance in these areas?
6
0
157
3w
Modal Presentation in UIKit Adds Solid Background in iOS 26
Hello, I have a number of UIViewControllers that are presented as follows: vc.modalPresentationStyle = UIModalPresentationStyle.popover vc.modalTransitionStyle = UIModalTransitionStyle.coverVertical self.present(vc, animated: true, completion: nil) The VC is designed from a Storyboard where I set the 'view' of the VC to have a .clear 'backgroundColor', I have a smaller 'Alert View' added as a subview which is what the user interacts with. In iOS 13 - iOS 18 this would present modally, not take up the entire screen and allow the user to see relevant context from the screen underneath. In iOS 26 Beta 5 and every beta prior the system injects a 'UIDropShadowView' in the View Hierarchy, this view has a solid color backdrop, either white/black depending on light/dark mode. This causes all underlying content to be blocked and essentially forces a full screen modal presentation despite the existing design. I am looking for a way to remove this solid color. I'm not sure if it's intentional or a bug / oversight. I have been able to remove it in a hacky way, I cycle the view hierarchy to find 'UIDropShadowView' and set it's backdrop to transparent. However when you swipe down to partially dismiss the view it turns to Liquid Glass when it is around 75% dismissed and then resets the background color to white/black. I tried creating a custom UIViewControllerTransitioningDelegate so that I could re-implement the existing behaviour but it's incredibly difficult to mimic the partial dismiss swipe down effect on the VC. I have also tried changing my presentation to: vc.modalPresentationStyle = UIModalPresentationStyle.overFullScreen vc.modalTransitionStyle = UIModalTransitionStyle.crossDissolve This works but then the user loses the ability to interactively swipe to dismiss. Any help would be appreciated. Thank you!
Topic: UI Frameworks SubTopic: UIKit Tags:
6
2
316
Aug ’25
iOS 18 SwiftUI navigation bar problem
Okay I know, fill a bug... but here is a super simple app that will demostrate that the navigation bar chnages from Dark scheme to light scheme when you tap back after it goes to the second view. import SwiftUI struct ContentView: View { var body: some View { NavigationStack { NavigationLink { Text("Tap back and notice the navigation title changes to black text instead of white") .toolbarBackground(.visible, for: .navigationBar) .toolbarBackground(Color.orange, for: .navigationBar) .toolbarColorScheme(.dark, for: .navigationBar) } label: { VStack { Text("First page is the sweetest") } .padding() } .navigationTitle("First Page") .toolbarBackground(.visible, for: .navigationBar) .toolbarBackground(Color.orange, for: .navigationBar) .toolbarColorScheme(.dark, for: .navigationBar) } } } #Preview { ContentView() }
6
9
4.6k
Feb ’25
How to update data in a DataFrame
My project loads a CSV into a DataFrame and displays it in a Table (a MacOS app). So far so good ... but when trying to update a value in a column, I dont see anyway to update this value. The table gets the value for the column like this: func getColumnValue(row :DataFrame.Rows.Element, columnName :String) -&gt; String { if row.base.containsColumn(columnName) { var value = "" if row[columnName] != nil { value = "\(row[columnName]!)" } return value } ... But the documentation and googles dont show any way to update the same column. Any help is appreciated with cookies. Attempt to update: func setColumnValue(row :DataFrame.Rows.Element, columnName :String, value :String) { var column: [String?] = data[columnName] column[row.id] = value ... }
6
0
636
Dec ’24
Issues with List selection in Sequoia (SwiftUI)
In one of my applications I use several List views with Sections. After upgrading to Sequoia I faced the issue, that after selecting an item, the List suddenly scrolls to a different position. Sometimes the selection even gets out of the view, but in every case a double click just went to the wrong item. At one list I found out, that the issue could be solved after changing the data source. I used a computed property, what seems to be a stupid idea. After changing this it now works. Unfortunately there is another List, where this didn't bring the solution. And unfortunately, I cannot reproduce the issue in a code example. One guess of mine is, that it could be related to the fact, that the rows have different heights (because in some are two lines of text and in some are three). And it seems to happen only in very long lists. It worked perfectly in Sonoma. Does anyone face the same issue?
Topic: UI Frameworks SubTopic: SwiftUI
6
0
436
Feb ’25