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

Multipeer Connectivity with iOS devices
I used the Multipeer Connectivity Framework to allow players of my game app to see the highest score along with their own score as the game progresses. It works fine running in 3 or 4 simulators in Xcode. However, I was just told by two Apple support reps that bluetooth connectivity is not allowed between two Apple devices, other than for watches, AirPods, headphones, etc. My question is: can two iPhones or iPads connect for peer-to-peer play? Can they play offline? If the answer is yes to either or both of those questions, how do I get that to happen since I get an error message when trying to connect two iPhones via bluetooth even though they can see each other in the Other Devices list? If the answer is no, then why does Apple provide a Multipeer Connectivity Framework and simulate that type of device to device connection with the Xcode simulators?
Topic: UI Frameworks SubTopic: SwiftUI
0
0
170
Feb ’25
Moving SceneDelegate to a different target
I have a SwiftUI project which has the following hierarchy: IOSSceneDelegate (App target) - depends on EntryPoint and Presentation static libs. Presentation (Static library) - Depends on EntryPoint static lib. Contains UI related logic and updates the UI after querying the data layer. EntryPoint (Static library) - Contains the entry point, AppDelegate (for its lifecycle aspects) etc. I've only listed the relevant targets here. SceneDelegate was initially present in EntryPoint library, because the AppDelegate references it when a scene is created. public func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { // Set the SceneDelegate dynamically let sceneConfig: UISceneConfiguration = UISceneConfiguration(name: "mainWindow", sessionRole: connectingSceneSession.role) sceneConfig.delegateClass = SceneDelegate.self return sceneConfig } The intent is to move the SceneDelegate to the Presentation library. When moved, the EntryPoint library fails to compile because it's referencing the SceneDelegate (as shown above). To remove this reference, I tried to set up the SceneDelegate in the old way - In the info.plist file, mention a SceneConfiguration and set the SceneDelegate in Presentation. // In the Info.plist file <key>UIApplicationSceneManifest</key> <dict> <key>UIApplicationSupportsMultipleScenes</key> <true/> <key>UISceneConfigurations</key> <dict> <key>UIWindowSceneSessionRoleApplication</key> <array> <dict> <key>UISceneConfigurationName</key> <string>Default Configuration</string> <key>UISceneDelegateClassName</key> <string>Presentation.SceneDelegate</string> </dict> </array> </dict> </dict> // In the AppDelegate public func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { // Refer to a static UISceneconfiguration listed in the info.plist file return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) } As shown above, the Presentation.SceneDelegate is referred in the Info.plist file and the reference is removed from the AppDelegate (in EntryPoint library). The app target compiles, but when I run it, the SceneDelegate is not invoked. None of the methods from the SceneDelegate (scene(_:willConnectTo:options:), sceneDidDisconnect(_:), sceneDidEnterBackground(_:) etc.) are invoked. I only get the AppDelegate logs. It seems like the Configuration is ignored because it was incorrect. Any thoughts? Is it possible to move the SceneDelegate in this situation?
0
1
399
Feb ’25
@State memory leak Xcode 16.2
Hi, A class initialized as the initial value of an @State property is not released until the whole View disappears. Every subsequent instance deinitializes properly. Am I missing something, or is this a known issue? struct ContentView: View { // 1 - init first SimpleClass instance @State var simpleClass: SimpleClass? = SimpleClass(name: "First") var body: some View { VStack { Text("Hello, world!") } .task { try? await Task.sleep(for: .seconds(2)) // 2 - init second SimpleClass instance and set as new @State // "First" should deinit simpleClass = SimpleClass(name: "Second") // 3 - "Second" deinit just fine simpleClass = nil } } } class SimpleClass { let name: String init(name: String) { print("init: \(name)") self.name = name } deinit { print("deinit: \(name)") } } output: init: First init: Second deinit: Second Thanks
0
0
269
Feb ’25
Initialize view struct with @StateObject parameter
May I inquire about the differences between the two ways of view under the hood in SwiftUI? class MyViewModel: ObservableObject { @Published var state: Any init(state: Any) { self.state = state } } struct MyView: View { @StateObject var viewModel: MyViewModel var body: some View { // ... } } struct CustomView: View { let navigationPath: NavigationPath @StateObject var viewModel: MyViewModel var body: some View { Button("Go to My View") { navigationPath.append(makeMyView()) } } } // Option 1: A viewModel is initialized outside view's initialization func makeMyView(state: Any) -> some View { let viewModel = MyViewModel(state: state) MyView(viewModel: viewModel) } // Option 2: A viewModel is initialized inside view's initialization func makeMyView(state: Any) -> some View { MyView(viewModel: MyViewModel(state: state)) } For option 1, the view model will be initialized whenever custom view is re-rendered by changes whereas the view model is only initialized once when the view is re-rendered for option 2. So what happens here?
0
0
229
Dec ’24
UIDocumentBrowserViewController - adjust order and color of navigation bar items
I'm using UIDocumentBrowserViewController. This view controller automatically creates a TabView with navigation titles and up to two trailing navigation bar items. To visualize this, open the Files app by Apple on an iPhone. I want to do the following: Add a third button and place it farthest on the trailing side. Keep all three buttons blue (the default color), but adjust the color of the navigation title to use the primary text color (it is also currently blue, by default) Button Order If my button is represented by C, then the order from left-to-right or leading-to-trailing should be A B C. I tried to add it by using additionaltrailingnavigationbarbuttonitems: class DocumentBrowserViewController: UIDocumentBrowserViewController, UIDocumentBrowserViewControllerDelegate { override func viewDidLoad() { super.viewDidLoad() let button = UIBarButtonItem(...) additionalTrailingNavigationBarButtonItems.append(button) } } This always adds it as the leftmost trailing item. The order when the view loads is C A B, where C represents my button. Here are some things I've tried: Add it in viewWillAppear - same results. Add it in viewDidAppear - same results. Add it using rightBarButtonItems - does not show up at all. insert it at: 0 instead of appending it - same results. Add it with a delay using DispatchQueue.main.async - same results. After some experimentation, I realized that the arrays referenced by additionalTrailingNavigationBarButtons and rightBarButtonItems seem to be empty, other than my own button. This is the case even if the DispatchQueue delay is so long that the view has already rendered and the two default buttons are clearly visible. So I'm not sure how to place my button relative to these, since I can't figure out where they actually are in the view controller's properties. How do I put my button farther to the trailing/right side of these two default buttons? Title Color The navigation titles created by UIDocumentBrowserViewController are blue when not in their inline format. I want them to use the primary text color instead. In viewDidLoad, I could do something like this: UINavigationBar.appearance().tintColor = UIColor.label This will change the title color to white or black, but it will also change the color of the buttons. I've tried various approaches like titleTextAttributes, and none of them seem to work with this view controller. How do I change just the color of the navigation title, and not the color of the navigation bar items?
Topic: UI Frameworks SubTopic: UIKit
0
0
349
Jan ’25
macos 15, savepanel return the wrong path when saving files occasionally
macOS: 15.0 macFUSE: 4.8.3 I am using rclone + macFUSE and mount my netdisk where it has created three subdirectories in its root directory: /user, /share, and /group. When I save a file to /[root]/user using NSSavePanel and name it test.txt, I expect the file to be saved as: /[root]/user/test.txt However, occasionally, the delegate method: - (BOOL)panel:(id)sender validateURL:(NSURL *)url error:(NSError **)outError { } returns an incorrect path: /[root]/test.txt This issue only occurs when selecting /user. The same operation works correctly for /share and /group. Is there any logs I could provide to help solving this issue? Many thanks!
Topic: UI Frameworks SubTopic: AppKit Tags:
0
0
368
Feb ’25
Detect change to apps Screen Time Access
I'm creating an app which gamifies Screen Time reduction. I'm running into an issue with apples Screen Time setting where the user can disable my apps "Screen Time access" and get around losing the game. Is there a way to detect when this setting is disabled for my app? I've tried using AuthorizationCenter.shared.authorizationStatus but this didn't do the trick. Does anyone have an ideas?
0
0
405
Feb ’25
iOS18 UIPinchGestureRecognizer finger change
Before ios18, when two fingers are switched to single fingers, the printing scale value will not change. When switching to two fingers again, the pinch and zoom printing scale will change. The same operation is performed after ios18, and two fingers are switched to single fingers. Switch back to two fingers, and the scale printing will not change. code here: - (void)pinchGesture:(UIPinchGestureRecognizer *)recognizer { NSSet <UIEvent*> *events = [recognizer valueForKey:@"_activeEvents"]; UIEvent *event = [events anyObject]; if (recognizer.state == UIGestureRecognizerStateBegan) { NSLog(@"---- begin finger count: %d scale: %f",event.allTouches.count,recognizer.scale); recognizer.scale = 1.0; } else if (recognizer.state == UIGestureRecognizerStateChanged) { NSLog(@"---- change finger count: %d scale: %f",event.allTouches.count,recognizer.scale); // recognizer.scale = 1.0; } log image for iOS 17.7 log image for ios18.0.2
Topic: UI Frameworks SubTopic: UIKit Tags:
0
0
290
Jan ’25
SFSafariViewController's preferred colors are invalidated after rotation
There are two issues about SFSafariViewController. After rotate from landscape to portrait, The topAnchor is destroyed. The specified bar tint color and control tint color are invalidated.(Returns to system color) Regarding the second issue, I’ve found a temporary workaround. Override the viewWillTransition(to:with:) and keep it empty. Don't call super.viewWillTransition(to:with:). Since UIKit is not open source, I don’t know the exact cause, but I found something that could be the key to the issue. So, I reported it to Apple Feedback Assistant. You can check the details and the sample project in the GitHub repository below. https://github.com/ueunli/SafariViewer
0
0
302
Feb ’25
*** -colorSpaceName not valid
Here is a relatively simple code fragment: let attributedQuote: [NSAttributedString.Key: Any] = [ .font: FieldFont!, .foregroundColor: NSColor.red] let strQuote = NSAttributedString.init(string:"Hello World", attributes:attributedQuote) strQuote.draw(in: Rect1) It compiles without an issue, bur when I execute it, I get: "*** -colorSpaceName not valid for the NSColor <NSColor: 0x6000005adfd0>; need to first convert colorspace." I have tried everything I can think of. What's going on?
0
1
387
Jan ’25
[iOS, SwiftUI] UiRefreshControl.tintColor is not the same as given one
Hello! I'm trying to set a UiRefreshControl.tintColor: .onAppear { UIRefreshControl.appearance().tintColor = UIColor.systemBlue } But instead of I get The color in the second picture is a high contrast version of the first one. I can't understand why it works this way. I also tried the following. UIRefreshControl.appearance().tintColor = UIColor(red: 0, green: 0.478, blue: 1, alpha: 1) // doesn't work UIRefreshControl.appearance().tintColor = UIColor(named: "RefreshControlColor") // doesn't work, here set "High contrast" on and indicated Universal.systemBlueColor Perhaps I missed something?
0
0
192
Feb ’25
Task cancellation behaviour
Hi everyone, I believe this should be a simple and expected default behavior in a real-world app, but I’m unable to make it work: 1. I have a View (a screen/page in this case) that calls an endpoint using async/await. 2. If the endpoint hasn’t finished, but I navigate forward to a DetailView, I want the endpoint to continue fetching data (i.e., inside the @StateObject ViewModel that the View owns). This way, when I go back, the View will have refreshed with the fetched data once it completes. 3. If the endpoint hasn’t finished and I navigate back to the previous screen, I want it to be canceled, and the @StateObject ViewModel should be deinitialized. I can achieve 1 and 3 using the .task modifier, since it automatically cancels the asynchronous task when the view disappears: view .task { await vm.getData() } I can achieve 1 and 2 using a structured Task in the View (or in the ViewModel, its the same behavior), for example: .onFirstAppearOnly { Task { away vm.getData() } } onFirstAppearOnly is a custom modifier that I have for calling onAppear only once in view lifecycle. Just to clarify, dont think that part is important for the purpose of the example But the question is: How can I achieve all three behaviors? Is this really such an unusual requirement? My minimum deployment target is iOS 15, and I’m using NavigationView + NavigationLink. However, I have also tried using NavigationStack + NavigationPath and still couldn’t get it to work. Any help would be much appreciated. Thank you, folks!
0
0
397
Feb ’25
iOS Architecture Research - Help a student
Hi everyone! I'm thrilled to share that I'm conducting a field research as part of my final university project, focused on iOS architecture. The goal is to dive deeper into the best practices, challenges, and trends in the iOS development world. To make this research truly impactful, I need your help! If you're an iOS developer, I’d love it if you could take a few minutes to answer a short survey. Your insights and experiences will be invaluable for my research, and I greatly appreciate your support! Here is the link: https://docs.google.com/forms/d/e/1FAIpQLSdf9cacfA7my1hnlazyl7uJraa2oTsQ7dJBWvFtZ_4vbYenRA/viewform?usp=send_form Thank you so much in advance for helping me out—feel free to share this post with others who might also be interested. Let’s build something amazing together! 💡✨
0
0
303
Feb ’25
LaunchScreen Storyboard layout issue on iPhone 16 Pro Max
I currently face an Issue where the SafeAreaInsets on the iPhone 16 Pro Max is not respected on LaunchScreens. Lets say you have an ImageView whose leading, trailing and top are equal to the SafeAreaLayoutGuides leading, trailing and top. Then you have a SwiftUI View such as the following representing the same layout in code: GeometryReader { reader in ZStack { VStack(spacing: 0) { Spacer(minLength: 0) .frame(height: reader.safeAreaInsets.top) Image(decorative: "splashLogo") Spacer(minLength: 0) } .frame(width: reader.size.width) } .edgesIgnoringSafeArea(.all) } Both the storyboard preview as well as the SwiftUI preview show identical results in Xcode. Launching the app on the device however briefly shows the image below the Dynamic Island cutout until the app is launched to the SwiftUI view. Noticed this only happening on the iPhone 16 Pro Max.
Topic: UI Frameworks SubTopic: General
0
1
358
Dec ’24
AVQueuePlayer Error: LoudnessManager.mm:709 unable to open stream for LoudnessManager plist
Getting this error in iPhone Portrait Mode with notch. Currrently using AVQueuePlayer to play more than 30 mp3 files one by one. All constraint properties are correct but error occures only in Apple iPhone Portrait Mode with notch series. But same code works on same iPhone in Landscape mode. **But I get this error: ** LoudnessManager.mm:709 unable to open stream for LoudnessManager plist Type: Error | Timestamp: 2025-02-07 | Process: | Library: AudioToolbox | Subsystem: com.apple.coreaudio | Category: aqme | TID: 0x42754 LoudnessManager.mm:709 unable to open stream for LoudnessManager plist LoudnessManager.mm:709 unable to open stream for LoudnessManager plist Timestamp: 2025-02-07 | Library: AudioToolbox | Subsystem: com.apple.coreaudio | Category: aqme
0
2
946
Feb ’25
How do I maintain a stable scroll position when inserting items above in a ScrollView?
As the title says, I am not sure how to properly build an inverted ScrollView where I can safely insert items above my data ("prepend") without everything jumping around. My current code is essentially this: @State private var scrollPosition = ScrollPosition(idType: Message.ID.self) private func onMessageDidScrollIntoView(_ id: Message.ID) { let indexOfVisibleMessage = /* ... */ if indexOfVisibleMessage < 10 { fetchOlderMessages() // ^ this updates my ViewModel `messages` } } var body: some View { ScrollView { LazyVStack { ForEach(messages) { message in MessageCell(message) } }.scrollTargetLayout() } .defaultScrollAnchor(.bottom) .scrollPosition($scrollPosition) .onChange(of: scrollPosition) { oldValue, newValue in guard let visibleMessageId = scrollPosition.viewID(type: Message.ID.self) else { return } onMessageDidScrollIntoView(visibleMessageId) } } ..so if the user scrolls up to the oldest 10 messages, I start loading more and insert them at the top. The problem with this is that the ScrollView now jumps when new messages are inserted. This is because the ScrollView maintains it's Y position, but the content size changes since we are adding new items "above". I tried to play around with a few suggestions I found on StackOverflow, namely; Inverting the ScrollView (.scaleEffect(y: -1) on the ScrollView and then again on the MessageCell to counter it): This somehow jumped the x position of the ScrollView and completely breaks .contextMenu. Playing around with .onScrollGeometryChange to update scrollPosition.scrollTo(y:) when it's contentSize changes: This just didn't work and stopped the user scroll gesture/interaction. Setting scrollPosition to the Message.ID I want to keep stable before doing an update: This didn't do anything. But nothing actually worked for the reasons described above. How do you actually build these UIs in SwiftUI? I think an inverted ScrollView is quite a common UI, and obviously data has to be loaded lazily.
0
1
101
Apr ’25