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

Removing sidebar divider in NavigationSplitView
Hi, I’m practicing with NavigationSplitView for macOS and customizing the sidebar. I’ve managed to adjust most parts, but I couldn’t remove the sidebar’s divider. It seems like it’s not possible in modern SwiftUI. My AppKit knowledge is also not very strong. How can I remove the sidebar divider? I want to use a plain background. I also solved it by creating my own sidebar, but I wanted to try it using NavigationSplitView.
Topic: UI Frameworks SubTopic: SwiftUI Tags:
0
0
212
Mar ’25
DatePicker bezel delete?
Is there a SwuiftUI way to remove the bezel from the compact DatePicker on MacOS? I have an AppKit version but getting the font/background colors to behave is overly complicated for such a simple mission.
Topic: UI Frameworks SubTopic: SwiftUI Tags:
1
0
247
Feb ’25
App crashes after multiple transitions to screen containing AR Kit using SwiftUI NavigationStack
Hello. I am currently building an app using AR Kit. As for the UI, I am using SwiftUI and NavigationStack + NavigationLink for navigation and screen transitions! Here I need to go back and forth between the AR screen and other screens. If the number of screen transitions is small, this is not a problem. However, if the number of screen transitions increases to 10 or 20, it crashes somewhere. We are struggling with this problem. (The nature of the application requires multiple screen transitions.) The crash log showed the following. error: read memory from 0x1e387f2d4 failed AR_Crash_Sample-2025-03-07-115914.txt Incident Identifier: B23D806E-D578-4A95-8828-2A1E8D6BB7F8 Beta Identifier: 924A85AB-441C-41A7-9BC2-063940BDAF32 Hardware Model: iPhone16,1 Process: AR_Crash_Sample [2375] Path: /private/var/containers/Bundle/Application/FAC3D662-DB10-434E-A006-79B9515D8B7A/AR_Crash_Sample.app/AR_Crash_Sample Identifier: ar.crash.sample.AR.Crash.Sample Version: 1.0 (1) AppStoreTools: 16C7015 AppVariant: 1:iPhone16,1:18 Beta: YES Code Type: ARM-64 (Native) Role: Foreground Parent Process: launchd [1] Coalition: ar.crash.sample.AR.Crash.Sample [1464] Date/Time: 2025-03-07 11:59:14.3691 +0900 Launch Time: 2025-03-07 11:57:47.3955 +0900 OS Version: iPhone OS 18.3.1 (22D72) Release Type: User Baseband Version: 2.40.05 Report Version: 104 Exception Type: EXC_CRASH (SIGABRT) Exception Codes: 0x0000000000000000, 0x0000000000000000 Termination Reason: SIGNAL 6 Abort trap: 6 Terminating Process: AR_Crash_Sample [2375] Triggered by Thread: 7 Application Specific Information: abort() called Thread 7 name: Dispatch queue: com.apple.arkit.depthtechnique Thread 7 Crashed: 0 libsystem_kernel.dylib 0x1e387f2d4 __pthread_kill + 8 1 libsystem_pthread.dylib 0x21cedd59c pthread_kill + 268 2 libsystem_c.dylib 0x199f98b08 abort + 128 3 libc++abi.dylib 0x21ce035b8 abort_message + 132 4 libc++abi.dylib 0x21cdf1b90 demangling_terminate_handler() + 320 5 libobjc.A.dylib 0x18f6c72d4 _objc_terminate() + 172 6 libc++abi.dylib 0x21ce0287c std::__terminate(void (*)()) + 16 7 libc++abi.dylib 0x21ce02820 std::terminate() + 108 8 libdispatch.dylib 0x199edefbc _dispatch_client_callout + 40 9 libdispatch.dylib 0x199ee65cc _dispatch_lane_serial_drain + 768 10 libdispatch.dylib 0x199ee7158 _dispatch_lane_invoke + 432 11 libdispatch.dylib 0x199ee85c0 _dispatch_workloop_invoke + 1744 12 libdispatch.dylib 0x199ef238c _dispatch_root_queue_drain_deferred_wlh + 288 13 libdispatch.dylib 0x199ef1bd8 _dispatch_workloop_worker_thread + 540 14 libsystem_pthread.dylib 0x21ced8680 _pthread_wqthread + 288 15 libsystem_pthread.dylib 0x21ced6474 start_wqthread + 8 Perhaps I am using too much memory! How can I address this phenomenon? For the AR functionality, we are using UIViewRepresentable, which is written in UIKit and can be called from SwiftUI import ARKit import AsyncAlgorithms import AVFoundation import SCNLine import SwiftUI internal struct MeasureARViewContainer: UIViewRepresentable { @Binding var tapCount: Int @Binding var distance: Double? @Binding var currentIndex: Int var focusSquare: FocusSquare = FocusSquare() let coachingOverlay: ARCoachingOverlayView = ARCoachingOverlayView() func makeUIView(context: Context) -> ARSCNView { let arView: ARSCNView = ARSCNView() arView.delegate = context.coordinator let configuration: ARWorldTrackingConfiguration = ARWorldTrackingConfiguration() configuration.planeDetection = [.horizontal, .vertical] if ARWorldTrackingConfiguration.supportsFrameSemantics(.sceneDepth) { configuration.frameSemantics = [.sceneDepth, .smoothedSceneDepth] } arView.session.run(configuration, options: [.resetTracking, .removeExistingAnchors]) context.coordinator.sceneView = arView context.coordinator.scanTarget() coachingOverlay.session = arView.session coachingOverlay.delegate = context.coordinator coachingOverlay.goal = .horizontalPlane coachingOverlay.activatesAutomatically = true coachingOverlay.autoresizingMask = [.flexibleWidth, .flexibleHeight] coachingOverlay.translatesAutoresizingMaskIntoConstraints = false arView.addSubview(coachingOverlay) return arView } func updateUIView(_ _: ARSCNView, context: Context) { context.coordinator.mode = MeasurementMode(rawValue: currentIndex) ?? .width if tapCount == 0 { context.coordinator.resetMeasurement() return } if distance != nil { return } DispatchQueue.main.async { if context.coordinator.distance == nil { context.coordinator.handleTap() } } } static func dismantleUIView(_ uiView: ARSCNView, coordinator: Coordinator) { uiView.session.pause() coordinator.stopScanTarget() coordinator.stopSpeech() DispatchQueue.main.async { uiView.removeFromSuperview() } } func makeCoordinator() -> Coordinator { Coordinator(self) } class Coordinator: NSObject, ARSCNViewDelegate, ARSessionDelegate, ARCoachingOverlayViewDelegate { var parent: MeasureARViewContainer var sceneView: ARSCNView? var startPosition: SCNVector3? var pointedCount: Int = 0 var distance: Float? var mode: MeasurementMode = .width let synthesizer: AVSpeechSynthesizer = AVSpeechSynthesizer() var scanTargetTask: Task<Void, Never>? var currentResult: ARRaycastResult? init(_ parent: MeasureARViewContainer) { self.parent = parent } // ... etc } }
2
0
346
Mar ’25
How do I properly mix SwiftUI Views with Auto Layout Constraint animations?
I have a SwiftUI View I've introduced to a UIKit app, using UIHostingController. The UIView instance that contains the SwiftUI view is animated using auto layout constraints. In this code block, when a view controller's viewDidAppear method I'm creating the hosting controller and adding its view as a subview of this view controller's view, in addition to doing the Container View Controller dance. override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) let hostingViewController = UIHostingController(rootView: TestView()) hostingViewController.view.translatesAutoresizingMaskIntoConstraints = false addChild(hostingViewController) view.addSubview(hostingViewController.view) let centerXConstraint = hostingViewController.view.centerXAnchor.constraint(equalTo: view.centerXAnchor) let topConstraint = hostingViewController.view.topAnchor.constraint(equalTo: view.topAnchor) widthConstraint = hostingViewController.view.widthAnchor.constraint(equalToConstant: 361) heightConstraint = hostingViewController.view.heightAnchor.constraint(equalToConstant: 342) NSLayoutConstraint.activate([centerXConstraint, topConstraint, widthConstraint, heightConstraint]) hostingViewController.didMove(toParent: self) self.hostingViewController = hostingViewController } I add a button to the UI which will scale the UIHostingViewController by adjusting its height and width constraints. When it's tapped, this action method runs. @IBAction func animate(_ sender: Any) { widthConstraint.constant = 120.3 heightConstraint.constant = 114.0 UIView.animate(withDuration: 0.5) { self.view.layoutIfNeeded() } } The problem is, the SwiftUI view's contents "jump" at the start of the animation to the final height, then animate into place. I see this both using UIView.animate the UIKit way, or creating a SwiftUI animation and calling `UIView. What else do I need to add to make this animate smoothly?
1
0
474
Feb ’25
Unexpected onAppear behavior in NavigationStack with ViewThatFits
Hello, My goal is to have a NavigationStack whose root view is determined based on its height and width. To do so, I'm using ViewThatFits, which should choose the right view to display. It is working fine, but unexpectedly both views trigger onAppear, whereas only the appropriate one should. This causes the logic in both closures to be executed, which is not intended. The code below demonstrates the problem: struct NavigationStackContentView: View { var body: some View { NavigationStack { ViewThatFits(in: .vertical) { Color.yellow .onAppear { print("|-> on appear: yellow") } .onDisappear { print("|-> on disappear: yellow") } Color.red .frame(width: 1500, height: 1500) .onAppear { print("|-> on appear: red") } .onDisappear { print("|-> on disappear: red") } } } } } this produces: |-> on appear: red |-> on disappear: red |-> on appear: yellow When ViewThatFits is not nested within NavigationStack, the problem does not occur — only the yellow view (in this sample) triggers onAppear, which is the expected behavior. I also checked the macOS version, and the problem does not occur at all, whether within NavigationStack or not. This example is simple and demonstrates that the larger view is the second one. When I switch their places, the problem does not occur because it recognizes that the first view would not fit at this point. However, in my case I will have these views without knowing which one will not fit, so switching their order is not a viable solution if this works without NavigationStack. Am I doing something wrong, or is this a bug? // iOS: 18.3.1 Xcode: 16.2
1
0
207
Mar ’25
Guidelines for ViewThatFits to avoid run-time crashes
TLDR: What rules ensure you won't have sporadic run-time crashes when using ViewThatFits? My app crashes - luckily reproducible. But the code appeared syntacticly and logically correct. Simplified excerpt: https://github.com/alanrick/Experiment3 The crash is caused by ViewThatFits being overwhelmed by concurrent changes in other views, exacerbated by animation effects. In the original code the problem was even worse because I'd gone overboard and used ViewThatFits in sub-views making the whole thing too dynamic. 

 My first rule is: Do not use nested ViewThatFits. 

 But this alone is not sufficient. What other rules can I apply to ensure I won't have hard-to-detect run-time crashes when using ViewThatFits?
Topic: UI Frameworks SubTopic: SwiftUI
2
0
444
Feb ’25
How do you pass a view builder into a view?
I'm making a custom control, specifically a checkbox next to a "label." I want the label parameter, like many in Apple's built-in controls, to take a view-building closure. But I can't figure out the correct syntax. I looked at the declaration of Apple's NavigationLink control for clues: public struct NavigationLink<Label, Destination> : View where Label : View, Destination : View { /// Creates a navigation link that presents the destination view. /// - Parameters: /// - destination: A view for the navigation link to present. /// - label: A view builder to produce a label describing the `destination` /// to present. public init(@ViewBuilder destination: () -> Destination, @ViewBuilder label: () -> Label) But when I mimic this, the compiler complains about the body() function: struct CheckboxItem<Label> : View where Label : View { let stateCheck: () -> Bool let label: () -> any View let boxSize: CGFloat init(withStateCheck: @escaping () -> Bool, boxSize: CGFloat, @ViewBuilder label: @escaping () -> Label) { stateCheck = withStateCheck self.label = label self.boxSize = boxSize } var body: some View { HStack { <-- ERROR: "Type 'any View' cannot conform to 'View'" Image(systemName: stateCheck() ? "checkmark.square" : "square") .resizable() .aspectRatio(contentMode: .fit) .frame(width: boxSize, height: boxSize) .foregroundColor(AppStyle.labelColor) .opacity(0.75) label() } } } Also, note that I had to put @escaping before my label parameter, but that's not seen in Apple's. Any ideas?
Topic: UI Frameworks SubTopic: SwiftUI
1
0
209
Mar ’25
Strange behavior when pushing UIViewController
This is a very strange behavior when pushing vc that I have never seen since I started coding. The pushed ViewController is transparent and only navBarTitle is shown. After the push, you can't control anything unless you go back to the home screen. STEPS TO REPRODUCE Long press currency change button below.(currencyWrapper) Call selectCountry and this bug happens. SourceCode let currencyWrapper = UIView() private func configureCurrencyCard(){ //The strange behavior shows up after long pressing this currencyWrapper.backgroundColor = .white currencyWrapper.addTarget(self, action: #selector(changeCurrency)) currencyWrapper.setWidth(currencyChangeIcon.follow(by: 16, x: true)) currencyWrapper.setCenterX(w1/2) currencyWrapper.setHeight(currencyLabel.follow(by: 12, x: false)) currencyWrapper.roundToCircle(true) view.addSubview(currencyWrapper) } private func selectCountry(country: Country){ let vc = CountryViewController(country: country) vc.hidesBottomBarWhenPushed = true navigationController?.pushViewController(vc, animated: true) }
Topic: UI Frameworks SubTopic: UIKit Tags:
2
0
244
Mar ’25
business
import SwiftUI struct Product: Identifiable { let id = UUID() let name: String let pricePerKg: Double } struct ContentView: View { @State private var selectedProduct: Product? @State private var quantity: Double = 1.0 @State private var orderDate = Date() @State private var showingConfirmation = false let products = [ Product(name: "Lamb", pricePerKg: 15.0), Product(name: "Beef", pricePerKg: 20.0), Product(name: "Chicken", pricePerKg: 10.0) ] var body: some View { NavigationView { Form { Section(header: Text("Select Meat")) { Picker("Meat Type", selection: $selectedProduct) { ForEach(products) { product in Text(product.name).tag(product as Product?) } } } if let selectedProduct = selectedProduct { Section(header: Text("Quantity (kg)")) { Stepper(value: $quantity, in: 0.5...10, step: 0.5) { Text("\(quantity, specifier: "%.1f") kg") } } Section(header: Text("Delivery Date")) { DatePicker("Select Date", selection: $orderDate, in: Date()..., displayedComponents: .date) } Section(header: Text("Total Price")) { Text("$\(selectedProduct.pricePerKg * quantity, specifier: "%.2f")") } Button("Confirm Order") { showingConfirmation = true } .alert(isPresented: $showingConfirmation) { Alert(title: Text("Order Confirmed"), message: Text("You have ordered \(quantity, specifier: "%.1f") kg of \(selectedProduct.name) for \(orderDate.formatted(date: .long, time: .omitted))."), dismissButton: .default(Text("OK"))) } } } .navigationTitle("Halal Butcher") } } } @main struct HalalButcherApp: App { var body: some Scene { WindowGroup { ContentView() } } }
2
0
216
Mar ’25
How can I use specify the anchor used to display an item that a user scrolls to ?
I have a scrollview displaying a sequence of circles, which a user should be able to scroll through to select an item. When the user stops scrolling and the animation comes to rest the circle selected should display screen-centered. I had hoped to achieve this using .scrollPosition(id: selectedItem, anchor: .center) but it appears that the anchor argument is ignored when scrolled manually. (BTW - I searched but didn't locate this aspect in the Apple documentation so I'm not confident that this observation is really correct). https://youtu.be/TpXDTuL5yPQ The video shows the user-scrolling behaviour, and also the snap-to-anchor that I would like to achieve, but I would like this WITHOUT forcing a button press. I could juggle the container size and size of the circles so that they naturally fit centered into the screen, but I would prefer a more elegant solution. How can I force the scrolling to come to rest such that the circle glides to rest in the center of the screen/container? struct ItemChooser: View { @State var selectedItem: Int? var body: some View { VStack { Text("You have picked: \(selectedItem ?? 0)") ScrollHorizontalItemChooser(selectedItem: $selectedItem) } } } #Preview { ItemChooser(selectedItem: 1) } struct ScrollHorizontalItemChooser: View { @Binding var selectedItem: Int? @State var scrollAlignment: UnitPoint? = .center let ballSize: CGFloat = 150 let items = Array(1...6) @State var scrollPosition: ScrollPosition = ScrollPosition() var body: some View { VStack { squareUpButton ScrollView(.horizontal) { HStack(spacing: 10) { showBalls } .scrollTargetLayout() } .scrollPosition(id: $selectedItem, anchor: scrollAlignment ) .overlay{ crosshairs } } } var crosshairs: some View { Image(systemName: "scope").scaleEffect(3.0).opacity(0.3) } @ViewBuilder var showBalls: some View { let screenWidth: CGFloat = UIScreen.main.bounds.width var emptySpace: CGFloat {screenWidth / 2 - ballSize / 2 - 10} Spacer(minLength: emptySpace) ForEach(items, id: \.self) { item in poolBall( item) .id(item) } Spacer(minLength: emptySpace) } @ViewBuilder private func poolBall(_ item: Int) -> some View { Text("Item \(item)") .background { Circle() .foregroundColor(Color.green) .frame(width: ballSize, height: ballSize) } .frame(width: ballSize, height: ballSize) } @ViewBuilder var squareUpButton: some View { var tempSelected: Int? = nil Button("Square up with Anchor") { tempSelected = selectedItem selectedItem = 0 DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { selectedItem = tempSelected ?? 0 } } } }
Topic: UI Frameworks SubTopic: SwiftUI
1
0
230
Mar ’25
Closure with typed throws stored as a View property crashes on iOS 17
I've encountered an issue where storing a throws(PermissionError) closure as a property inside a SwiftUI View causes a runtime crash on iOS 17, while it works correctly on iOS 18. Here’s an example of the affected code: enum PermissionError: Error { case denied } struct PermissionCheckedView<AllowedContent: View, DeniedContent: View>: View { var protectedView: () throws(PermissionError) -> AllowedContent var deniedView: (PermissionError) -> DeniedContent init( @ViewBuilder protectedView: @escaping () throws(PermissionError) -> AllowedContent, @ViewBuilder deniedView: @escaping (PermissionError) -> DeniedContent ) { self.protectedView = protectedView self.deniedView = deniedView } public var body: some View { switch Result(catching: protectedView) { case .success(let content): content case .failure(let error): deniedView(error) } } } @main struct TestApp: App { var body: some Scene { WindowGroup { PermissionCheckedView { } deniedView: { _ in } } } } Specifically this is the stack trace (sorry for the picture I didn't know how to get the txt): If I use var protectedView: () throws -> AllowedContent without typed throws it works.
2
0
300
Feb ’25
AppKit: presentAsModalWindow doesn't center the presented window on macOS 15
When I present a view controller, whose view is a SwiftUI View, via presentAsModalWindow(_:) the presented window is no longer centered horizontally to the screen, but rather its origin is there. I know this issue occurs for macOS 15.2+, but can't tell if it is from 15.0+. I couldn't find any documentation on why was this changed. Here's an example code that represents my architecture: class RootViewController: NSViewController { private lazy var button: NSButton = NSButton( title: "Present", target: self, action: #selector(presentView)) override func viewDidLoad() { super.viewDidLoad() // Add button to tree } @objc func presentView() { presentAsModalWindow(PresentedViewController()) } } class PresentedViewController: NSViewController { override loadView() { view = NSHostingView(rootView: MyView()) } } struct MyView: View { /* impl */ }
Topic: UI Frameworks SubTopic: AppKit Tags:
0
0
186
Mar ’25
not work paragraphStyle in AttributedString
I tried to create a Text View using attributedString. I want to set the line height using paragraphStyle and return the Text, but paragraphStyle is not being applied. Why is that? extension Text { init?(_ content: String, font: StyleType, color: Color = .ppBlack) { var attributedString = AttributedString(content) attributedString.font = Font.custom(font.fontWeight, fixedSize: font.fontSize) attributedString.foregroundColor = color let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.minimumLineHeight = 16 paragraphStyle.maximumLineHeight = 16 paragraphStyle.lineSpacing = 0 attributedString.mergeAttributes(.init([.paragraphStyle: paragraphStyle])) self = Text(attributedString) } }
1
0
343
Feb ’25
NSTextView and TextField becomes non clickable after a alert/menu is shown
I have a NSViewController as the root view and have a switui view embedded in it via NSHostingView. override func loadView() { self.view = NSHostingView(rootView: SwiftUiView()) } } In the SwiftUiView, I have a TextField and an NSTextView embedded using NSViewRepresentable, along with a few buttons. There is also a menu: Menu { ForEach(menuItems), id: \.self) { item in Button { buttonClicked() } label: { Text(item) } } } label: { Image("DropDown") .contentShape(Rectangle()) .frame(maxWidth: .infinity) .frame(maxHeight: .infinity) } The NSTextView and TextField work fine, and I can type in them until I click on the menu or show an alert. After that, I can no longer place my cursor in the text fields. I am able to select the text but not type in it. When I click on the NSTextView or TextField, nothing happens. At first, I thought it was just a cursor visibility issue and tried typing, but I received an alert sound. I've been trying to fix this for a couple of days and haven't found any related posts. Any help would be greatly appreciated.
1
0
244
Mar ’25
Signal SIGABRT on accessing values from SwiftData query
I work on an iOS app using SwiftUI and SwiftData. I added a computed property to one of my models - Parent - that uses relationship - array of Child models - data and I started getting strange problems. Let me start with models: @Model final class Parent { var name: String @Relationship(deleteRule: .cascade, inverse: \Child.parent) var children: [Child]? = [] var streak: Int { // Yes, I know that's not optimal solution for such counter ;) guard let children = children?.sorted(using: SortDescriptor(\.date, order: .reverse)) else { return 0 } var date = Date.now let calendar = Calendar.current for (index, child) in children.enumerated() { if !calendar.isDate(child.date, inSameDayAs: date) { return index } date = calendar.date(byAdding: .day, value: -1, to: date) ?? .now } return children.count } init(name: String) { self.name = name } } @Model final class Child { var date: Date @Relationship(deleteRule: .nullify) var parent: Parent? init(date: Date, parent: Parent) { self.date = date self.parent = parent } } At first everything works as expected. The problem arises once I try to remove one of child from the parent instance. I remove the value from context and save changes without any problems, at least not ones that can be caught by do { } catch. But instead of refreshing UI I get an signal SIGABRT somewhere inside SwiftData internals that points to the line where I'm trying (inside View body) get a child from a Query: struct LastSevenDaysButtons: View { @Environment(\.modelContext) private var modelContext @Query private var children: [Child] private let dates: [Date] private let parent: Parent init(for parent: Parent) { self.parent = parent var lastSevenDays = [Date]() let calendar = Calendar.current let firstDate = calendar.date(byAdding: .day, value: -6, to: calendar.startOfDay(for: .now)) ?? .now var date = firstDate while date <= .now { lastSevenDays.append(date) date = calendar.date(byAdding: .day, value: 1, to: date) ?? .now } dates = lastSevenDays let parentId = parent.persistentModelID _children = Query( filter: #Predicate { $0.parent?.persistentModelID == parentId && $0.date >= firstDate }, sort: [SortDescriptor(\Child.date, order: .reverse)], animation: .default ) } var body: some View { VStack { HStack(alignment: .top) { ForEach(dates, id: \.self) { date in // Here is the last point on stack from my code that I see let child = children.first { $0.date == date } Button { if let child { modelContext.delete(child) } else { modelContext.insert(Child(date: date, parent: parent)) } do { try modelContext.save() } catch { print("Can't save changes for \(parent.name) on \(date.formatted(date: .abbreviated, time: .omitted)): \(error.localizedDescription)") } } label: { Text("\(date.formatted(date: .abbreviated, time: .omitted))") .foregroundStyle(child == nil ? .red : .blue) } } } } } } The LastSevenDaysButtons View is kind of deep in a View hierarchy: RootView -> ParentList -> ParentListItem -> LastSevenDaysButtons However once I move insides of ParentList to RootView application works just fine, although I see and warning: === AttributeGraph: cycle detected through attribute 6912 ===. What could be that I do wrong in here? I believe it must something I'm missing here, but after 2 days of debug, trial and errors, I can't think clearly anymore. Here is the minimal repro I managed to create: Signal SIGABRT on accessing values from SwiftData query
3
0
751
Feb ’25
Entire view re-renders when updating dictionary
I'm trying to create a form which reads and writes data to a dictionary. when I type something in a field whole form seems to update. Is there anyway to only update the field I'm typing? Android compose have something called SnapshotStateMap which allows smart re-rendering.
Topic: UI Frameworks SubTopic: SwiftUI
1
0
120
Mar ’25
Crash when rendering CALayer using UIGraphicsImageRenderer on background thread
Hello! I’m experiencing a crash in my iOS/iPadOS app related to a CALayer rendering process. The crash occurs when attempting to render a UIImage on a background thread. The crashes are occurring in our production app, and while we can monitor them through Crashlytics, we are unable to reproduce the issue in our development environment. Relevant Code I have a custom view controller that handles rendering CALayers onto images. This method creates a CALayer on the main thread and then starts a detached task to render this CALayer into a UIImage. The whole idea is learnt from this StackOverflow post: https://stackoverflow.com/a/77834613/9202699 Here are key parts of my implementation: class MyViewController: UIViewController { @MainActor func renderToUIImage(size: CGSize, itemsToDraw: [MyDrawingItem], transform: CGAffineTransform) async -> UIImage? { // Create CALayer and add it to the view. CATransaction.begin() let customLayer = MyDrawingLayer() customLayer.setupContent(itemsToDraw: itemsToDraw) // Position the frame off-screen to it hidden. customLayer.frame = CGRect( origin: CGPoint(x: -100 - size.width, y: -100 - size.height), size: size) customLayer.masksToBounds = true customLayer.drawsAsynchronously = true view.layer.addSublayer(customLayer) CATransaction.commit() // Render CALayer to UIImage in background thread. let image = await Task.detached { customLayer.setNeedsDisplay() let renderer = UIGraphicsImageRenderer(size: size) let image = renderer.image { // CRASH happens on this line let cgContext = $0.cgContext cgContext.saveGState() cgContext.concatenate(transform) customLayer.render(in: cgContext) cgContext.restoreGState() } return image }.value // Remove the CALayer from the view. CATransaction.begin() customLayer.removeFromSuperlayer() CATransaction.commit() return image } } class MyDrawingLayer: CALayer { var itemsToDraw: [MyDrawingItem] = [] func setupContent(itemsToDraw: [MyDrawingItem]) { self.itemsToDraw = itemsToDraw } override func draw(in ctx: CGContext) { for item in itemsToDraw { // Render the item to the context (example pseudo-code). // All items are thread-safe to use. // Things to draw may include CGPath, CGImages, UIImages, NSAttributedString, etc. item.draw(in: ctx) } } } Crash Log The crash occurs at the following location: Crashed: com.apple.root.default-qos.cooperative 0 MyApp 0x5cb300 closure #1 in closure #1 in MyViewController.renderToUIImage(size: CGSize, itemsToDraw: [MyDrawingItem], transform: CGAffineTransform) + 4313002752 (<compiler-generated>:4313002752) 1 MyApp 0x5cb300 closure #1 in closure #1 in MyViewController.renderToUIImage(size: CGSize, itemsToDraw: [MyDrawingItem], transform: CGAffineTransform) + 4313002752 (<compiler-generated>:4313002752) 2 MyApp 0x1a4578 AnyModifier.modified(for:) + 4308649336 (<compiler-generated>:4308649336) 3 MyApp 0x7b4e64 thunk for @escaping @callee_guaranteed (@guaranteed UIGraphicsPDFRendererContext) -> () + 4315008612 (<compiler-generated>:4315008612) 4 UIKitCore 0x1489c0 -[UIGraphicsRenderer runDrawingActions:completionActions:format:error:] + 324 5 UIKitCore 0x14884c -[UIGraphicsRenderer runDrawingActions:completionActions:error:] + 92 6 UIKitCore 0x148778 -[UIGraphicsImageRenderer imageWithActions:] + 184 7 MyApp 0x5cb1c0 closure #1 in MyViewController.renderToUIImage(size: CGSize, itemsToDraw: [MyDrawingItem], transform: CGAffineTransform) + 100 (FileName.swift:100) 8 libswift_Concurrency.dylib 0x60f5c swift::runJobInEstablishedExecutorContext(swift::Job*) + 252 9 libswift_Concurrency.dylib 0x62514 swift_job_runImpl(swift::Job*, swift::SerialExecutorRef) + 144 10 libdispatch.dylib 0x15ec0 _dispatch_root_queue_drain + 392 11 libdispatch.dylib 0x166c4 _dispatch_worker_thread2 + 156 12 libsystem_pthread.dylib 0x3644 _pthread_wqthread + 228 13 libsystem_pthread.dylib 0x1474 start_wqthread + 8 Questions Is it safe to run UIGraphicsImageRenderer.image on the background thread? Given that I want to leverage GPU rendering, what are some best practices for rendering images off the main thread while ensuring stability? Are there alternatives to using UIGraphicsImageRenderer for background rendering that can still take advantage of GPU rendering? It is particularly interesting that the crash logs indicate the error may be related to UIGraphicsPDFRendererContext (crash log line number 3). It would be very helpful if someone could explain the connection between starting and drawing on a UIGraphicsImageRenderer and UIGraphicsPDFRendererContext. Any insights or guidance on this issue would be greatly appreciated. Thanks!!!
1
0
613
Feb ’25
Memory Leak in WatchOS 11.2 with NavigationTitle in Presented Sheet
I would like to report a memory leak issue in watchOS 11.2 that occurs when using .navigationTitle() inside a sheet. This behavior is reproducible both on the simulator and on a real device, but not on iOS. While this does not register as a leak in Instruments, the deinit of the DetailsViewModel is never called, and multiple instances of the view model accumulate in the Memory Graph Debugger. Commenting out .navigationTitle("Sheet View") resolves the issue, and deinit prints as expected. Using @MainActor on the DetailsViewModel does not fix the issue. Nor does switching to @StateObject and using ObservableObject resolve the memory retention. This issue seems related to other SwiftUI memory leaks that have been reported: https://developer.apple.com/forums/thread/738840 https://developer.apple.com/forums/thread/736110?login=true&page=1#769898022 https://developer.apple.com/forums/thread/737967?answerId=767599022#767599022 Feedback Number: FB16442048 struct MainView: View { var body: some View { NavigationStack { NavigationLink("Details", value: 1) .navigationDestination(for: Int.self) { _ in DetailsView() } } } } struct SheetObject: Identifiable { let id = UUID() let date: Date let value: Int } @Observable @MainActor final class DetailsViewModel { var sheetObject: SheetObject? init() { print("Init") } deinit { print("Deinit") } func onAppear() async { try? await Task.sleep(for: .seconds(2)) sheetObject = .init(date: .now, value: 1) } } struct DetailsView: View { @State private var viewModel = DetailsViewModel() @Environment(\.dismiss) var dismiss var body: some View { Text("Detail View. Going to sheet, please wait...") .task { await viewModel.onAppear() } .sheet(item: $viewModel.sheetObject) { sheetObject in SheetView(sheetObject: sheetObject) .onDisappear { dismiss() } } } } struct SheetView: View { let sheetObject: SheetObject @Environment(\.dismiss) var dismiss var body: some View { NavigationStack { VStack { Text(sheetObject.date.formatted()) Text(sheetObject.value.formatted()) Button("Dismiss") { dismiss() } } .navigationTitle("Sheet View") // This line causes a memory leak. Commenting out, you will see "Deinit" be printed. } } }
Topic: UI Frameworks SubTopic: SwiftUI
2
0
280
Feb ’25
.fileImporter not working on iPhone
I've been running into an issue using .fileImporter in SwiftUI already for a year. On iPhone simulator, Mac Catalyst and real iPad it works as expected, but when it comes to the test on a real iPhone, the picker just won't let you select files. It's not the permission issue, the sheet won't close at all and the callback isn't called. At the same time, if you use UIKits DocumentPickerViewController, everything starts working as expected, on Mac Catalyst/Simulator/iPad as well as on a real iPhone. Steps to reproduce: Create a new Xcode project using SwiftUI. Paste following code: import SwiftUI struct ContentView: View { @State var sShowing = false @State var uShowing = false @State var showAlert = false @State var alertText = "" var body: some View { VStack { VStack { Button("Test SWIFTUI") { sShowing = true } } .fileImporter(isPresented: $sShowing, allowedContentTypes: [.item]) {result in alertText = String(describing: result) showAlert = true } VStack { Button("Test UIKIT") { uShowing = true } } .sheet(isPresented: $uShowing) { DocumentPicker(contentTypes: [.item]) {url in alertText = String(describing: url) showAlert = true } } .padding(.top, 50) } .padding() .alert(isPresented: $showAlert) { Alert(title: Text("Result"), message: Text(alertText)) } } } DocumentPicker.swift: import SwiftUI import UniformTypeIdentifiers struct DocumentPicker: UIViewControllerRepresentable { let contentTypes: [UTType] let onPicked: (URL) -> Void func makeCoordinator() -> Coordinator { Coordinator(self) } func makeUIViewController(context: Context) -> UIDocumentPickerViewController { let documentPicker = UIDocumentPickerViewController(forOpeningContentTypes: contentTypes, asCopy: true) documentPicker.delegate = context.coordinator documentPicker.modalPresentationStyle = .formSheet return documentPicker } func updateUIViewController(_ uiViewController: UIDocumentPickerViewController, context: Context) {} class Coordinator: NSObject, UIDocumentPickerDelegate { var parent: DocumentPicker init(_ parent: DocumentPicker) { self.parent = parent } func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) { print("Success!", urls) guard let url = urls.first else { return } parent.onPicked(url) } func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) { print("Picker was cancelled") } } } Run the project on Mac Catalyst to confirm it working. Try it out on a real iPhone. For some reason, I can't attach a video, so I can only show a screenshot
1
0
457
Feb ’25