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

tvOS Remote Control Entitlements
I've been looking for a solution to configure the Apple TV remote(s) from the 5th generation and upwards. Some of the basic functionalities are disabling buttons on the physical remote control while maintaining proper functionality on remote controller apps on iOS devices. There seems to be a lack of relevant entitlements in that category, and without it I can't seem to figure out a way to make it work. Any ideas on the matter? Maybe a workaround that allows to configure the Apple TV to work with other remotes? Thank you in advance to anyone that put in thought to my query.
0
0
116
May ’25
How to show lanes in carplay programatically
Hello all, I'm confused about how to show lanes in CarPlay. I understand CPLaneGuidance and CPLane I don't find anywhere where to tell Carplay which icon to show for each lane. I've found some information saying we put the icon in CPManeuver, but then CPManeuver is linked to only one CPLaneGuidance, and we can put only one icon in CPManeuver. At the same time, we might have multiple lanes. Any help, tips, or examples would be highly helpful.
0
0
73
Jun ’25
Xcode 26 - New Swift 6.2 Concurrency Sendable Closure Problems
I'm running into a seemingly unsolvable compile problem with the new Xcode 26 and Swift 6.2. Here's the issue. I've got this code that was working before: NSAnimationContext.runAnimationGroup({(context) -> Void in context.duration = animated ? 0.5 : 0 clipView.animator().setBoundsOrigin(p) }, completionHandler: { self.endIgnoreFrameChangeEvents() }) It's very simple. The clipView is a scrollView.contentView, and "animated" is a bool, and p is an NSPoint It captures those things, scrolls the clip view (animating if needed) to the point, and then calls a method in self to signal that the animation has completed. I'm getting this error: Call to main actor-isolated instance method 'endIgnoreFrameChangeEvents()' in a synchronous nonisolated context So, I don't understand why so many of my callbacks are getting this error now, when they worked before, but it is easy to solve. There's also an async variation of runAnimationGroup. So let's use that instead: Task { await NSAnimationContext.runAnimationGroup({(context) -> Void in context.duration = animated ? 0.5 : 0 clipView.animator().setBoundsOrigin(p) }) self.endIgnoreFrameChangeEvents() } So, when I do this, then I get a new error. Now it doesn't like the first enclosure. Which it was perfectly happy with before. Here's the error: Sending value of non-Sendable type '(NSAnimationContext) -> Void' risks causing data races Here are the various overloaded definitions of runAnimationGroup: open class func runAnimationGroup(_ changes: (NSAnimationContext) -> Void, completionHandler: (@Sendable () -> Void)? = nil) @available(macOS 10.7, *) open class func runAnimationGroup(_ changes: (NSAnimationContext) -> Void) async @available(macOS 10.12, *) open class func runAnimationGroup(_ changes: (NSAnimationContext) -> Void) The middle one is the one that I'm trying to use. The closure in this overload isn't marked sendable. But, lets try making it sendable now to appease the compiler, since that seems to be what the error is asking for: Task { await NSAnimationContext.runAnimationGroup({ @Sendable (context) -> Void in context.duration = animated ? 0.5 : 0 clipView.animator().setBoundsOrigin(p) }) self.endIgnoreFrameChangeEvents() } So now I get errors in the closure itself. There are 2 errors, only one of which is easy to get rid of. Call to main actor-isolated instance method 'animator()' in a synchronous nonisolated context Call to main actor-isolated instance method 'setBoundsOrigin' in a synchronous nonisolated context So I can get rid of that first error by capturing clipView.animator() outside of the closure and capturing the animator. But the second error, calling setBoundsOrigin(p) - I can't move that outside of the closure, because that is the thing I am animating! Further, any property you're going to me animating in runAnimationGroup is going to be isolated to the main actor. So now my code looks like this, and I'm stuck with this last error I can't eliminate: let animator = clipView.animator() Task { await NSAnimationContext.runAnimationGroup({ @Sendable (context) -> Void in context.duration = animated ? 0.5 : 0 animator.setBoundsOrigin(p) }) self.endIgnoreFrameChangeEvents() } Call to main actor-isolated instance method 'setBoundsOrigin' in a synchronous nonisolated context There's something that I am not understanding here that has changed about how it is treating closures. This whole thing is running synchronously on the main thread anyway, isn't it? It's being called from a MainActor context in one of my NSViews. I would expect the closure in runAnimationGroup would need to be isolated to the main actor, anyway, since any animatable property is going to be marked MainActor. How do I accomplish what I am trying to do here? One last note: There were some new settings introduced at WWDC that supposedly make this stuff simpler - "Approchable Concurrency". In this example, I didn't have that turned on. Turning it on and setting the default to MainActor does not seem to have solved this problem. (All it does is cause hundreds of new concurrency errors in other parts of my code that weren't there before!) This is the last new error in my code (without those settings), but I can't see any way around this one. It's basically the same error as the others I was getting (in the callback closures), except with those I could eliminate the closures by changing APIs.
5
0
547
Jun ’25
Mixing ReferenceFileDocument and @Observable
I have an app in which the data model is @Observable, and views see it through @Environment(dataModel.self) private var dataModel. Since there are a large number of views, only some of which may need to be redrawn at a given time, I believe that @Observable is more efficient at run time than @Published and @ObservedObject I’ve been trying to make the app document based. Although I started using SwiftData, it has trouble with Codable, and a long thread in the Developer forum suggests that SwiftData does not support the Undo manager - and in any event, simple JSON serialization is all that this app requires. Unfortunately, ReferenceFileDocument inherits from ObservableObject, which seems to not play nice with @Observable. I’d like to keep using @Observable, but haven’t been able to figure out how. When I deserialize a JSON ReferenceFileDocument, I can’t seem to connect it to an @Observable class instance and to let the various views and view models know where to find and update it. I’d appreciate advice on how to implement document persistence in this app. Also, the default behaviour of DoumentGroup provides a nice menu to, another things, rename a new file to something other than Untitled xx, but it doesn’t appear to work (there is an extensive thread on the Developer website discussing this issue). Is there a solution to this problem? Thanks for any help you can offer.
3
0
176
May ’25
SwiftData .deny deleteRule not working
I tried to use the .deny deleteRule but it seems to have no effect. The toolbar button adds an item with a relationship to a category to the context. Swiping on the category deletes the category even though an item is referencing the category. There is also no error thrown when saving the context. It is as if the deleteRule was not there. For other deleteRules like .cascade, the provided sample code works as expected. import SwiftUI import SwiftData @Model class Category { var name: String @Relationship(deleteRule: .deny) var items: [Item] = [] init(name: String) { self.name = name } } @Model class Item { var name: String var category: Category? init(name: String, category: Category) { self.name = name self.category = category } } struct DenyDeleteRule: View { @Environment(\.modelContext) private var modelContext @Query private var categories: [Category] @Query private var items: [Item] var body: some View { List { Section("Items") { ForEach(items) { item in Text(item.name) } } Section("Categories") { ForEach(categories) { category in VStack(alignment: .leading) { Text(category.name).bold() ForEach(category.items) { item in Text("• \(item.name)") } } } .onDelete(perform: deleteCategory) } } .toolbar { Button("Add Sample") { let category = Category(name: "Sample") let item = Item(name: "Test Item", category: category) modelContext.insert(item) } } } func deleteCategory(at offsets: IndexSet) { for index in offsets { let category = categories[index] modelContext.delete(category) do { try modelContext.save() } catch { print(error) } } } } #Preview { NavigationStack { DenyDeleteRule() } .modelContainer(for: [Item.self, Category.self], inMemory: true) }
1
0
114
Jun ’25
Recently used applications on guardian phone - FamilyControls, DeviceActivityReport
For an iOS app that runs in both child and parent mode across iOS devices. On the child device, with Family Controls enabled using .child permissions via AuthorizationCenter.requestAuthorization(for: .child). Is any way to display a list of recently used apps by the child on the parent (guardian) device, in a privacy-preserving and Apple-compliant way?
1
0
172
May ’25
SwiftUI app crashes (EXC_BAD_ACCESS) when view hierarchy becomes too large.
Hey! Our team is experiencing some issue in a large SwiftUI application. When loading large views, the app crashes with a EXC_BAD_ACCESS signal. This signal can be reported by Xcode either on the @main attribute, inside a view hierarchy, or any order property that is accessed in the view hierarchy. After some investigation we found several possible workarounds: Splitting up the view into smaller subviews Wrapping parts of the view into an AnyView, which isn't ideal. However, this only temporarily solved the issue. As the app becomes bigger, we run into this problem more frequently. When trying to reproduce this issue in a clean Xcode project, I came up with the following: struct ContentView: View { var body: some View { Text("Hello") .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} } } When running this, the app immediately crashes on an iPhone 14 (YMMV on different (newer) devices). Of course such a view is not very likely to occur, but in total a view hierarchy could have this many view modifiers. Is there some limit we should we aware of? How can we circumvent this? Thanks!
0
0
102
Jun ’25
AVAudioSession dropping wired USBAudio source
My app inputs electrical waveforms from an IV485B39 2 channel USB device using an AVAudioSession. Before attempting to acquire data I make sure the input device is available as follows: AVAudiosSession *audioSession = [AVAudioSession sharedInstance]; [audioSession setCategory :AVAudioSessionCategoryRecord error:&err]; NSArray *inputs = [audioSession availableInputs]; I have been using this code for about 10 years. My app is scriptable so a user can acquire data from the IV485B29 multiple times with various parameter settings (sampling rates and sample duration). Recently the scripts have been failing to complete and what I have notice that when it fails the list of available inputs is missing the USBAudio input. While debugging I have noticed that when working properly the list of inputs includes both the internal microphone as well as the USBAudio device as shown below. VIB_TimeSeriesViewController:***Available inputs = ( "<AVAudioSessionPortDescription: 0x11584c7d0, type = MicrophoneBuiltIn; name = iPad Microphone; UID = Built-In Microphone; selectedDataSource = Front>", "<AVAudioSessionPortDescription: 0x11584cae0, type = USBAudio; name = 485B39 200095708064650803073200616; UID = AppleUSBAudioEngine:Digiducer.com :485B39 200095708064650803073200616:000957 200095708064650803073200616:1; selectedDataSource = (null)>" ) But when it fails I only see the built in microphone. VIB_TimeSeriesViewController:***Available inputs = ( "<AVAudioSessionPortDescription: 0x11584cef0, type = MicrophoneBuiltIn; name = iPad Microphone; UID = Built-In Microphone; selectedDataSource = Front>" ) If I only see the built in microphone I immediately repeat the three lines of code and most of the "inputs" contains both the internal microphone and the USBAudioDevice AVAudiosSession *audioSession = [AVAudioSession sharedInstance]; [audioSession setCategory :AVAudioSessionCategoryRecord error:&err]; NSArray *inputs = [audioSession availableInputs]; This fix always works on my M2 iPadPro and my iPhone 14 but some of my customers have older devices and even with 3 tries they still get faults about 1 in 10 tries. I rolled back my code to a released version from about 12 months ago where I know we never had this problem and compiled it against the current libraries and the problem still exists. I assume this is a problem caused by a change in the AVAudioSession framework libraries. I need to find a way to work around the issue or get the library fixed.
0
0
146
May ’25
Hide title bar in Xcode preview (macOS)
I am trying to hide the titlebar for a macOS app and despite searching throughout the entire day, there's nothing that points to how I can achieve this. I did find this page in the documentation but I don't understand it. https://developer.apple.com/documentation/uikit/uititlebar/titlevisibility How do I remove the part where it says Xcode Previews? I have used the following on my WindowGroup that works perfectly when the app is being run but it doesn't do anything in the preview. .windowStyle(.hiddenTitleBar)
Topic: UI Frameworks SubTopic: SwiftUI
0
0
79
Jun ’25
The NSTextViewDelegate method textViewDidChangeSelection(:) will not fire, while all other text view delegate methods do.
I am trying to implement the NSTextViewDelegate function textViewDidChangeSelection(_ notification: Notification). My text view's delegate is the Coordinator of my NSViewRepresentable. I've found that this delegate function never fires, but any other delegate function that I implement, as long as it doesn't take a Notification as an argument, does fire (e.g., textView(:willChangeSelectionFromCharacterRange:toCharacterRange:), fires and is called on the delegate exactly when it should be). For context, I've verified all of the below: textView.isSelectable = true textView.isEditable = true textView.delegate === my coordinator I can call textViewDidChangeSelection(:) directly on the delegate without issue. I can select and edit text without issues. I.e., the selections are being set correctly. But the delegate method is never called when they are. I am able to add the intended delegate as an observer for the selector textViewDidChangeSelection via NotificationCenter. If I do this, the function executes when it should, but fires for every text view in my view hierarchy, which can number in the hundreds. I'm using an NSLayoutManager, so I figure this should only fire once. I've added a check within my code: func textViewDidChangeSelection(_ notification: Notification) { guard let textView = notification.object as? NSTextView, textView === layoutManager.firstTextView else { return } // Any code I want to execute... } But the above guard check lets through every notification, so, no matter what, my closure executes hundreds of times if I have hundreds of text views, all of them being sent by textView === layoutManager.firstTextView, but once for each and every text view managed by that layoutManager. Does anyone know why this method isn't ever called on the delegate, while seemingly all other delegate methods are? I could go the NotificationCenter route, but I'd love to know why this won't execute as a delegate method when documentation says that it should, and I don't want to have to implement a counter to make sure my code only executes once per selection update. And for more reasons than that, implementing via delegate method is preferable to using notifications for my use case. Thanks for any help!
3
0
261
May ’25
iOS UILabel textAlignment .justified results in wrong rect by layoutManager.boundingRect
I have a UILabel subclass showing NSAttributedString in which I need to draw a rounded rectangle background color around links: import UIKit class MyLabel: UILabel { private var linkRects = [[CGRect]]() private let layoutManager = NSLayoutManager() private let textContainer = NSTextContainer(size: .zero) private let textStorage = NSTextStorage() override func draw(_ rect: CGRect) { let path = UIBezierPath() linkRects.forEach { rects in rects.forEach { linkPieceRect in path.append(UIBezierPath(roundedRect: linkPieceRect, cornerRadius: 2)) } } UIColor.systemGreen.withAlphaComponent(0.4).setFill() path.fill() super.draw(rect) } override init(frame: CGRect) { super.init(frame: frame) setup() } required init?(coder: NSCoder) { super.init(coder: coder) setup() } private func setup() { numberOfLines = 0 adjustsFontForContentSizeCategory = true isUserInteractionEnabled = true lineBreakMode = .byWordWrapping contentMode = .redraw clearsContextBeforeDrawing = true isMultipleTouchEnabled = false backgroundColor = .red.withAlphaComponent(0.1) textContainer.lineFragmentPadding = 0 textContainer.maximumNumberOfLines = numberOfLines textContainer.lineBreakMode = lineBreakMode textContainer.layoutManager = layoutManager layoutManager.textStorage = textStorage layoutManager.addTextContainer(textContainer) textStorage.addLayoutManager(layoutManager) } override func layoutSubviews() { super.layoutSubviews() calculateRects() } private func calculateRects(){ linkRects.removeAll() guard let attributedString = attributedText else { return } textStorage.setAttributedString(attributedString) let labelSize = frame.size textContainer.size = labelSize layoutManager.ensureLayout(for: textContainer) let textBoundingBox = layoutManager.usedRect(for: textContainer) print("labelSize: \(labelSize)") print("textBoundingBox: \(textBoundingBox)") var wholeLineRanges = [NSRange]() layoutManager.enumerateLineFragments(forGlyphRange: NSRange(0 ..< layoutManager.numberOfGlyphs)) { _, rect, _, range, _ in wholeLineRanges.append(range) print("Whole line: \(rect), \(range)") } attributedString.enumerateAttribute(.link, in: NSRange(location: 0, length: attributedString.length)) { value, clickableRange, _ in if value != nil { var rectsForCurrentLink = [CGRect]() wholeLineRanges.forEach { wholeLineRange in if let linkPartIntersection = wholeLineRange.intersection(clickableRange) { var rectForLinkPart = layoutManager.boundingRect(forGlyphRange: linkPartIntersection, in: textContainer) rectForLinkPart.origin.y = rectForLinkPart.origin.y + (textContainer.size.height - textBoundingBox.height) / 2 // Adjust for vertical alignment rectsForCurrentLink.append(rectForLinkPart) print("Link rect: \(rectForLinkPart), \(linkPartIntersection)") } } if !rectsForCurrentLink.isEmpty { linkRects.append(rectsForCurrentLink) } } } print("linkRects: \(linkRects)") setNeedsDisplay() } } And I use this as such: let label = MyLabel() label.setContentHuggingPriority(.required, for: .vertical) label.setContentHuggingPriority(.required, for: .horizontal) view.addSubview(label) label.snp.makeConstraints { make in make.width.lessThanOrEqualTo(view.safeAreaLayoutGuide.snp.width).priority(.required) make.horizontalEdges.greaterThanOrEqualTo(view.safeAreaLayoutGuide).priority(.required) make.center.equalTo(view.safeAreaLayoutGuide).priority(.required) } let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.alignment = .justified let s = NSMutableAttributedString(string: "Lorem Ipsum: ", attributes: [.font: UIFont.systemFont(ofSize: 17, weight: .regular), .paragraphStyle: paragraphStyle]) s.append(NSAttributedString(string: "This property controls the maximum number of lines to use in order to fit the label's text into its bounding rectangle.", attributes: [.link: URL(string: "https://news.ycombinator.com/") as Any, .foregroundColor: UIColor.link, .font: UIFont.systemFont(ofSize: 14, weight: .regular), .paragraphStyle: paragraphStyle])) label.attributedText = s Notice the paragraphStyle.alignment = .justified This results in: As you can see, the green rect background is starting a bit further to the right and also ending much further to the right. If I set the alignment to be .left or .center, then it gives me the correct rects: Also note that if I keep .justified but change the font size for the "Lorem Ipsom:" part to be a bit different, lets say 16 instead of 17, then it gives me the correct rect too: Also note that if we remove some word from the string, then also it starts giving correct rect. It seems like if the first line is too squished, then it reports wrong rects. Why is .justified text alignment giving me wrong rects? How can I fix it?
Topic: UI Frameworks SubTopic: UIKit
0
0
115
May ’25
Is it safe to access NSPrinter.printerNames on a background thread?
I'm working on a macOS application that needs to query the list of available printers using NSPrinter.printerNames. For performance reasons, I'd like to perform this operation on a background thread. However, since NSPrinter is part of AppKit, and AppKit is generally not thread-safe unless explicitly stated, I want to confirm: Is it safe to call NSPrinter.printerNames from a background thread? I couldn’t find explicit guidance in the documentation regarding the thread-safety of printerNames, so any clarification or best practices would be appreciated. Thanks in advance! Note: I tested this api on a background thread in code and it did not give any error.
Topic: UI Frameworks SubTopic: AppKit Tags:
0
0
142
May ’25
.chartScrollableAxes(.horizontal) prevents chartYAxisLabel from being postioned top right, over Y axis
Adding .chartScrollableAxes(.horizontal) to a chart prevents chartYAxisLabel from being positioned top right, over Y axis. We want the label at top right, over the Y-axis, but with chartScrollableAxes it is always top right relative to the initial chartXVisibleDomain, which puts it in the middle of the chart if chartXVisibleDomain < full x domain. import SwiftUI import Charts struct ContentView: View { @State private var numbers = (0..<100) .map { _ in Double.random(in: 0...100) } @State var visibleDomain : Int = 50 var body: some View { Chart(Array(zip(numbers.indices, numbers)), id: \.1) { index, number in LineMark( x: .value("Index", index), y: .value("Number", number) ) } .chartScrollableAxes(.horizontal) .chartXVisibleDomain(length: visibleDomain) .chartScrollPosition(initialX: 70) .chartYAxisLabel(position: .topTrailing, alignment: .center) { /* We want the label at top right, over the Y-axis, but with chartScrollableAxes it is always top right relative to the initial chartXVisibleDomain, which puts it in the middle of the chart if chartXVisibleDomain < full x domain */ Text("units") .foregroundStyle(.red) .fontWeight(.bold) } .padding() } } #Preview { ContentView() }
1
0
279
Jun ’25
NSTextLists not rendered when NSTextContentStorageDelegate textContentStorage (_:, textParagraphWith:) is implemented
I have a UITextView that contains paragraphs with text bullet lists (via NSTextList). I also implement NSTextContentStorageDelegate.textContentStorage(_:, textParagraphWith:) in order to apply some custom attributes to the text without affecting the underlying attributed text. My implementation returns a new NSParagraph that modifies the foreground color of the text. I based this on the example in the WWDC 21 session "Meet Text Kit 2". UITextView stops rendering the bullets when I implement the delegate function and return a custom paragraph. Why? func textContentStorage(_ textContentStorage: NSTextContentStorage, textParagraphWith range: NSRange) -> NSTextParagraph? { guard let originalText = textContentStorage.textStorage?.attributedSubstring(from: range) else { return nil } let updatedText = NSMutableAttributedString(attributedString: originalText) updatedText.addAttribute(.foregroundColor, value: UIColor.green, range: NSRange(location: 0, length: updatedText.length)) let paragraph = NSTextParagraph(attributedString: updatedText) // Verify that the text still contains NSTextList if let paragraphStyle = paragraph.attributedString.attribute(.paragraphStyle, at: 0, effectiveRange: nil) as? NSParagraphStyle { assert(!paragraphStyle.textLists.isEmpty) } else { assertionFailure("Paragraph has lost its text lists") } return paragraph }
0
0
469
May ’25
largeTitleDisplayMode does not work correctly in iOS 26
The following code does not work correctly for apps built with the iOS 26 beta SDK. largeTitleDisplayMode = .always // it doesn’t work ! The same program works as expected when built with the iOS 18 SDK. // iOS 26 beta largeTitleDisplayMode = .never // it works as expected largeTitleDisplayMode = .automatic // it works as expected. largeTitleDisplayMode = .always // it doesn’t work! Works same as .automatic!
Topic: UI Frameworks SubTopic: UIKit
2
0
138
Jun ’25
Use Custom UIApplication Subclass with SwiftUI
I have a SwiftUI app which needs the Ivanti AppConnect SDK. The docs only show how to integrate it into a Swift/UIKit app. But I need it to work with SwiftUI. I probably could make a UIKit base app and then load my existing SwiftUI views and code through a SwiftUI component host or something. But I'd like to avoid that if possible. Here is where I'm stuck: The AppConnect framework loads through a custom UIApplication subclass in the main.swift file: import Foundation import AppConnect UIApplicationMain( CommandLine.argc, CommandLine.unsafeArgv, ACUIApplicationClassName, NSStringFromClass(AppDelegate.self) ) The startup works as expected, and the expected function is called in the AppDelegate class: func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {...} However, the SwiftUI view is not loaded and the scree stays blank. I implemented a SceneDelegate.swift class which doesn't seem to be called. Also, the following function in the AppDelegate doesn't get called either: func application( _ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {...} So how do I bootstrap SwiftUI with a custom UIApplication class? can that be done with the @main macro somehow? I'm still pretty new to Swift and iOS development. Any help is appreciated
Topic: UI Frameworks SubTopic: SwiftUI Tags:
0
0
92
May ’25
Failed to open URL asynchronously
I have a problem with the URL schemes under iOS 18. Data is being sent from one app to another app. The amount of data varies. It can sometimes be more than 5 MB. With iOS 18, errors often occur when sending large amounts of data. The error message is: "Failed to open URL asynchronously". If I send the data once again in this case, it works. To reproduce the error quickly, I wrote two small apps. AppA sends data to AppB. AppB calls AppA and AppA sends data to AppB again. The whole thing runs in an endless loop. Code snippet: // AppA // The file to which fileUrl points contains a 4 MB string. // The string consists of only one letter “AAAAAA....” let dataStr = try String(contentsOf: fileUrl, encoding: .utf8) if let url = URL(string: "appb://receive?data=\(dataStr)") { UIApplication.shared.open(url, options: [:]) { (result) in if !result { os_log("can't open url", type: .error) } } } // AppB DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { if let returnUrl = URL(string: "appa://return") { UIApplication.shared.open(returnUrl) } } If the test is started, the error occurs approximately 15-20 times per hour. The first error occurs very quickly if the device is restarted prior to this. As soon as the error occurs, we end up in os_log(“can't open url”, type: .error) I know the possibility of exchanging the data via AppGroups, but cannot use it in our case. Tested with following devices: // The error occurs: iPhone 11 with iOS 18.4.1 iPhone SE with iOS 18.5 // The error does not occur iPhone 8 with iOS 16.7.10 iPhone 16 simulator on a M1 MacBook (macOS 15.4.1) Unfortunately, there is no other error message in the "Console" app. Except "Failed to open URL asynchronously". There were no problems at this point between iOS 12 and iOS 17. My question is now, are there new limitations to the URL schemes under iOS 18 or is it a bug?
Topic: UI Frameworks SubTopic: UIKit Tags:
3
0
251
Jun ’25
macOS Sequoia/Xcode 16.2 - Catalyst App Missing Tab Bar
My Objective-C Catalyst app when built with Xcode 16.x/iOS 18 does not have a visible Tab Bar when run on Sequoia. App starts up in first tab, but there is no way to access other tabs. The same app when run on macOS Sonoma (or macOS Catalina) has a normal Tab Bar. The app has an initial View UITabBarController with 3 tabs. The main tab is a UiSplitViewController. Minimum macOS deployment 10.5. If app is built on Sonoma with Xcode 15.x/iOS 17 the Tab Bar is normal on macOS Sonoma, Sequoia, and Catalina. I've tried without success: if (@available(macCatalyst 18.0, *)) { self.tabBarController.tabBarHidden = false; } else { // Fallback on earlier versions } I wonder if this console log message has anything to do with the problem: CLIENT OF UIKIT REQUIRES UPDATE: This process does not adopt UIScene lifecycle. This will become an assert in a future version.
2
0
279
Jun ’25
UICollectionView with orthogonal (horizontal) section not calling touchesShouldCancel(in:)
I have a UICollectionView with horizontally scrolling sections. In the cell I have a UIButton. I need to cancel the touches when the user swipes horizontally but it does not work. touchesShouldCancel(in:) is only called when swiping vertically over the UIButton, not horizontally. Is there a way to make it work? Sample code below import UIKit class ConferenceVideoSessionsViewController: UIViewController { let videosController = ConferenceVideoController() var collectionView: UICollectionView! = nil var dataSource: UICollectionViewDiffableDataSource <ConferenceVideoController.VideoCollection, ConferenceVideoController.Video>! = nil var currentSnapshot: NSDiffableDataSourceSnapshot <ConferenceVideoController.VideoCollection, ConferenceVideoController.Video>! = nil static let titleElementKind = "title-element-kind" override func viewDidLoad() { super.viewDidLoad() navigationItem.title = "Conference Videos" configureHierarchy() configureDataSource() } } extension ConferenceVideoSessionsViewController { func createLayout() -> UICollectionViewLayout { let sectionProvider = { (sectionIndex: Int, layoutEnvironment: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection? in let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalHeight(1.0)) let item = NSCollectionLayoutItem(layoutSize: itemSize) // if we have the space, adapt and go 2-up + peeking 3rd item let groupFractionalWidth = CGFloat(layoutEnvironment.container.effectiveContentSize.width > 500 ? 0.425 : 0.85) let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(groupFractionalWidth), heightDimension: .absolute(200)) let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item]) let section = NSCollectionLayoutSection(group: group) section.orthogonalScrollingBehavior = .continuous section.interGroupSpacing = 20 section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 20) return section } let config = UICollectionViewCompositionalLayoutConfiguration() config.interSectionSpacing = 20 let layout = UICollectionViewCompositionalLayout( sectionProvider: sectionProvider, configuration: config) return layout } } extension ConferenceVideoSessionsViewController { func configureHierarchy() { collectionView = MyUICollectionView(frame: .zero, collectionViewLayout: createLayout()) collectionView.translatesAutoresizingMaskIntoConstraints = false collectionView.backgroundColor = .systemBackground view.addSubview(collectionView) NSLayoutConstraint.activate([ collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor), collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor), collectionView.topAnchor.constraint(equalTo: view.topAnchor), collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor) ]) collectionView.canCancelContentTouches = true } func configureDataSource() { let cellRegistration = UICollectionView.CellRegistration <ConferenceVideoCell, ConferenceVideoController.Video> { (cell, indexPath, video) in // Populate the cell with our item description. cell.buttonView.setTitle("Push, hold and swipe", for: .normal) cell.titleLabel.text = video.title } dataSource = UICollectionViewDiffableDataSource <ConferenceVideoController.VideoCollection, ConferenceVideoController.Video>(collectionView: collectionView) { (collectionView: UICollectionView, indexPath: IndexPath, video: ConferenceVideoController.Video) -> UICollectionViewCell? in // Return the cell. return collectionView.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item: video) } currentSnapshot = NSDiffableDataSourceSnapshot <ConferenceVideoController.VideoCollection, ConferenceVideoController.Video>() videosController.collections.forEach { let collection = $0 currentSnapshot.appendSections([collection]) currentSnapshot.appendItems(collection.videos) } dataSource.apply(currentSnapshot, animatingDifferences: false) } } class MyUICollectionView: UICollectionView { override func touchesShouldCancel(in view: UIView) -> Bool { print("AH: touchesShouldCancel view \(view.description)") if view is MyUIButton { return true } return false } } final class MyUIButton: UIButton { } class ConferenceVideoCell: UICollectionViewCell { static let reuseIdentifier = "video-cell-reuse-identifier" let buttonView = MyUIButton() let titleLabel = UILabel() override init(frame: CGRect) { super.init(frame: frame) configure() } required init?(coder: NSCoder) { fatalError() } } extension ConferenceVideoCell { func configure() { buttonView.translatesAutoresizingMaskIntoConstraints = false titleLabel.translatesAutoresizingMaskIntoConstraints = false contentView.addSubview(buttonView) contentView.addSubview(titleLabel) titleLabel.font = UIFont.preferredFont(forTextStyle: .caption1) titleLabel.adjustsFontForContentSizeCategory = true buttonView.layer.borderColor = UIColor.black.cgColor buttonView.layer.borderWidth = 1 buttonView.layer.cornerRadius = 4 buttonView.backgroundColor = UIColor.systemPink let spacing = CGFloat(10) NSLayoutConstraint.activate([ buttonView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), buttonView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), buttonView.topAnchor.constraint(equalTo: contentView.topAnchor), titleLabel.topAnchor.constraint(equalTo: buttonView.bottomAnchor, constant: spacing), titleLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), titleLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), titleLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor) ]) } }
Topic: UI Frameworks SubTopic: UIKit Tags:
1
0
108
Jun ’25
tvOS Remote Control Entitlements
I've been looking for a solution to configure the Apple TV remote(s) from the 5th generation and upwards. Some of the basic functionalities are disabling buttons on the physical remote control while maintaining proper functionality on remote controller apps on iOS devices. There seems to be a lack of relevant entitlements in that category, and without it I can't seem to figure out a way to make it work. Any ideas on the matter? Maybe a workaround that allows to configure the Apple TV to work with other remotes? Thank you in advance to anyone that put in thought to my query.
Replies
0
Boosts
0
Views
116
Activity
May ’25
How to show lanes in carplay programatically
Hello all, I'm confused about how to show lanes in CarPlay. I understand CPLaneGuidance and CPLane I don't find anywhere where to tell Carplay which icon to show for each lane. I've found some information saying we put the icon in CPManeuver, but then CPManeuver is linked to only one CPLaneGuidance, and we can put only one icon in CPManeuver. At the same time, we might have multiple lanes. Any help, tips, or examples would be highly helpful.
Replies
0
Boosts
0
Views
73
Activity
Jun ’25
Xcode 26 - New Swift 6.2 Concurrency Sendable Closure Problems
I'm running into a seemingly unsolvable compile problem with the new Xcode 26 and Swift 6.2. Here's the issue. I've got this code that was working before: NSAnimationContext.runAnimationGroup({(context) -&gt; Void in context.duration = animated ? 0.5 : 0 clipView.animator().setBoundsOrigin(p) }, completionHandler: { self.endIgnoreFrameChangeEvents() }) It's very simple. The clipView is a scrollView.contentView, and "animated" is a bool, and p is an NSPoint It captures those things, scrolls the clip view (animating if needed) to the point, and then calls a method in self to signal that the animation has completed. I'm getting this error: Call to main actor-isolated instance method 'endIgnoreFrameChangeEvents()' in a synchronous nonisolated context So, I don't understand why so many of my callbacks are getting this error now, when they worked before, but it is easy to solve. There's also an async variation of runAnimationGroup. So let's use that instead: Task { await NSAnimationContext.runAnimationGroup({(context) -&gt; Void in context.duration = animated ? 0.5 : 0 clipView.animator().setBoundsOrigin(p) }) self.endIgnoreFrameChangeEvents() } So, when I do this, then I get a new error. Now it doesn't like the first enclosure. Which it was perfectly happy with before. Here's the error: Sending value of non-Sendable type '(NSAnimationContext) -&gt; Void' risks causing data races Here are the various overloaded definitions of runAnimationGroup: open class func runAnimationGroup(_ changes: (NSAnimationContext) -&gt; Void, completionHandler: (@Sendable () -&gt; Void)? = nil) @available(macOS 10.7, *) open class func runAnimationGroup(_ changes: (NSAnimationContext) -&gt; Void) async @available(macOS 10.12, *) open class func runAnimationGroup(_ changes: (NSAnimationContext) -&gt; Void) The middle one is the one that I'm trying to use. The closure in this overload isn't marked sendable. But, lets try making it sendable now to appease the compiler, since that seems to be what the error is asking for: Task { await NSAnimationContext.runAnimationGroup({ @Sendable (context) -&gt; Void in context.duration = animated ? 0.5 : 0 clipView.animator().setBoundsOrigin(p) }) self.endIgnoreFrameChangeEvents() } So now I get errors in the closure itself. There are 2 errors, only one of which is easy to get rid of. Call to main actor-isolated instance method 'animator()' in a synchronous nonisolated context Call to main actor-isolated instance method 'setBoundsOrigin' in a synchronous nonisolated context So I can get rid of that first error by capturing clipView.animator() outside of the closure and capturing the animator. But the second error, calling setBoundsOrigin(p) - I can't move that outside of the closure, because that is the thing I am animating! Further, any property you're going to me animating in runAnimationGroup is going to be isolated to the main actor. So now my code looks like this, and I'm stuck with this last error I can't eliminate: let animator = clipView.animator() Task { await NSAnimationContext.runAnimationGroup({ @Sendable (context) -&gt; Void in context.duration = animated ? 0.5 : 0 animator.setBoundsOrigin(p) }) self.endIgnoreFrameChangeEvents() } Call to main actor-isolated instance method 'setBoundsOrigin' in a synchronous nonisolated context There's something that I am not understanding here that has changed about how it is treating closures. This whole thing is running synchronously on the main thread anyway, isn't it? It's being called from a MainActor context in one of my NSViews. I would expect the closure in runAnimationGroup would need to be isolated to the main actor, anyway, since any animatable property is going to be marked MainActor. How do I accomplish what I am trying to do here? One last note: There were some new settings introduced at WWDC that supposedly make this stuff simpler - "Approchable Concurrency". In this example, I didn't have that turned on. Turning it on and setting the default to MainActor does not seem to have solved this problem. (All it does is cause hundreds of new concurrency errors in other parts of my code that weren't there before!) This is the last new error in my code (without those settings), but I can't see any way around this one. It's basically the same error as the others I was getting (in the callback closures), except with those I could eliminate the closures by changing APIs.
Replies
5
Boosts
0
Views
547
Activity
Jun ’25
Mixing ReferenceFileDocument and @Observable
I have an app in which the data model is @Observable, and views see it through @Environment(dataModel.self) private var dataModel. Since there are a large number of views, only some of which may need to be redrawn at a given time, I believe that @Observable is more efficient at run time than @Published and @ObservedObject I’ve been trying to make the app document based. Although I started using SwiftData, it has trouble with Codable, and a long thread in the Developer forum suggests that SwiftData does not support the Undo manager - and in any event, simple JSON serialization is all that this app requires. Unfortunately, ReferenceFileDocument inherits from ObservableObject, which seems to not play nice with @Observable. I’d like to keep using @Observable, but haven’t been able to figure out how. When I deserialize a JSON ReferenceFileDocument, I can’t seem to connect it to an @Observable class instance and to let the various views and view models know where to find and update it. I’d appreciate advice on how to implement document persistence in this app. Also, the default behaviour of DoumentGroup provides a nice menu to, another things, rename a new file to something other than Untitled xx, but it doesn’t appear to work (there is an extensive thread on the Developer website discussing this issue). Is there a solution to this problem? Thanks for any help you can offer.
Replies
3
Boosts
0
Views
176
Activity
May ’25
SwiftData .deny deleteRule not working
I tried to use the .deny deleteRule but it seems to have no effect. The toolbar button adds an item with a relationship to a category to the context. Swiping on the category deletes the category even though an item is referencing the category. There is also no error thrown when saving the context. It is as if the deleteRule was not there. For other deleteRules like .cascade, the provided sample code works as expected. import SwiftUI import SwiftData @Model class Category { var name: String @Relationship(deleteRule: .deny) var items: [Item] = [] init(name: String) { self.name = name } } @Model class Item { var name: String var category: Category? init(name: String, category: Category) { self.name = name self.category = category } } struct DenyDeleteRule: View { @Environment(\.modelContext) private var modelContext @Query private var categories: [Category] @Query private var items: [Item] var body: some View { List { Section("Items") { ForEach(items) { item in Text(item.name) } } Section("Categories") { ForEach(categories) { category in VStack(alignment: .leading) { Text(category.name).bold() ForEach(category.items) { item in Text("• \(item.name)") } } } .onDelete(perform: deleteCategory) } } .toolbar { Button("Add Sample") { let category = Category(name: "Sample") let item = Item(name: "Test Item", category: category) modelContext.insert(item) } } } func deleteCategory(at offsets: IndexSet) { for index in offsets { let category = categories[index] modelContext.delete(category) do { try modelContext.save() } catch { print(error) } } } } #Preview { NavigationStack { DenyDeleteRule() } .modelContainer(for: [Item.self, Category.self], inMemory: true) }
Replies
1
Boosts
0
Views
114
Activity
Jun ’25
Recently used applications on guardian phone - FamilyControls, DeviceActivityReport
For an iOS app that runs in both child and parent mode across iOS devices. On the child device, with Family Controls enabled using .child permissions via AuthorizationCenter.requestAuthorization(for: .child). Is any way to display a list of recently used apps by the child on the parent (guardian) device, in a privacy-preserving and Apple-compliant way?
Replies
1
Boosts
0
Views
172
Activity
May ’25
SwiftUI app crashes (EXC_BAD_ACCESS) when view hierarchy becomes too large.
Hey! Our team is experiencing some issue in a large SwiftUI application. When loading large views, the app crashes with a EXC_BAD_ACCESS signal. This signal can be reported by Xcode either on the @main attribute, inside a view hierarchy, or any order property that is accessed in the view hierarchy. After some investigation we found several possible workarounds: Splitting up the view into smaller subviews Wrapping parts of the view into an AnyView, which isn't ideal. However, this only temporarily solved the issue. As the app becomes bigger, we run into this problem more frequently. When trying to reproduce this issue in a clean Xcode project, I came up with the following: struct ContentView: View { var body: some View { Text("Hello") .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} .task {} } } When running this, the app immediately crashes on an iPhone 14 (YMMV on different (newer) devices). Of course such a view is not very likely to occur, but in total a view hierarchy could have this many view modifiers. Is there some limit we should we aware of? How can we circumvent this? Thanks!
Replies
0
Boosts
0
Views
102
Activity
Jun ’25
AVAudioSession dropping wired USBAudio source
My app inputs electrical waveforms from an IV485B39 2 channel USB device using an AVAudioSession. Before attempting to acquire data I make sure the input device is available as follows: AVAudiosSession *audioSession = [AVAudioSession sharedInstance]; [audioSession setCategory :AVAudioSessionCategoryRecord error:&err]; NSArray *inputs = [audioSession availableInputs]; I have been using this code for about 10 years. My app is scriptable so a user can acquire data from the IV485B29 multiple times with various parameter settings (sampling rates and sample duration). Recently the scripts have been failing to complete and what I have notice that when it fails the list of available inputs is missing the USBAudio input. While debugging I have noticed that when working properly the list of inputs includes both the internal microphone as well as the USBAudio device as shown below. VIB_TimeSeriesViewController:***Available inputs = ( "<AVAudioSessionPortDescription: 0x11584c7d0, type = MicrophoneBuiltIn; name = iPad Microphone; UID = Built-In Microphone; selectedDataSource = Front>", "<AVAudioSessionPortDescription: 0x11584cae0, type = USBAudio; name = 485B39 200095708064650803073200616; UID = AppleUSBAudioEngine:Digiducer.com :485B39 200095708064650803073200616:000957 200095708064650803073200616:1; selectedDataSource = (null)>" ) But when it fails I only see the built in microphone. VIB_TimeSeriesViewController:***Available inputs = ( "<AVAudioSessionPortDescription: 0x11584cef0, type = MicrophoneBuiltIn; name = iPad Microphone; UID = Built-In Microphone; selectedDataSource = Front>" ) If I only see the built in microphone I immediately repeat the three lines of code and most of the "inputs" contains both the internal microphone and the USBAudioDevice AVAudiosSession *audioSession = [AVAudioSession sharedInstance]; [audioSession setCategory :AVAudioSessionCategoryRecord error:&err]; NSArray *inputs = [audioSession availableInputs]; This fix always works on my M2 iPadPro and my iPhone 14 but some of my customers have older devices and even with 3 tries they still get faults about 1 in 10 tries. I rolled back my code to a released version from about 12 months ago where I know we never had this problem and compiled it against the current libraries and the problem still exists. I assume this is a problem caused by a change in the AVAudioSession framework libraries. I need to find a way to work around the issue or get the library fixed.
Replies
0
Boosts
0
Views
146
Activity
May ’25
Hide title bar in Xcode preview (macOS)
I am trying to hide the titlebar for a macOS app and despite searching throughout the entire day, there's nothing that points to how I can achieve this. I did find this page in the documentation but I don't understand it. https://developer.apple.com/documentation/uikit/uititlebar/titlevisibility How do I remove the part where it says Xcode Previews? I have used the following on my WindowGroup that works perfectly when the app is being run but it doesn't do anything in the preview. .windowStyle(.hiddenTitleBar)
Topic: UI Frameworks SubTopic: SwiftUI
Replies
0
Boosts
0
Views
79
Activity
Jun ’25
The NSTextViewDelegate method textViewDidChangeSelection(:) will not fire, while all other text view delegate methods do.
I am trying to implement the NSTextViewDelegate function textViewDidChangeSelection(_ notification: Notification). My text view's delegate is the Coordinator of my NSViewRepresentable. I've found that this delegate function never fires, but any other delegate function that I implement, as long as it doesn't take a Notification as an argument, does fire (e.g., textView(:willChangeSelectionFromCharacterRange:toCharacterRange:), fires and is called on the delegate exactly when it should be). For context, I've verified all of the below: textView.isSelectable = true textView.isEditable = true textView.delegate === my coordinator I can call textViewDidChangeSelection(:) directly on the delegate without issue. I can select and edit text without issues. I.e., the selections are being set correctly. But the delegate method is never called when they are. I am able to add the intended delegate as an observer for the selector textViewDidChangeSelection via NotificationCenter. If I do this, the function executes when it should, but fires for every text view in my view hierarchy, which can number in the hundreds. I'm using an NSLayoutManager, so I figure this should only fire once. I've added a check within my code: func textViewDidChangeSelection(_ notification: Notification) { guard let textView = notification.object as? NSTextView, textView === layoutManager.firstTextView else { return } // Any code I want to execute... } But the above guard check lets through every notification, so, no matter what, my closure executes hundreds of times if I have hundreds of text views, all of them being sent by textView === layoutManager.firstTextView, but once for each and every text view managed by that layoutManager. Does anyone know why this method isn't ever called on the delegate, while seemingly all other delegate methods are? I could go the NotificationCenter route, but I'd love to know why this won't execute as a delegate method when documentation says that it should, and I don't want to have to implement a counter to make sure my code only executes once per selection update. And for more reasons than that, implementing via delegate method is preferable to using notifications for my use case. Thanks for any help!
Replies
3
Boosts
0
Views
261
Activity
May ’25
iOS UILabel textAlignment .justified results in wrong rect by layoutManager.boundingRect
I have a UILabel subclass showing NSAttributedString in which I need to draw a rounded rectangle background color around links: import UIKit class MyLabel: UILabel { private var linkRects = [[CGRect]]() private let layoutManager = NSLayoutManager() private let textContainer = NSTextContainer(size: .zero) private let textStorage = NSTextStorage() override func draw(_ rect: CGRect) { let path = UIBezierPath() linkRects.forEach { rects in rects.forEach { linkPieceRect in path.append(UIBezierPath(roundedRect: linkPieceRect, cornerRadius: 2)) } } UIColor.systemGreen.withAlphaComponent(0.4).setFill() path.fill() super.draw(rect) } override init(frame: CGRect) { super.init(frame: frame) setup() } required init?(coder: NSCoder) { super.init(coder: coder) setup() } private func setup() { numberOfLines = 0 adjustsFontForContentSizeCategory = true isUserInteractionEnabled = true lineBreakMode = .byWordWrapping contentMode = .redraw clearsContextBeforeDrawing = true isMultipleTouchEnabled = false backgroundColor = .red.withAlphaComponent(0.1) textContainer.lineFragmentPadding = 0 textContainer.maximumNumberOfLines = numberOfLines textContainer.lineBreakMode = lineBreakMode textContainer.layoutManager = layoutManager layoutManager.textStorage = textStorage layoutManager.addTextContainer(textContainer) textStorage.addLayoutManager(layoutManager) } override func layoutSubviews() { super.layoutSubviews() calculateRects() } private func calculateRects(){ linkRects.removeAll() guard let attributedString = attributedText else { return } textStorage.setAttributedString(attributedString) let labelSize = frame.size textContainer.size = labelSize layoutManager.ensureLayout(for: textContainer) let textBoundingBox = layoutManager.usedRect(for: textContainer) print("labelSize: \(labelSize)") print("textBoundingBox: \(textBoundingBox)") var wholeLineRanges = [NSRange]() layoutManager.enumerateLineFragments(forGlyphRange: NSRange(0 ..< layoutManager.numberOfGlyphs)) { _, rect, _, range, _ in wholeLineRanges.append(range) print("Whole line: \(rect), \(range)") } attributedString.enumerateAttribute(.link, in: NSRange(location: 0, length: attributedString.length)) { value, clickableRange, _ in if value != nil { var rectsForCurrentLink = [CGRect]() wholeLineRanges.forEach { wholeLineRange in if let linkPartIntersection = wholeLineRange.intersection(clickableRange) { var rectForLinkPart = layoutManager.boundingRect(forGlyphRange: linkPartIntersection, in: textContainer) rectForLinkPart.origin.y = rectForLinkPart.origin.y + (textContainer.size.height - textBoundingBox.height) / 2 // Adjust for vertical alignment rectsForCurrentLink.append(rectForLinkPart) print("Link rect: \(rectForLinkPart), \(linkPartIntersection)") } } if !rectsForCurrentLink.isEmpty { linkRects.append(rectsForCurrentLink) } } } print("linkRects: \(linkRects)") setNeedsDisplay() } } And I use this as such: let label = MyLabel() label.setContentHuggingPriority(.required, for: .vertical) label.setContentHuggingPriority(.required, for: .horizontal) view.addSubview(label) label.snp.makeConstraints { make in make.width.lessThanOrEqualTo(view.safeAreaLayoutGuide.snp.width).priority(.required) make.horizontalEdges.greaterThanOrEqualTo(view.safeAreaLayoutGuide).priority(.required) make.center.equalTo(view.safeAreaLayoutGuide).priority(.required) } let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.alignment = .justified let s = NSMutableAttributedString(string: "Lorem Ipsum: ", attributes: [.font: UIFont.systemFont(ofSize: 17, weight: .regular), .paragraphStyle: paragraphStyle]) s.append(NSAttributedString(string: "This property controls the maximum number of lines to use in order to fit the label's text into its bounding rectangle.", attributes: [.link: URL(string: "https://news.ycombinator.com/") as Any, .foregroundColor: UIColor.link, .font: UIFont.systemFont(ofSize: 14, weight: .regular), .paragraphStyle: paragraphStyle])) label.attributedText = s Notice the paragraphStyle.alignment = .justified This results in: As you can see, the green rect background is starting a bit further to the right and also ending much further to the right. If I set the alignment to be .left or .center, then it gives me the correct rects: Also note that if I keep .justified but change the font size for the "Lorem Ipsom:" part to be a bit different, lets say 16 instead of 17, then it gives me the correct rect too: Also note that if we remove some word from the string, then also it starts giving correct rect. It seems like if the first line is too squished, then it reports wrong rects. Why is .justified text alignment giving me wrong rects? How can I fix it?
Topic: UI Frameworks SubTopic: UIKit
Replies
0
Boosts
0
Views
115
Activity
May ’25
Is it safe to access NSPrinter.printerNames on a background thread?
I'm working on a macOS application that needs to query the list of available printers using NSPrinter.printerNames. For performance reasons, I'd like to perform this operation on a background thread. However, since NSPrinter is part of AppKit, and AppKit is generally not thread-safe unless explicitly stated, I want to confirm: Is it safe to call NSPrinter.printerNames from a background thread? I couldn’t find explicit guidance in the documentation regarding the thread-safety of printerNames, so any clarification or best practices would be appreciated. Thanks in advance! Note: I tested this api on a background thread in code and it did not give any error.
Topic: UI Frameworks SubTopic: AppKit Tags:
Replies
0
Boosts
0
Views
142
Activity
May ’25
.chartScrollableAxes(.horizontal) prevents chartYAxisLabel from being postioned top right, over Y axis
Adding .chartScrollableAxes(.horizontal) to a chart prevents chartYAxisLabel from being positioned top right, over Y axis. We want the label at top right, over the Y-axis, but with chartScrollableAxes it is always top right relative to the initial chartXVisibleDomain, which puts it in the middle of the chart if chartXVisibleDomain < full x domain. import SwiftUI import Charts struct ContentView: View { @State private var numbers = (0..<100) .map { _ in Double.random(in: 0...100) } @State var visibleDomain : Int = 50 var body: some View { Chart(Array(zip(numbers.indices, numbers)), id: \.1) { index, number in LineMark( x: .value("Index", index), y: .value("Number", number) ) } .chartScrollableAxes(.horizontal) .chartXVisibleDomain(length: visibleDomain) .chartScrollPosition(initialX: 70) .chartYAxisLabel(position: .topTrailing, alignment: .center) { /* We want the label at top right, over the Y-axis, but with chartScrollableAxes it is always top right relative to the initial chartXVisibleDomain, which puts it in the middle of the chart if chartXVisibleDomain < full x domain */ Text("units") .foregroundStyle(.red) .fontWeight(.bold) } .padding() } } #Preview { ContentView() }
Replies
1
Boosts
0
Views
279
Activity
Jun ’25
NSTextLists not rendered when NSTextContentStorageDelegate textContentStorage (_:, textParagraphWith:) is implemented
I have a UITextView that contains paragraphs with text bullet lists (via NSTextList). I also implement NSTextContentStorageDelegate.textContentStorage(_:, textParagraphWith:) in order to apply some custom attributes to the text without affecting the underlying attributed text. My implementation returns a new NSParagraph that modifies the foreground color of the text. I based this on the example in the WWDC 21 session "Meet Text Kit 2". UITextView stops rendering the bullets when I implement the delegate function and return a custom paragraph. Why? func textContentStorage(_ textContentStorage: NSTextContentStorage, textParagraphWith range: NSRange) -> NSTextParagraph? { guard let originalText = textContentStorage.textStorage?.attributedSubstring(from: range) else { return nil } let updatedText = NSMutableAttributedString(attributedString: originalText) updatedText.addAttribute(.foregroundColor, value: UIColor.green, range: NSRange(location: 0, length: updatedText.length)) let paragraph = NSTextParagraph(attributedString: updatedText) // Verify that the text still contains NSTextList if let paragraphStyle = paragraph.attributedString.attribute(.paragraphStyle, at: 0, effectiveRange: nil) as? NSParagraphStyle { assert(!paragraphStyle.textLists.isEmpty) } else { assertionFailure("Paragraph has lost its text lists") } return paragraph }
Replies
0
Boosts
0
Views
469
Activity
May ’25
largeTitleDisplayMode does not work correctly in iOS 26
The following code does not work correctly for apps built with the iOS 26 beta SDK. largeTitleDisplayMode = .always // it doesn’t work ! The same program works as expected when built with the iOS 18 SDK. // iOS 26 beta largeTitleDisplayMode = .never // it works as expected largeTitleDisplayMode = .automatic // it works as expected. largeTitleDisplayMode = .always // it doesn’t work! Works same as .automatic!
Topic: UI Frameworks SubTopic: UIKit
Replies
2
Boosts
0
Views
138
Activity
Jun ’25
Use Custom UIApplication Subclass with SwiftUI
I have a SwiftUI app which needs the Ivanti AppConnect SDK. The docs only show how to integrate it into a Swift/UIKit app. But I need it to work with SwiftUI. I probably could make a UIKit base app and then load my existing SwiftUI views and code through a SwiftUI component host or something. But I'd like to avoid that if possible. Here is where I'm stuck: The AppConnect framework loads through a custom UIApplication subclass in the main.swift file: import Foundation import AppConnect UIApplicationMain( CommandLine.argc, CommandLine.unsafeArgv, ACUIApplicationClassName, NSStringFromClass(AppDelegate.self) ) The startup works as expected, and the expected function is called in the AppDelegate class: func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {...} However, the SwiftUI view is not loaded and the scree stays blank. I implemented a SceneDelegate.swift class which doesn't seem to be called. Also, the following function in the AppDelegate doesn't get called either: func application( _ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {...} So how do I bootstrap SwiftUI with a custom UIApplication class? can that be done with the @main macro somehow? I'm still pretty new to Swift and iOS development. Any help is appreciated
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Replies
0
Boosts
0
Views
92
Activity
May ’25
Failed to open URL asynchronously
I have a problem with the URL schemes under iOS 18. Data is being sent from one app to another app. The amount of data varies. It can sometimes be more than 5 MB. With iOS 18, errors often occur when sending large amounts of data. The error message is: "Failed to open URL asynchronously". If I send the data once again in this case, it works. To reproduce the error quickly, I wrote two small apps. AppA sends data to AppB. AppB calls AppA and AppA sends data to AppB again. The whole thing runs in an endless loop. Code snippet: // AppA // The file to which fileUrl points contains a 4 MB string. // The string consists of only one letter “AAAAAA....” let dataStr = try String(contentsOf: fileUrl, encoding: .utf8) if let url = URL(string: "appb://receive?data=\(dataStr)") { UIApplication.shared.open(url, options: [:]) { (result) in if !result { os_log("can't open url", type: .error) } } } // AppB DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { if let returnUrl = URL(string: "appa://return") { UIApplication.shared.open(returnUrl) } } If the test is started, the error occurs approximately 15-20 times per hour. The first error occurs very quickly if the device is restarted prior to this. As soon as the error occurs, we end up in os_log(“can't open url”, type: .error) I know the possibility of exchanging the data via AppGroups, but cannot use it in our case. Tested with following devices: // The error occurs: iPhone 11 with iOS 18.4.1 iPhone SE with iOS 18.5 // The error does not occur iPhone 8 with iOS 16.7.10 iPhone 16 simulator on a M1 MacBook (macOS 15.4.1) Unfortunately, there is no other error message in the "Console" app. Except "Failed to open URL asynchronously". There were no problems at this point between iOS 12 and iOS 17. My question is now, are there new limitations to the URL schemes under iOS 18 or is it a bug?
Topic: UI Frameworks SubTopic: UIKit Tags:
Replies
3
Boosts
0
Views
251
Activity
Jun ’25
macOS Sequoia/Xcode 16.2 - Catalyst App Missing Tab Bar
My Objective-C Catalyst app when built with Xcode 16.x/iOS 18 does not have a visible Tab Bar when run on Sequoia. App starts up in first tab, but there is no way to access other tabs. The same app when run on macOS Sonoma (or macOS Catalina) has a normal Tab Bar. The app has an initial View UITabBarController with 3 tabs. The main tab is a UiSplitViewController. Minimum macOS deployment 10.5. If app is built on Sonoma with Xcode 15.x/iOS 17 the Tab Bar is normal on macOS Sonoma, Sequoia, and Catalina. I've tried without success: if (@available(macCatalyst 18.0, *)) { self.tabBarController.tabBarHidden = false; } else { // Fallback on earlier versions } I wonder if this console log message has anything to do with the problem: CLIENT OF UIKIT REQUIRES UPDATE: This process does not adopt UIScene lifecycle. This will become an assert in a future version.
Replies
2
Boosts
0
Views
279
Activity
Jun ’25
UICollectionView with orthogonal (horizontal) section not calling touchesShouldCancel(in:)
I have a UICollectionView with horizontally scrolling sections. In the cell I have a UIButton. I need to cancel the touches when the user swipes horizontally but it does not work. touchesShouldCancel(in:) is only called when swiping vertically over the UIButton, not horizontally. Is there a way to make it work? Sample code below import UIKit class ConferenceVideoSessionsViewController: UIViewController { let videosController = ConferenceVideoController() var collectionView: UICollectionView! = nil var dataSource: UICollectionViewDiffableDataSource <ConferenceVideoController.VideoCollection, ConferenceVideoController.Video>! = nil var currentSnapshot: NSDiffableDataSourceSnapshot <ConferenceVideoController.VideoCollection, ConferenceVideoController.Video>! = nil static let titleElementKind = "title-element-kind" override func viewDidLoad() { super.viewDidLoad() navigationItem.title = "Conference Videos" configureHierarchy() configureDataSource() } } extension ConferenceVideoSessionsViewController { func createLayout() -> UICollectionViewLayout { let sectionProvider = { (sectionIndex: Int, layoutEnvironment: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection? in let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalHeight(1.0)) let item = NSCollectionLayoutItem(layoutSize: itemSize) // if we have the space, adapt and go 2-up + peeking 3rd item let groupFractionalWidth = CGFloat(layoutEnvironment.container.effectiveContentSize.width > 500 ? 0.425 : 0.85) let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(groupFractionalWidth), heightDimension: .absolute(200)) let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item]) let section = NSCollectionLayoutSection(group: group) section.orthogonalScrollingBehavior = .continuous section.interGroupSpacing = 20 section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 20) return section } let config = UICollectionViewCompositionalLayoutConfiguration() config.interSectionSpacing = 20 let layout = UICollectionViewCompositionalLayout( sectionProvider: sectionProvider, configuration: config) return layout } } extension ConferenceVideoSessionsViewController { func configureHierarchy() { collectionView = MyUICollectionView(frame: .zero, collectionViewLayout: createLayout()) collectionView.translatesAutoresizingMaskIntoConstraints = false collectionView.backgroundColor = .systemBackground view.addSubview(collectionView) NSLayoutConstraint.activate([ collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor), collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor), collectionView.topAnchor.constraint(equalTo: view.topAnchor), collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor) ]) collectionView.canCancelContentTouches = true } func configureDataSource() { let cellRegistration = UICollectionView.CellRegistration <ConferenceVideoCell, ConferenceVideoController.Video> { (cell, indexPath, video) in // Populate the cell with our item description. cell.buttonView.setTitle("Push, hold and swipe", for: .normal) cell.titleLabel.text = video.title } dataSource = UICollectionViewDiffableDataSource <ConferenceVideoController.VideoCollection, ConferenceVideoController.Video>(collectionView: collectionView) { (collectionView: UICollectionView, indexPath: IndexPath, video: ConferenceVideoController.Video) -> UICollectionViewCell? in // Return the cell. return collectionView.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item: video) } currentSnapshot = NSDiffableDataSourceSnapshot <ConferenceVideoController.VideoCollection, ConferenceVideoController.Video>() videosController.collections.forEach { let collection = $0 currentSnapshot.appendSections([collection]) currentSnapshot.appendItems(collection.videos) } dataSource.apply(currentSnapshot, animatingDifferences: false) } } class MyUICollectionView: UICollectionView { override func touchesShouldCancel(in view: UIView) -> Bool { print("AH: touchesShouldCancel view \(view.description)") if view is MyUIButton { return true } return false } } final class MyUIButton: UIButton { } class ConferenceVideoCell: UICollectionViewCell { static let reuseIdentifier = "video-cell-reuse-identifier" let buttonView = MyUIButton() let titleLabel = UILabel() override init(frame: CGRect) { super.init(frame: frame) configure() } required init?(coder: NSCoder) { fatalError() } } extension ConferenceVideoCell { func configure() { buttonView.translatesAutoresizingMaskIntoConstraints = false titleLabel.translatesAutoresizingMaskIntoConstraints = false contentView.addSubview(buttonView) contentView.addSubview(titleLabel) titleLabel.font = UIFont.preferredFont(forTextStyle: .caption1) titleLabel.adjustsFontForContentSizeCategory = true buttonView.layer.borderColor = UIColor.black.cgColor buttonView.layer.borderWidth = 1 buttonView.layer.cornerRadius = 4 buttonView.backgroundColor = UIColor.systemPink let spacing = CGFloat(10) NSLayoutConstraint.activate([ buttonView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), buttonView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), buttonView.topAnchor.constraint(equalTo: contentView.topAnchor), titleLabel.topAnchor.constraint(equalTo: buttonView.bottomAnchor, constant: spacing), titleLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), titleLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), titleLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor) ]) } }
Topic: UI Frameworks SubTopic: UIKit Tags:
Replies
1
Boosts
0
Views
108
Activity
Jun ’25
Status Bar Driving Orientation
With CarPlay, is it possible to programmatically know which side of the screen the status bar is placed on?
Topic: UI Frameworks SubTopic: UIKit Tags:
Replies
2
Boosts
0
Views
241
Activity
Jun ’25