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

A Summary of the WWDC25 Group Lab - UI Frameworks
At WWDC25 we launched a new type of Lab event for the developer community - Group Labs. A Group Lab is a panel Q&A designed for a large audience of developers. Group Labs are a unique opportunity for the community to submit questions directly to a panel of Apple engineers and designers. Here are the highlights from the WWDC25 Group Lab for UI Frameworks. How would you recommend developers start adopting the new design? Start by focusing on the foundational structural elements of your application, working from the "top down" or "bottom up" based on your application's hierarchy. These structural changes, like edge-to-edge content and updated navigation and controls, often require corresponding code modifications. As a first step, recompile your application with the new SDK to see what updates are automatically applied, especially if you've been using standard controls. Then, carefully analyze where the new design elements can be applied to your UI, paying particular attention to custom controls or UI that could benefit from a refresh. Address the large structural items first then focus on smaller details is recommended. Will we need to migrate our UI code to Swift and SwiftUI to adopt the new design? No, you will not need to migrate your UI code to Swift and SwiftUI to adopt the new design. The UI frameworks fully support the new design, allowing you to migrate your app with as little effort as possible, especially if you've been using standard controls. The goal is to make it easy to adopt the new design, regardless of your current UI framework, to achieve a cohesive look across the operating system. What was the reason for choosing Liquid Glass over frosted glass, as used in visionOS? The choice of Liquid Glass was driven by the desire to bring content to life. The see-through nature of Liquid Glass enhances this effect. The appearance of Liquid Glass adapts based on its size; larger glass elements look more frosted, which aligns with the design of visionOS, where everything feels larger and benefits from the frosted look. What are best practices for apps that use customized navigation bars? The new design emphasizes behavior and transitions as much as static appearance. Consider whether you truly need a custom navigation bar, or if the system-provided controls can meet your needs. Explore new APIs for subtitles and custom views in navigation bars, designed to support common use cases. If you still require a custom solution, ensure you're respecting safe areas using APIs like SwiftUI's safeAreaInset. When working with Liquid Glass, group related buttons in shared containers to maintain design consistency. Finally, mark glass containers as interactive. For branding, instead of coloring the navigation bar directly, consider incorporating branding colors into the content area behind the Liquid Glass controls. This creates a dynamic effect where the color is visible through the glass and moves with the content as the user scrolls. I want to know why new UI Framework APIs aren’t backward compatible, specifically in SwiftUI? It leads to code with lots of if-else statements. Existing APIs have been updated to work with the new design where possible, ensuring that apps using those APIs will adopt the new design and function on both older and newer operating systems. However, new APIs often depend on deep integration across the framework and graphics stack, making backward compatibility impractical. When using these new APIs, it's important to consider how they fit within the context of the latest OS. The use of if-else statements allows you to maintain compatibility with older systems while taking full advantage of the new APIs and design features on newer systems. If you are using new APIs, it likely means you are implementing something very specific to the new design language. Using conditional code allows you to intentionally create different code paths for the new design versus older operating systems. Prefer to use if #available where appropriate to intentionally adopt new design elements. Are there any Liquid Glass materials in iOS or macOS that are only available as part of dedicated components? Or are all those materials available through new UIKit and AppKit views? Yes, some variations of the Liquid Glass material are exclusively available through dedicated components like sliders, segmented controls, and tab bars. However, the "regular" and "clear" glass materials should satisfy most application requirements. If you encounter situations where these options are insufficient, please file feedback. If I were to create an app today, how should I design it to make it future proof using Liquid Glass? The best approach to future-proof your app is to utilize standard system controls and design your UI to align with the standard system look and feel. Using the framework-provided declarative API generally leads to easier adoption of future design changes, as you're expressing intent rather than specifying pixel-perfect visuals. Pay close attention to the design sessions offered this year, which cover the design motivation behind the Liquid Glass material and best practices for its use. Is it possible to implement your own sidebar on macOS without NSSplitViewController, but still provide the Liquid Glass appearance? While technically possible to create a custom sidebar that approximates the Liquid Glass appearance without using NSSplitViewController, it is not recommended. The system implementation of the sidebar involves significant unseen complexity, including interlayering with scroll edge effects and fullscreen behaviors. NSSplitViewController provides the necessary level of abstraction for the framework to handle these details correctly. Regarding the SceneDelagate and scene based life-cycle, I would like to confirm that AppDelegate is not going away. Also if the above is a correct understanding, is there any advice as to what should, and should not, be moved to the SceneDelegate? UIApplicationDelegate is not going away and still serves a purpose for application-level interactions with the system and managing scenes at a higher level. Move code related to your app's scene or UI into the UISceneDelegate. Remember that adopting scenes doesn't necessarily mean supporting multiple scenes; an app can be scene-based but still support only one scene. Refer to the tech note Migrating to the UIKit scene-based life cycle and the Make your UIKit app more flexible WWDC25 session for more information.
Topic: UI Frameworks SubTopic: General
0
0
814
Jun ’25
Live Q&A Summary - SwiftUI foundations: Build great apps with SwiftUI
Here’s a recap of the Live Q&A for SwiftUI foundations: Build great apps with SwiftUI. If you participated and asked questions, thank you for coming and participating! If you weren’t able to join us live we hope this recap is useful Where can I watch the VOD? Is the sample code “Wishlist” that was shown available for download? You can view the replay of the entire event here https://www.youtube.com/watch?v=Z3vloOtZLkQ The sample code for the Wishlist app will be made available in the coming weeks on the Apple Developer website, we'll send an update via email when it is available. What are the best practices when it comes to building complex navigations in SwiftUI? The developer website has documentation on navigation style best practices. Explore navigation basics like NavigationStack and TabView to get a ground-up understanding. For documentation on navigation APIs see Navigation. How can I integrate UIKit with my SwiftUI app? What about adding SwiftUI into my UIKit app? See UIKit integration: Add UIKit views to your SwiftUI app, or use SwiftUI views in your UIKit app. Both UIKit and SwiftUI provide API to show a view hierarchy of the other. For UIKit to SwiftUI, you would use UIViewControllerRepresentable. For SwiftUI to UIKit, you would use UIHostingController. Landmarks: Interfacing with UIKit walks you through step by step how to implement UIKit in SwiftUI with UIViewControllerRepresentable, and this WWDC22 video demonstrates UIHostingController, for those that want to add SwiftUI to their UIKit. Does Wishlist feature a new iOS 26 font? How can I add custom fonts and text of my app? We’re glad to hear many of you liked wide text shown in Wishlist, however, It is the default system font with some light SwiftUI styling! Check it out for yourself in the sample code when made available, and you can learn more about customizing fonts and text by seeing Font and Applying custom fonts to text. Does Xcode have a dependency graph we can use to optimize our SwiftUI Views? Xcode comes with Instruments. Instruments is the best way to figure out what is causing excessive updates and other issues with performance. That link provides direct tutorials and resources for how to use and understand. Previews also have many useful tools for analyzing SwiftUI views, for more info see Previews in Xcode Check out this video from our latest WWDC Optimize SwiftUI performance with Instruments for information on how to use Instruments to profile and optimize your app with real-world applications If you still have questions, Check out the Instruments section of these forums and create a post so the community has the opportunity to help guide you. Are there UI debugging tools to help diagnose layout issues? Yes, Xcode also features a View Debugger located by selecting the View Debug Hierarchy, pictured below. Use the View Debugger to capture and inspect your view hierarchy, identifying which views affect window sizing. The SwiftUI Inspector also lets you examine view frames and layout behavior. See Diagnosing issues in the appearance of a running app to learn about debugging visual and layout issues. As an absolute beginner, what would be the first go-to step to go for training? Do I need prior knowledge of frameworks to get started with SwiftUI? A great place to learn how to develop for Apple platforms is with Pathways! Many developers start with Develop in Swift tutorials, which exposes you to several frameworks while teaching you the basics of SwiftUI. When you're ready to take your learning further, you can read the documentation for the specific frameworks that interest you at https://developer.apple.com/documentation/.
Topic: UI Frameworks SubTopic: SwiftUI
2
0
93
1w
iOS 18.4 App updates crashes the widget and the only solution is to restart the device or change the device language
App update in which there were no changes regarding the widget. Just after it updated, the widget turns black in some cases. It also appears black in the widget gallery. Removing and adding it again did not work in this case, only after an iOS restart it works fine again This is the log 2025-03-20 02:14:05.961611 +0800 Content load failed: unable to find or unarchive file for key: [com.aa.bb::com.aa.bb.widget:cc_widget:systemMedium::360.00/169.00/23.00:(null)~(null)] on no host. The session may still produce one shortly. Error: Using url file:///private/var/mobile/Containers/Data/PluginKitPlugin/51C5E4F2-6F1F-4466-A428-73C73B9CC887/SystemData/com.apple.chrono/placeholders/cc_widget/systemMedium----360.00w--169.00h--23.00r--1f--0.00t-0.00l-0.00b0.00t.chrono-timeline ... Error Domain=NSCocoaErrorDomain Code=4 "file“systemMedium----360.00w--169.00h--23.00r--1f--0.00t-0.00l-0.00b0.00t.chrono-timeline”not exist。" UserInfo={NSFilePath=/private/var/mobile/Containers/Data/PluginKitPlugin/51C5E4F2-6F1F-4466-A428-73C73B9CC887/SystemData/com.apple.chrono/placeholders/cc_widget/systemMedium----360.00w--169.00h--23.00r--1f--0.00t-0.00l-0.00b0.00t.chrono-timeline, NSUnderlyingError=0xa693d3a80 {Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}}
0
0
123
Mar ’25
Settings.bundle in tvOS 15.0 seems to no longer work.
I have an App that builds for iOS, iPadOS, macOS and Apple TV, which was last released to all the App Stores in April. Preferences/settings are handled by the App itself except for the Apple TV variant, where I use a Settings bundle. This worked fine until tvOS 15.0, where it appears that tvOS is not updating the value of the App’s settings from NSUserDefaults when the Settings App opens. I have been working on this problem off and on for the last week and am at wits end. I’ve searched WWDC videos looking for a clue, there must be some simple change I cannot see. I’ve made clean projects for iOS and tvOS, and using the identical OBJ-C code and Settings plist entries, the iOS version works perfectly, the tvOS version fails in the simulator and on the device. I am not trying to synchronize Settings across devices, just persist across restarts on a single device. My code stores data correctly in NSUserDefaults, it simply seems that tvOS Settings App is not reading values from there for display, nor writing changes that the user makes from Settings back to user defaults. None of the types in the test projects work: TexField, Switch, Title. The test code is so simple I hesitate to include it, but the code and the NSUserDefaults key identifiers do match. This code will preset my App’s version number for Settings to display in iOS 15 but not tvOS 15. It used to work in tvOS 14: <key>DefaultValue</key> <string>DefaultVersionValue</string> <key>Type</key> <string>PSTitleValueSpecifier</string> <key>Title</key> <string>Version</string> <key>Key</key> <string>VersionKey</string> </dict> ```   NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];    [ud registerDefaults:@{      @"TextFieldKey" : @"TextFieldValue",      @"VersionKey" : @"VersionValue"    }];        [ud setObject:@"3.14" forKey:@"VersionKey"]; Any idea? Many thanks.
Topic: UI Frameworks SubTopic: General Tags:
5
0
1.4k
Mar ’25
SwiftUI subview .frame ignored on parent view appear, MacOS
When a parent view is selected for the detail pane of a NavigationSplitView subviews appear as expected but not with the dimensions set by .frame on the subview. Toggling the flag works as expected, appearing the subview with the idealWidth. I persist the flag in a SwiftData @Model class so that on restart and first appearance of the parent view the Right View subview presence is as it was left. The problem is that the .frame size is ignored, apparently. No manner of programatic view refresh seems to trigger a resize to the preferred values, only toggling the flag. Is there a better way to handle a collapsing subview in an HSplitView? Why is the .frame not respected? In this example I've added the else clause so HSplitView always has two views with .frame settings but the result is the same without it. VStack { HSplitView { VStack { Text("left view") } .frame( minWidth: 100, idealWidth: .infinity, maxWidth: .infinity, maxHeight: .infinity ) if documentSettings.nwIsPieChartShowing { VStack { Text("right view") } .frame( minWidth: 100, idealWidth: 200, maxWidth: .infinity, maxHeight: .infinity ) } else { Text("") .frame( minWidth: 0, idealWidth: 0, maxWidth: 0, maxHeight: .infinity ) } } HStack { Button("Right View", systemImage: { documentSettings.nwIsPieChartShowing ? "chart.pie.fill" : "chart.pie"}(), action: { documentSettings.nwIsPieChartShowing.toggle() } ) } } } } MacOS Sequoia 15.3.1, Xcode 16.2
Topic: UI Frameworks SubTopic: SwiftUI Tags:
2
0
274
Mar ’25
How to correctly and simply remove the edges of listStyle sidebar?
Hello, I've managed to get rid of these spaces in different ways. Using scrollview, giving negative insets, rewriting modifiers from scratch with plain style etc. But I couldn't solve this with a simple solution. I've read comments from many people experiencing similar problems online. It seems like there isn't a simple modifier to remove these spaces when we use sidebar as the list style in SwiftUI, or I couldn't find the simple solution. I wonder what's the simplest and correct way to reset these spaces? let numbers = Array(1...5) @State private var selected: Int? var body: some View { List(numbers, id: \.self, selection: $selected) { number in HStack { Text("Test") Spacer() } .frame(maxWidth: .infinity, alignment: .leading) } .listStyle(.sidebar) } }
0
0
193
Mar ’25
PhoneSceneDelegate white screen
I am currently implementing multiple scenes in my React Native / Swift application (one scene for the phone and one scene for CarPlay). I am facing an issue where one scene renders completely white (on the iPhone) but I can see in the console that the code is running (for example if I add a console.log to the App.tsx I can see that console log happen in XCode). There are no errors when building the app in XCode, and testing with the simulator CarPlay appears to render the correct output, but there is no component being rendered on the simulated phone screen (just white). AppDelegate.swift import CarPlay import React import React_RCTAppDelegate import ReactAppDependencyProvider import UIKit @main class AppDelegate: RCTAppDelegate { var rootView: UIView?; static var shared: AppDelegate { return UIApplication.shared.delegate as! AppDelegate } override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { self.moduleName = "appName" self.dependencyProvider = RCTAppDependencyProvider() self.initialProps = [:] self.rootView = self.createRootView( with: RCTBridge( delegate: self, launchOptions: launchOptions ), moduleName: self.moduleName!, initProps: self.initialProps! ); return super.application(application, didFinishLaunchingWithOptions: launchOptions) } override func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { if (connectingSceneSession.role == UISceneSession.Role.carTemplateApplication) { let scene = UISceneConfiguration(name: "CarPlay", sessionRole: connectingSceneSession.role) scene.delegateClass = CarSceneDelegate.self return scene } let scene = UISceneConfiguration(name: "Phone", sessionRole: connectingSceneSession.role) scene.delegateClass = PhoneSceneDelegate.self return scene } override func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {} override func sourceURL(for bridge: RCTBridge) -> URL? { self.bundleURL() } override func bundleURL() -> URL? { #if DEBUG RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index") #else Bundle.main.url(forResource: "main", withExtension: "jsbundle") #endif } } PhoneSceneDelegate.swift import Foundation import UIKit import SwiftUI class PhoneSceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow?; func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { if session.role != .windowApplication { return } guard let appDelegate = (UIApplication.shared.delegate as? AppDelegate) else { return } guard let windowScene = (scene as? UIWindowScene) else { return } let rootViewController = UIViewController() rootViewController.view = appDelegate.rootView; let window = UIWindow(windowScene: windowScene) window.rootViewController = rootViewController self.window = window window.makeKeyAndVisible() } } App.tsx import React, {useEffect, useState} from 'react'; import type {PropsWithChildren} from 'react'; import {CarPlay, ListTemplate} from 'react-native-carplay'; import { ScrollView, StatusBar, StyleSheet, Text, useColorScheme, View, } from 'react-native'; import { Colors, DebugInstructions, Header, LearnMoreLinks, ReloadInstructions, } from 'react-native/Libraries/NewAppScreen'; type SectionProps = PropsWithChildren<{ title: string; }>; function Section({children, title}: SectionProps): React.JSX.Element { const isDarkMode = useColorScheme() === 'dark'; return ( <View style={styles.sectionContainer}> <Text style={[ styles.sectionTitle, { color: isDarkMode ? Colors.white : Colors.black, }, ]}> {title} </Text> <Text style={[ styles.sectionDescription, { color: isDarkMode ? Colors.light : Colors.dark, }, ]}> {children} </Text> </View> ); } function App(): any { // React.JSX.Element const isDarkMode = useColorScheme() === 'dark'; const backgroundStyle = { backgroundColor: isDarkMode ? Colors.darker : Colors.lighter, }; const [carPlayConnected, setCarPlayConnected] = useState(CarPlay.connected); useEffect(() => { function onConnect() { setCarPlayConnected(true); CarPlay.setRootTemplate(new ListTemplate(/** This renders fine on the CarPlay side */)); } function onDisconnect() { setCarPlayConnected(false); } CarPlay.registerOnConnect(onConnect); CarPlay.registerOnDisconnect(onDisconnect); return () => { CarPlay.unregisterOnConnect(onConnect); CarPlay.unregisterOnDisconnect(onDisconnect); }; }); if (carPlayConnected) { console.log('car play connected'); } else { console.log('car play not connected'); } const safePadding = '5%'; // This doesn't render on the phone? return ( <View style={backgroundStyle}> <StatusBar barStyle={isDarkMode ? 'light-content' : 'dark-content'} backgroundColor={backgroundStyle.backgroundColor} /> <ScrollView style={backgroundStyle}> <View style={{paddingRight: safePadding}}> <Header/> </View> <View style={{ backgroundColor: isDarkMode ? Colors.black : Colors.white, paddingHorizontal: safePadding, paddingBottom: safePadding, }}> <Section title="Step One"> Edit <Text style={styles.highlight}>App.tsx</Text> to change this screen and then come back to see your edits. </Section> <Section title="See Your Changes"> <ReloadInstructions /> </Section> <Section title="Debug"> <DebugInstructions /> </Section> <Section title="Learn More"> Read the docs to discover what to do next: </Section> <LearnMoreLinks /> </View> </ScrollView> </View> ); } const styles = StyleSheet.create({ sectionContainer: { marginTop: 32, paddingHorizontal: 24, }, sectionTitle: { fontSize: 24, fontWeight: '600', }, sectionDescription: { marginTop: 8, fontSize: 18, fontWeight: '400', }, highlight: { fontWeight: '700', }, }); export default App; I have been attempting to get this working now for some 20+ hours with no luck with searching for answers elsewhere. I am very new to building apps with React Native and Swift so could do with some support.
0
0
362
Mar ’25
Bumpy navigation animation on back button
I was determined to fully rely on SwiftUI's navigation system giving the fact I'm starting a new project - strongly reconsidering this after a very basic requirement. Namely: navigate between my two screens while wanting to hide the back button label. One of them has a large navigation title The other one has no navigation title The problem is that back button icon simply jumps from top, a little bit down (where the title is supposed to be) and then back to top while navigating - resulting in a rough bouncy animation. struct ParentView: View { var body: some View { NavigationStack { WelcomeView() .navigationDestination(for: WelcomeRoute.self) { route in destinationView(for: route) } } } ... } struct WelcomeView: View { var body: some View { ScrollView { VStack { ... NavigationLink(value: WelcomeRoute.Routes.next) { Text("Next screen") } } } .navigationTitle("WELCOME") .navigationBarTitleDisplayMode(.large) } } struct NextWelcomeView: View { var body: some View { ScrollView { VStack { .... Text("Hello") } } .toolbarRole(.editor) } }
2
0
72
Mar ’25
Can We Detect When Running Behind a Slide Over Window?
I'm trying to determine if it’s possible to detect when a user interacts with a Slide Over window while my app is running in the background on iPadOS. I've explored lifecycle methods such as scenePhase and various UIApplication notifications (e.g., willResignActiveNotification) to detect focus loss, but these approaches don't seem to capture the event reliably. Has anyone found an alternative solution or workaround for detecting this specific state change? Any insights or recommended practices would be greatly appreciated.
0
0
123
Mar ’25
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
223
Mar ’25
How to Programmatically Simulate a Button Tap in SwiftUI?
In UIKit, certain events like a button tap can be simulated using: button.sendActions(for: .touchUpInside) This allows us to trigger the button’s action programmatically. However, in SwiftUI, there is no direct equivalent of sendActions(for:) for views like Button. What is the recommended approach to programmatically simulate a SwiftUI button tap and trigger its action? Is there an alternative mechanism to achieve this(and for other events under UIControl.event) , especially in scenarios where we want to test interactions or trigger actions without direct user input?
1
0
401
Mar ’25
App Launches on Login but Window Doesn't Appear Automatically (macOS Sequoia 15.2, Xcode 16.2)
I am developing a macOS app using SwiftUI, and I am encountering an issue when launching the app at login. The app starts as expected, but the window does not appear automatically. Instead, it remains in the Dock, and the user must manually click the app icon to make the window appear. Additionally, I noticed that the timestamp obtained during the app's initialization (init) differs from the timestamp obtained in .onAppear. This suggests that .onAppear does not trigger until the user interacts with the app. However, I want .onAppear to execute automatically upon login. Steps to Reproduce Build the app and add it to System Settings > General > Login Items as an item that opens at login. Quit the app and restart the Mac. Log in to macOS. Observe that the app starts and appears in the Dock but does not create a window. Click the app icon in the Dock, and only then does the window appear. Expected Behavior The window should be created and appear automatically upon login without requiring user interaction. .onAppear should execute immediately when the app starts at login. Observed Behavior The app launches and is present in the Dock, but the window does not appear. .onAppear does not execute until the user manually clicks the app icon. A discrepancy exists between the timestamps obtained in init and .onAppear. Sample Code Here is a minimal example that reproduces the issue: LoginTestApp.swift import SwiftUI @main struct LoginTestApp: App { @State var date2: Date init(){ date2 = Date() } var body: some Scene { WindowGroup { MainView(date2: $date2) } } } MainView.swift import SwiftUI struct MainView: View { @State var date1: Date? @Binding var date2: Date var body: some View { Text("This is MainView") Text("MainView created: \(date1?.description ?? "")") .onAppear { date1 = Date() } Text("App initialized: \(date2.description)") } } Test Environment Book Pro 13-inch, M1, 2020 macOS Sequoia 15.2 Xcode 16.2 Questions Is this expected behavior in macOS Sequoia 15.2? How can I ensure that .onAppear executes automatically upon login? Is there an alternative approach to ensure the window is displayed without user interaction?
2
0
335
Mar ’25
ActiveLabel
Hey Everyone, I can't see to ActiveLabel as it says there is no active module. Please help me. Thanks, Ben import UIKit import ActiveLabel protocol TweetCellDelegate: AnyObject { func handleProfileImageTapped(_ cell: TweetCell) func handleReplyTapped(_ cell: TweetCell) func handleLikeTapped(_ cell: TweetCell) } class TweetCell: UICollectionViewCell {
0
0
265
Mar ’25
NSLayoutManager returning inconsistent values for a glyph's text container and its line fragment rect
TLDR: NSLayoutManager's textContainer(forGlyphAt:effectiveRange:) and lineFragmentRect(forGlyphRange:effectiveRange:) are returning inconsistent results. Context: I'm developing a word processing app that paginates from an NSTextStorage using NSLayoutManager. My app uses a text attribute (.columnType) to paginate sub-ranges of the text at a time, ensuring that each columnRange gets a container (or series of containers across page breaks) to fit. This is to support both multi-column and standard full-page-width content. After any user edit, I update pagination data in my Paginator model class. I calcuate frames/sizes for the views/containers, along with what superview they belong to (page). The UI updates accordingly. In order to determine whether the columnRange has overflowed from a container due to a page break OR whether the range of text hasn't overflowed its container and is actually using less space than available and should be sized down, I call both: layoutManager.textContainer(forGlyphAt: lastGlyphOfColumn, effectiveRange: &actualGlyphRangeInContainer)` // and `layoutManager.lineFragmentRect(forGlyphAt: lastGlyphOfColumn, effectiveRange: nil) Apple Documentation notes that both these calls force glyph generation and layout. As I'm in early development, I have not set non-contiguous layout. So these should be causing full layout, assuring accurate return values. Or so I'd hoped. This does work fine in many cases. I edit. Pagination works. But then I'll encounter UI-breaking inconsistent returns from these two calls. By inconsistent, I mean that the second call returns a line fragment rect that is in the container coordinates of A DIFFERENT container than the container returned by the first call. To be specific, the line fragment rect seems to be in the coordinates of the container that comes next in layoutManager.textContainers. Example Code: if !layoutManager.textContainers.indices.contains(i) { containerToUse = createTextContainer(with: availableSize) layoutManager.addTextContainer(containerToUse) } else { // We have a container already but it may be // the wrong size. containerToUse = layoutManager.textContainers[i] if containerToUse.size.width != availableSize.width { // Mandatory that we resize if we don't have // a matching width. Height resizing is not // mandatory and requires a layout check below. containerToUse.size = availableSize } } let glyphRange = layoutManager.glyphRange(forCharacterRange: remainingColumnRange, actualCharacterRange: nil) let lastGlyphOfColumn = NSMaxRange(glyphRange) - 1 var containerForLastGlyphOfColumn = layoutManager.textContainer(forGlyphAt: lastGlyphOfColumn, effectiveRange: &actualGlyphRangeInContainer) if containerForLastGlyphOfColumn != containerToUse && containerToUse.size.height < availableSize.height { // If we are here, we overflowed the container, // BUT the container we overflowed didn't use // the maximum remaining page space (this // means it was a pre-existing container that // needs to be sized up and checked once more). // NOTE RE: THE BUG: // at this point, prints show... // containerToUse.size.height // =628 // availableSize.height // =648 containerToUse.size = availableSize containerForLastGlyphOfColumn = layoutManager.textContainer(forGlyphAt: lastGlyphOfColumn, effectiveRange: &actualGlyphRangeInContainer) } // We now check again, knowing that the container we // are testing flow into is the max size it can be. if containerForLastGlyphOfColumn != containerToUse { // If we are here, we have overflowed the // container, so containerToUse size SHOULD be // final/accurate, since it is fully used. actualCharRangeInContainer = layoutManager.characterRange(forGlyphRange: actualGlyphRangeInContainer, actualGlyphRange: nil) // Start of overflow range is the first character // in the container that was overflowed into. let overflowLoc = actualCharRangeInContainer.location remainingColumnRange = NSRange(location: overflowLoc, length: remainingColumnRange.length - overflowLoc) // Update page count as we have broken to a new page currentPage += 1 } else { // If we are here, we have NOT overflowed // from the container. BUT... // THE BUG: // ***** HERE IS THE BUG! ***** lineFragmentRectForLastChar = layoutManager.lineFragmentRect(forGlyphAt: lastGlyphOfColumn, effectiveRange: nil) let usedHeight = lineFragmentRectForLastChar.maxY // BUG: ^The lines of code above return a // fragment rect that is in the coordinates // of the WRONG text container. Prints show: // usedHeight // =14 // usedHeight shouldn't be just 14 if this is // the SAME container that, when it was 628 // high, resulted in text overflowing. // Therefore, the line fragment here seems // to be in the coordinates of the ENSUING // container that we overflowed INTO, but // that shouldn't be possible, since we're in // a closure for which we know: // // containerForLastGlyphOfColumn == containerToUse // // If the last glyph container is the container // we just had to size UP, why does the final // glyph line fragment rect have a maxY of 14!? // Including ensuing code below only for context. if usedHeight < containerToUse.size.height { // Adjust container size down to usedRect containerToUse.size = CGSize(width: containerToUse.size.width, height: usedHeight) } else if usedHeight == availableSize.height { // We didn't force break to a new page BUT // we've used exactly the height of our page // to layout this column range, so need to // break to a new page for any ensuing text // columns. currentPage += 1 } else if usedHeight > containerToUse.size.height { // We should have caught this earlier. Text // has overflowed, but this should've been // caught when we checked // containerForLastGlyphOfColumn != // containerToUse. // // Note: this error has never thrown. throw PaginationError.unknownError("Oops.") } } Per my comments in the code block above, I don't understand why the very same text container that just overflowed and so had to be sized up from 628 to 648 in order to try to fit a glyph would now report that same glyph as both being IN that same container and having a line fragment rect with a maxY of just 14. A glyph couldn't fit in a container when it was 628 high, but if I size it up to 648, it only needs 14? There's something very weird going on here. Working with NSLayoutManager is a bit of a nightmare given the unclear documentation. Any help or insight here would be massively, massively appreciated.
2
0
513
Apr ’25
Trouble using MKAnnotation on a MKMapView
Hi everyone, i'im having troubles using Annotation on a Map View. I have a a core data model called Location that conforms to NSManagedObject from CoreData and MKAnnotation form MapKit, and i'm trying to add an array of Location to a MKMview instance in a UIViewController class, by doing somethhing like this: mapView.addAnnotation(locations), but xcode compliants with a strange error which says Argument type '[Location]' does not conform to expected type 'MKAnnotation'. it's strange to me because my Location class conforms to MKAnnotation protocol and i implemented the protocol's methods (coordinate, title, subtitle). Please can anyone help me how to fix this issues. Thank you all
Topic: UI Frameworks SubTopic: UIKit Tags:
2
0
179
Mar ’25
Buttons in menu don't respect the environment value of .layoutDirection in SwiftUI
Problem Setting ".environment(.layoutDirection, .rightToLeft)" to a view programmatically won't make buttons in menu to show right to left. However, setting ".environment(.locale, .init(identifier: "he-IL"))" to a view programmatically makes buttons in menu to show Hebrew strings correctly. Development environment: Xcode 16.x, macOS 15.3.1 Target iOS: iOS 17 - iOS 18 The expected result is that the button in the menu should be displayed as an icon then a text from left to right. Code to demonstrate the problem: struct ContentView: View { var body: some View { VStack(alignment: .leading) { Text("Buttons in menu don't respect the environment value of .layoutDirection") .font(.subheadline) .padding(.bottom, 48) /// This button respects both "he-IL" of ".locale" and ".rightToLeft" of ".layoutDirection". Button { print("Button tapped") } label: { HStack { Text("Send") Image(systemName: "paperplane") } } Menu { /// This button respects "he-IL" of ".locale" but doesn't respect ".rightToLeft" of ".layoutDirection". Button { print("Button tapped") } label: { HStack { Text("Send") Image(systemName: "paperplane") } } } label: { Text("Menu") } } .padding() .environment(\.locale, .init(identifier: "he-IL")) .environment(\.layoutDirection, .rightToLeft) } }
4
0
89
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
244
Mar ’25
Remove "copy cursor" when dragging a view in SwiftUI
Hi, Im new to SwiftUI and Im trying to implement some drag and drop functionality for some tabs in my application. Im using .draggable(_) and .dropDestination for this and the issue I have is that as I drag the view, the mouse cursor changes to the copy cursor with the green plus sign and I don't like it but I can't figure out how to avoid it. Any help would be appreciated.
1
0
73
Apr ’25
SensorKit Data Not Retrieving
I have received permission from Apple to access SensorKit data for my app. I have granted all necessary permissions, but no data is being retrieved. The didCompleteFetch method is being called, but I’m unsure where to find event data like Device Usage and Ambient Light. Additionally, the didFetchResult method is never called. Could anyone please assist me in resolving this issue? Any guidance or troubleshooting steps would be greatly appreciated. import SensorKit class ViewController: UIViewController, SRSensorReaderDelegate { let store = SRSensorReader(sensor: .deviceUsageReport) override func viewDidLoad() { super.viewDidLoad() requestSensorAuthorization() } func requestSensorAuthorization() { var sensors: Set<SRSensor> = [ .accelerometer, .deviceUsageReport, .messagesUsageReport, .visits, .keyboardMetrics, .phoneUsageReport, .ambientLightSensor ] if #available(iOS 16.4, *) { sensors.insert(.mediaEvents) } SRSensorReader.requestAuthorization(sensors: sensors) { error in if let error = error { print("Authorization failed: \(error.localizedDescription)") } else { self.store.startRecording() self.requestSensorData() print("Authorization granted for requested sensors.") } } } func requestSensorData() { let fromTime = SRAbsoluteTime.fromCFAbsoluteTime(_cf: Date().addingTimeInterval(-60 * 60).timeIntervalSinceReferenceDate) let toTime = SRAbsoluteTime.fromCFAbsoluteTime(_cf: Date().timeIntervalSinceReferenceDate) let request = SRFetchRequest() request.from = fromTime request.to = toTime request.device = SRDevice.current store.fetch(request) store.delegate = self } func sensorReader(_ reader: SRSensorReader, didCompleteFetch fetchRequest: SRFetchRequest) { print("Fetch request completed: \(fetchRequest.from) to \(fetchRequest.to)") Task { do { let samples = try await reader.fetch(fetchRequest) print("Samples count: \(samples)") } catch { print("Error Fetching Data: \(error.localizedDescription)") } } } func sensorReader(_ reader: SRSensorReader, fetching fetchRequest: SRFetchRequest, didFetchResult result: SRFetchResult<AnyObject>) -> Bool { print(result) return true } }
0
0
254
Mar ’25