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

SwiftUI: How do you do CoreData backup and restore?
Hi, I am trying to create a local backup + restore when using SwiftUI and CoreData but I am facing errors left and right. the latest error I am stuck on is: *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'executeFetchRequest:error: A fetch request must have an entity.' Here is what am trying to do: Creating a backup (already solved using NSPersistentStoreCoordinator.migratePersistentStore(_:to:options:type:)) Create a new NSPersistentContainer and call its NSPersistentContainer.loadPersistentStores(completionHandler:) (already solved, load is successful) Update the .environment(.managedObjectContext, viewModel.context) so that SwiftUI uses the new context. (HERE is where the error appears) Any help would be appreciated. Here is some sample code of SwiftUI part of the main view: class ViewModel: ObservableObject { @Published var context: NSManagedObjectContext } @main struct MyApp: App { @StateObject var viewModel: ViewModel var body: some Scene { WindowGroup { ContentView() .environment(\.managedObjectContext, viewModel.context) } } }
Topic: UI Frameworks SubTopic: SwiftUI
0
0
245
Mar ’25
Alerts/formsheets add 0.8 alpha tint to all vcs all views underneath!?!?!
Hello, I just noticed weird unexpected behaviour. It seems when you present UIAlertController or custom VC as partially screen covering formsheet, ALL the views underneath get 0.8 alpha tint (ios 15 and 18) Is there any way to disable this behaviour? So far it only breaks minor custom "star" view but I imagine arbitrarily adding 0.8 alpha to EVERYTHING can really mess up some layouts/designs. Regards, Martynas Stanaitis
Topic: UI Frameworks SubTopic: UIKit
1
0
151
Mar ’25
NSTextAttachment lagging in textkit 2
I have an attributedString with 100 NSTextAttachments(contains image of 400kb). When i scroll the textview, it is lagging, When i did the same in textkit 1, it is butter smooth. It can be because of how textkit 1 & 2 layout the elements. let attachment = NSTextAttachment() attachment.image = UIImage(named: "image2") let attachmentString = NSAttributedString(attachment: attachment) let mutableAttributedString = NSMutableAttributedString(attributedString: textView.attributedText) for _ in 0...100 { mutableAttributedString.append(NSAttributedString(string: "\n")) mutableAttributedString.append(attachmentString) } textView.attributedText = mutableAttributedString How to handle images in textkit 2 so that it feels smooth while scrolling textview?
1
0
487
Feb ’25
The issue of unable to use document type for Mac catalyst project
Hello, I have encountered a question that I hope to receive an answer to. Currently, I am working on a music project for Mac Catalyst and need to enable music files such as FLAC to be opened by right clicking to view my Mac Catalyst app. But currently, I have encountered a problem where I can see my app option in the right-click open mode after debugging the newly created macOS project using the following configuration. But when I created an iOS project and converted it to a Mac Catalyst app, and then modified the info.plist with the same configuration, I couldn't see my app in the open mode after debugging. May I ask how to solve this problem? Do I need to configure any permissions or features in the Mac Catalyst project? I have been searching for a long time but have not found a solution regarding it. Please resolve it, thank you. Here is the configuration of my macOS project: CFBundleDocumentTypes CFBundleTypeExtensions flac CFBundleTypeIconSystemGenerated 1 CFBundleTypeName FLAC Audio File CFBundleTypeRole Viewer LSHandlerRank Default Note: Sandbox permissions have been enabled for both the macOS project and the iOS to Mac Catalyst project. The Mac Catalyst project also has additional permissions for com. apple. security. files. user taught. read write
0
0
107
Mar ’25
On macOS SwiftUI.TimelineView() inside NSViewController is causing AutoLayout recalculations
I have a complex app that requires the main SwiftUI view of the app to be embedded inside an NSHostingView which is a subview of an NSViewController's view. Then this NSViewController is wrapped using NSViewControllerRepresentable to be presented using SwiftUI's Window. And if I have a TimelineView inside my SwiftUI view hierarchy, it causes constant recalculation of the layout. Here's a simplified demo code: @main struct DogApp: App { private let dogViewController = DogViewController() var body: some Scene { Window("Dog", id: "main") { DogViewControllerUI() } } } private struct DogViewControllerUI: NSViewControllerRepresentable { let dogViewController = DogViewController () func makeNSViewController(context: Context) -> NSViewController { dogViewController } func updateNSViewController(_ nsViewController: NSViewController, context: Context) {} func sizeThatFits(_ proposal: ProposedViewSize, nsViewController: NSViewController, context: Context) -> CGSize? { debugPrint("sizeThatFits", proposal) return nil } } public class DogViewController: NSViewController { public override func viewDidLoad() { super.viewDidLoad() let mainView = MainView() let hostingView = NSHostingView(rootView: mainView) view.addSubview(hostingView) hostingView.translatesAutoresizingMaskIntoConstraints = false hostingView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true hostingView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true hostingView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true hostingView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true } } struct MainView: View { var body: some View { VStack { TimelineView(.animation) { _ in Color.random .frame(width: 100, height: 100) } } } } extension Color { static var random: Color { Color( red: .random(in: 0...1), green: .random(in: 0...1), blue: .random(in: 0...1) ) } } When running it's printing out this repeatedly (multiple times a second). "sizeThatFits" SwiftUI.ProposedViewSize(width: Optional(559.0), height: Optional(528.0)) "sizeThatFits" SwiftUI.ProposedViewSize(width: Optional(0.0), height: Optional(0.0)) "sizeThatFits" SwiftUI.ProposedViewSize(width: Optional(559.0), height: Optional(528.0)) "sizeThatFits" SwiftUI.ProposedViewSize(width: Optional(0.0), height: Optional(0.0)) "sizeThatFits" SwiftUI.ProposedViewSize(width: Optional(559.0), height: Optional(528.0)) "sizeThatFits" SwiftUI.ProposedViewSize(width: Optional(0.0), height: Optional(0.0)) "sizeThatFits" SwiftUI.ProposedViewSize(width: Optional(559.0), height: Optional(528.0)) If I run an equivalent code for an iPad, it only prints twice. If I comment out TimelineView on macOS, then it only prints out the above logs when resizing the app window. The main reason this is an issue is that it's clearly causing dramatic degradation in performance. I was told to submit a bug report after I submitted TSI so a SwiftUI engineer could investigate it. Case-ID: 7461887. FB13810482. This was back in May but I received no response. LLMs are no help, and I've experimented with all sorts of workarounds. My last hope is this forum, maybe someone has an idea of what might be going on and why the recalculation is happening constantly on macOS.
1
0
389
Feb ’25
SwiftUI Transformable: support drag to Finder on macOS
I am trying to support dragging out a 'file' object from my app into Finder, on macOS. I have my object conform to Transferable and the files are saved on disk locally, so I just want to pass it the URL. This works fine when dragging out to other apps, like Notes or Mail, but not in Finder. I setup a ProxyRepresentation as well, as suggested by another thread, but it doesn't seem to help. Is there any other setup I need to do in the Xcode project file for it to work, or is there something else that I'm missing? @available(iOSApplicationExtension 17.0, macOSApplicationExtension 14.0, *) extension FileAttachments: Transferable { public static var transferRepresentation: some TransferRepresentation { FileRepresentation(exportedContentType: UTType.content) { content in SentTransferredFile(content.fullFileURL(), allowAccessingOriginalFile: false) } .exportingCondition { file in if let fileUTI = UTType(filenameExtension: file.fullFileURL().pathExtension), let fileURL = file.fullFileURL() { print("FileAttachments: FileRepresentation exportingCondition fileUTI: \(fileUTI) for file: \(fileURL)") return fileUTI.conforms(to: UTType.content) } return false } .suggestedFileName{$0.fileRenamedName} ProxyRepresentation { file in if let fileURL = file.fullFileURL() { print("FileAttachments: ProxyRepresentation returning file") return fileURL } return file.fullFileURL()! } } }
1
0
386
Feb ’25
How to initialize @Observable object in View via @State?
When I switched to observable, I noticed a strange behavior of the ViewModel. The ViewModel is created 3x times. And my question is: How to properly initialize the ViewModel via state? Below is a minimal example with log output: ViewModel INIT : EBBB2C41 ViewModel INIT : D8E490DA ViewModel INIT : 54407300 ViewModel DEINIT: D8E490DA @Observable final class ViewModel { @ObservationIgnored let idd: UUID init() { idd = UUID() print("ViewModel INIT : \(idd.uuidString.prefix(8))") } deinit { print("ViewModel DEINIT: \(idd.uuidString.prefix(8))") } } struct SimpleView: View { @Environment(ViewModel.self) private var viewModel var body: some View { @Bindable var viewModel = viewModel Text("SimpleView: \(viewModel.idd.uuidString.prefix(8))") } } struct ContentView: View { @State private var viewModel = ViewModel() var body: some View { SimpleView() .environment(mainViewModel) } }
1
0
444
Feb ’25
Can't find any realistic example of how to use NavigationPath
Like many applications, mine involves navigation where the user starts a process on one screen and then progresses through several more steps to reach a conclusion. When he confirms that choice, I need to dismiss the entire stack. In my case, he's browsing contacts, selecting one, and then selecting a communication method from those offered by the contact. This still appears to be a PITA in SwiftUI. NavigationPath is supposed to provide a way to programmatically control a stack of views. Well... I can't find a single example of how to use it for this, except with absurdly shallow (as in a single level) of child views that all take the same datatype. Nowhere do I see how to use the path as users proceed through your view hierarchy with NavigationLinks. I have not seen any example of how elements get added to the path or how they are related to each added view. Nor can I find an example of popping views off the stack by removing related elements from the path. I created a class that encloses a NavigationPath: @Observable class NavPathController { var path: NavigationPath init() { path = NavigationPath() } func popOne() { path.removeLast() } func popAll() { path.removeLast(path.count) } } In my root view, I pass a binding to this controller's NavigationPath when creating the NavigationStack: @State private var viewStack = NavPathController() var body: some View { NavigationStack(path: $viewStack.path) { VStack() { NavigationLink(destination: UserFindingView(viewPathController: viewStack), label: { Text("Pick a recipient") }) } } And likewise each view passes the same view-path controller object to each child view that's invoked with a NavigationLink (instead of using an environment variable, because I find those hokey). But in the end, the path is empty; not surprisingly, clearing it does not pop the views. So how is one supposed to make this work?
Topic: UI Frameworks SubTopic: SwiftUI
1
0
204
Mar ’25
business
import SwiftUI struct Product: Identifiable { let id = UUID() let name: String let pricePerKg: Double } struct ContentView: View { @State private var selectedProduct: Product? @State private var quantity: Double = 1.0 @State private var orderDate = Date() @State private var showingConfirmation = false let products = [ Product(name: "Lamb", pricePerKg: 15.0), Product(name: "Beef", pricePerKg: 20.0), Product(name: "Chicken", pricePerKg: 10.0) ] var body: some View { NavigationView { Form { Section(header: Text("Select Meat")) { Picker("Meat Type", selection: $selectedProduct) { ForEach(products) { product in Text(product.name).tag(product as Product?) } } } if let selectedProduct = selectedProduct { Section(header: Text("Quantity (kg)")) { Stepper(value: $quantity, in: 0.5...10, step: 0.5) { Text("\(quantity, specifier: "%.1f") kg") } } Section(header: Text("Delivery Date")) { DatePicker("Select Date", selection: $orderDate, in: Date()..., displayedComponents: .date) } Section(header: Text("Total Price")) { Text("$\(selectedProduct.pricePerKg * quantity, specifier: "%.2f")") } Button("Confirm Order") { showingConfirmation = true } .alert(isPresented: $showingConfirmation) { Alert(title: Text("Order Confirmed"), message: Text("You have ordered \(quantity, specifier: "%.1f") kg of \(selectedProduct.name) for \(orderDate.formatted(date: .long, time: .omitted))."), dismissButton: .default(Text("OK"))) } } } .navigationTitle("Halal Butcher") } } } @main struct HalalButcherApp: App { var body: some Scene { WindowGroup { ContentView() } } }
2
0
216
Mar ’25
How can I use specify the anchor used to display an item that a user scrolls to ?
I have a scrollview displaying a sequence of circles, which a user should be able to scroll through to select an item. When the user stops scrolling and the animation comes to rest the circle selected should display screen-centered. I had hoped to achieve this using .scrollPosition(id: selectedItem, anchor: .center) but it appears that the anchor argument is ignored when scrolled manually. (BTW - I searched but didn't locate this aspect in the Apple documentation so I'm not confident that this observation is really correct). https://youtu.be/TpXDTuL5yPQ The video shows the user-scrolling behaviour, and also the snap-to-anchor that I would like to achieve, but I would like this WITHOUT forcing a button press. I could juggle the container size and size of the circles so that they naturally fit centered into the screen, but I would prefer a more elegant solution. How can I force the scrolling to come to rest such that the circle glides to rest in the center of the screen/container? struct ItemChooser: View { @State var selectedItem: Int? var body: some View { VStack { Text("You have picked: \(selectedItem ?? 0)") ScrollHorizontalItemChooser(selectedItem: $selectedItem) } } } #Preview { ItemChooser(selectedItem: 1) } struct ScrollHorizontalItemChooser: View { @Binding var selectedItem: Int? @State var scrollAlignment: UnitPoint? = .center let ballSize: CGFloat = 150 let items = Array(1...6) @State var scrollPosition: ScrollPosition = ScrollPosition() var body: some View { VStack { squareUpButton ScrollView(.horizontal) { HStack(spacing: 10) { showBalls } .scrollTargetLayout() } .scrollPosition(id: $selectedItem, anchor: scrollAlignment ) .overlay{ crosshairs } } } var crosshairs: some View { Image(systemName: "scope").scaleEffect(3.0).opacity(0.3) } @ViewBuilder var showBalls: some View { let screenWidth: CGFloat = UIScreen.main.bounds.width var emptySpace: CGFloat {screenWidth / 2 - ballSize / 2 - 10} Spacer(minLength: emptySpace) ForEach(items, id: \.self) { item in poolBall( item) .id(item) } Spacer(minLength: emptySpace) } @ViewBuilder private func poolBall(_ item: Int) -> some View { Text("Item \(item)") .background { Circle() .foregroundColor(Color.green) .frame(width: ballSize, height: ballSize) } .frame(width: ballSize, height: ballSize) } @ViewBuilder var squareUpButton: some View { var tempSelected: Int? = nil Button("Square up with Anchor") { tempSelected = selectedItem selectedItem = 0 DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { selectedItem = tempSelected ?? 0 } } } }
Topic: UI Frameworks SubTopic: SwiftUI
1
0
229
Mar ’25
Reduce padding, spacing between list section header and search bar
Anyone know how to reduce the padding between list section header (plain style) and search bar? I have tried all available method on google but none work. The default list style does not have this big padding/space between the section header and the search bar. struct Demo: View { @State private var searchText: String = "" var body: some View { NavigationStack { List { Section { ForEach(0..<100) { index in Text("Sample value for \(index)") } } header: { Text("Header") .font(.headline) } } .listStyle(.plain) .navigationTitle("Demo") .navigationBarTitleDisplayMode(.inline) .searchable(text: $searchText) } } }
0
0
248
Feb ’25
Strange behavior when pushing UIViewController
This is a very strange behavior when pushing vc that I have never seen since I started coding. The pushed ViewController is transparent and only navBarTitle is shown. After the push, you can't control anything unless you go back to the home screen. STEPS TO REPRODUCE Long press currency change button below.(currencyWrapper) Call selectCountry and this bug happens. SourceCode let currencyWrapper = UIView() private func configureCurrencyCard(){ //The strange behavior shows up after long pressing this currencyWrapper.backgroundColor = .white currencyWrapper.addTarget(self, action: #selector(changeCurrency)) currencyWrapper.setWidth(currencyChangeIcon.follow(by: 16, x: true)) currencyWrapper.setCenterX(w1/2) currencyWrapper.setHeight(currencyLabel.follow(by: 12, x: false)) currencyWrapper.roundToCircle(true) view.addSubview(currencyWrapper) } private func selectCountry(country: Country){ let vc = CountryViewController(country: country) vc.hidesBottomBarWhenPushed = true navigationController?.pushViewController(vc, animated: true) }
Topic: UI Frameworks SubTopic: UIKit Tags:
2
0
244
Mar ’25
Animate layout change
I want to show a view, where the user can add or remove items shown as icons, which are sorted in two groups: squares and circles. When there are only squares, they should be shown in one row: [] [] [] When there are so many squares that they don’t fit horizontally, a (horizontal) scrollview will be used, with scroll-indicator always shown to indicate that not all squares are visible. When there are only circles, they also should be shown in one row: () () () When there are so many circles that they don’t fit horizontally, a (horizontal) scrollview will be used, with scroll-indicator always shown to indicate that not all circles are visible. When there a few squares and a few circles, they should be shown adjacent in one row: [] [] () () When there are so many squares and circles that they don’t fit horizontally, they should be shown in two rows, squares on top, circles below: [] [] [] () () () When there are either too many squares or too many circles (or both) to fit horizontally, one common (horizontal) scrollview will be used, with scroll-indicator always shown to indicate that not all items are visible. I started with ViewThatFits: (see first code block) { let squares = HStack { ForEach(model.squares, id: \.self) { square in Image(square) } } let circles = HStack { ForEach(model.circles, id: \.self) { circle in Image(circle) } } let oneLine = HStack { squares circles } let twoLines = VStack { squares circles } let scrollView = ScrollView(.horizontal) { twoLines }.scrollIndicators(.visible) ViewThatFits(in: .horizontal) { oneLine twoLines scrollView.clipped() } } While this works in general, it doesn’t animate properly. When the user adds or removes an image the model gets updated, (see second code block) withAnimation(Animation.easeIn(duration: 0.25)) { model.squares += image } and the view animates with the existing images either making space for a new appearing square/circle, or moving together to close the gap where an image disappeared. This works fine as long as ViewThatFits returns the same view. However, when adding 1 image leads to ViewThatFits switching from oneLine to twoLines, this switch is not animated. The circles jump to the new position under the squares, instead of sliding there. I searched online for a solution, but this seems to be a known problem of ViewThatFits. It doesn't animate when it switches... (tbc)
1
0
118
Mar ’25
What is com.apple.TextInput.rdt?
Hello, community, I'm using an HTML editor in a .NET MAUI application running on macOS, and I'm encountering some unexpected behavior during text editing: Double-click text selection disappears after approximately one second. Styles randomly revert or are applied to the wrong text unexpectedly. It appears to be related to macOS spell checking. When using editable elements (, or with contenteditable), the system enables spell checking by default. During this, MAUI attempts to communicate with a system process: com.apple.TextInput.rdt, which is not running, leading to repeated errors like: Error Domain=NSCocoaErrorDomain Code=4099 "The connection to service named com.apple.TextInput.rdt was invalidated: failed at lookup with error 3 - No such process." Question: What is com.apple.TextInput.rdt, and why might it not be running? Thank you for any help!
2
0
128
Mar ’25
SwiftData predicate for many to many relationships?
Hello, I have a Task model in my application which has an optional many to many relationship to a User model. task.assignedUsers user.tasks I am looking to construct a SwiftData predicate to fetch tasks which either have no assigned users or assigned users does not contain specific user. Here is a partially working predicate I have now: static func assignedToOthersPredicate() -> Predicate<Task> { let currentUserGUID = User.currentUserGUID return #Predicate<Task> { task in task.assignedUsers.flatMap { users in users.contains(where: { $0.guid != currentUserGUID }) } == true } } This only returns tasks assigned to others, but not those which have no assigned users. If combine it with this: static func notAssignedPredicate() -> Predicate<Task> { return #Predicate<Task> { task in task.assignedUsers == nil } } Then I get a run time crash: "to-many key not allowed here" What is the proper way to do this? Thanks.
1
0
347
Mar ’25
LiveCommunicationKit events
how can i watch the LiveCommunicationKit event? i have codes likes this: import UIKit import LiveCommunicationKit @available(iOS 17.4, *) class LiveCallKit: NSObject, ConversationManagerDelegate { @available(iOS 17.4, *) func conversationManager(_ manager: ConversationManager, conversationChanged conversation: Conversation) { } @available(iOS 17.4, *) func conversationManagerDidBegin(_ manager: ConversationManager) { } @available(iOS 17.4, *) func conversationManagerDidReset(_ manager: ConversationManager) { } @available(iOS 17.4, *) func conversationManager(_ manager: ConversationManager, perform action: ConversationAction) { switch action.state { case .idle: self.completionHandler!(InterfaceKind.reject,self.payload!) case .running: self.completionHandler!(InterfaceKind.reject,self.payload!) case .complete: self.completionHandler!(InterfaceKind.reject,self.payload!) case .failed(let reason): self.completionHandler!(InterfaceKind.reject,self.payload!) default: self.completionHandler!(InterfaceKind.reject,self.payload!) } } @available(iOS 17.4, *) func conversationManager(_ manager: ConversationManager, timedOutPerforming action: ConversationAction) { } @available(iOS 17.4, *) func conversationManager(_ manager: ConversationManager, didActivate audioSession: AVAudioSession) { } @available(iOS 17.4, *) func conversationManager(_ manager: ConversationManager, didDeactivate audioSession: AVAudioSession) { } @objc public enum InterfaceKind : Int, Sendable, Codable, Hashable { /// 拒绝/挂断 case reject /// 接听. case answer } var sessoin: ConversationManager var callId: UUID var completionHandler: ((_ actionType: InterfaceKind,_ payload: [AnyHashable : Any]) -> Void)? var payload: [AnyHashable : Any]? @objc init(icon: UIImage!) { let data:Data = icon.pngData()!; let cfg: ConversationManager.Configuration = ConversationManager.Configuration(ringtoneName: "ring.mp3", iconTemplateImageData: data, maximumConversationGroups: 1, maximumConversationsPerConversationGroup: 1, includesConversationInRecents: false, supportsVideo: false, supportedHandleTypes: Set([Handle.Kind.generic])) self.sessoin = ConversationManager(configuration: cfg) self.callId = UUID() super.init() self.sessoin.delegate = self } @objc func toIncoming(_ payload: [AnyHashable : Any], displayName: String,actBlock: @escaping(_ actionType: InterfaceKind,_ payload: [AnyHashable : Any])->Void) async { self.completionHandler = actBlock do { self.payload = payload self.callId = UUID() var update = Conversation.Update(members: [Handle(type: .generic, value: displayName, displayName: displayName)]) let actNumber = Handle(type: .generic, value: displayName, displayName: displayName) update.activeRemoteMembers = Set([actNumber]) update.localMember = Handle(type: .generic, value: displayName, displayName: displayName); update.capabilities = [ .playingTones ]; try await self.sessoin.reportNewIncomingConversation(uuid: self.callId, update: update) try await Task.sleep(nanoseconds: 2000000000); } catch { } } } i want to watch the buttons action,how should i do?
Topic: UI Frameworks SubTopic: SwiftUI
0
0
250
Mar ’25
Missing tab bar after switching tabs when tab bar is hidden in initial tab
Hi, I'm experiencing the behaviour outlined below. When I navigate programmatically on iPadOS or macOS from a tab that hides the tab bar to another tab, the tab bar remains hidden. The real app has it's entry point in UIKit (i.e. it uses an UITabBarController instead of a SwiftUI TabView) but since the problem is reproducible with a SwiftUI only app, I used one for the sake of simplicity. import SwiftUI @main struct HiddenTabBarTestApp: App { @State private var selectedIndex = 0 var body: some Scene { WindowGroup { TabView(selection: $selectedIndex) { Text("First Tab") .tabItem { Label("1", systemImage: "1.circle") } .tag(0) NavigationStack { Button("Go to first tab") { selectedIndex = 0 } .searchable(text: .constant("")) } .tabItem { Label("2", systemImage: "2.circle") } .tag(1) } } } } Reproduction: Create a new SwiftUI App with the iOS App template and use the code from above Run the app on iPadOS or macOS Navigate to the second tab Click into the search bar Click the "Go to first tab" button The tab bar is no longer visible Is this a bug in the Framework or is it the expected behaviour? If it's the expected behaviour, do you have a good solution/workaround that doesn't require me to end the search programmatically (e.g. by using @Environment(\.dismissSearch)) before navigating to another tab? The goal would be to show the tab bar in the first tab while keeping the search open in the second tab.
1
0
291
Mar ’25
SwiftUI SimultaneousGesture with RotateGesture and MagnifyGesture fails if only one gesture is recognized
I'm trying to combine a RotateGesture and a MagnifyGesture within a single SwiftUI view using SimultaneousGesture. My goal is to allow users to rotate and zoom an image (potentially at the same time). However, I’m running into a problem: If only one gesture (say, the magnification) starts and finishes without triggering the other (rotation), it seems that the rotation gesture is considered "failed." After that, no further .onChanged or .onEnded callbacks fire for either gesture until the user lifts their fingers and starts over. Here’s a simplified version of my code: struct ImageDragView: View { @State private var scale: CGFloat = 1.0 @State private var lastScale: CGFloat = 1.0 @State private var angle: Angle = .zero @State private var lastAngle: Angle = .zero var body: some View { Image("Stickers3") .resizable() .aspectRatio(contentMode: .fit) .frame(height: 100) .rotationEffect(angle, anchor: .center) .scaleEffect(scale) .gesture(combinedGesture) } var combinedGesture: some Gesture { SimultaneousGesture( RotateGesture(minimumAngleDelta: .degrees(8)), MagnifyGesture() ) .onChanged { combinedValue in if let magnification = combinedValue.second?.magnification { let minScale = 0.2 let maxScale = 5.0 let newScale = magnification * lastScale scale = max(min(newScale, maxScale), minScale) } if let rotation = combinedValue.first?.rotation { angle = rotation + lastAngle } } .onEnded { _ in lastScale = scale lastAngle = angle } } } If I pinch and rotate together (or just rotate), both gestures work as expected. But if I only pinch (or, sometimes, if the rotation amount doesn’t meet minimumAngleDelta), subsequent gestures don’t trigger the .onChanged or .onEnded callbacks anymore, as if the entire gesture sequence is canceled. I found that setting minimumAngleDelta: .degrees(0) helps because then rotation almost never fails. But I’d like to understand why this happens and whether there’s a recommended way to handle the situation where one gesture might be recognized but not the other, without losing the gesture recognition session entirely. Is there a known workaround or best practice for combining a pinch and rotate gesture where either one might occur independently, but we still want both gestures to remain active? Any insights would be much appreciated!
1
0
271
Mar ’25
NSApplicationDelegate openURLs never called
Hi, I have an existing AppKit-based Mac app that I have been working on for a few years. For a new feature, I wanted to have the app opened by a different app, so I setup the URL scheme under CFBundleURLTypes in my Info.plist, and adopted this delegate callback:   - (void)application: (NSApplication *)application openURLs:(nonnull NSArray<NSURL *> *)urls Now when I invoke the URL from the 2nd app, it opens my app correctly, BUT this delegate method isn't called. What's interesting is that if I make a totally new app with a URL scheme and adopt this delegate method, it gets called without a problem! SO what about my original project could be responsible for this 'opensURLs' method to not be called? I've been searching for a solution for a couple of days without any luck. The macOS app's target has a Deployment Target of 10.15 and I'm running this on macOS12.0 with Xcode 13.
Topic: UI Frameworks SubTopic: AppKit Tags:
2
0
809
Feb ’25
How to solve "Extra trailing closure passed in call" in a section?
Hey there, I'm new to Swift and currently building my first app. I'm having the error "Extra trailing closure passed in call" in a section and already compared it to working sections and just can't find the error in my code. Maybe you guys can help me: Form { Section("Essential Information") { TextField("Title", text: $title) TextField("Composer", text: $composer) TextField("Opus", text: $opus) } The error is occurring in the section line. There are over sections that work perfectly fine: Section("Additional Details") { TextField("Epoch", text: $epoch) TextField("Type", text: $type) TextField("Accompaniment", text: $accompaniment) TextField("Length (minutes)", value: $length, format: .number) .keyboardType(.numberPad) TextField("Key", text: $key) TextField("Difficulty", text: $difficulty) TextField("Tempo (BPM)", value: $tempo, format: .number) .keyboardType(.numberPad) }
Topic: UI Frameworks SubTopic: SwiftUI
2
0
356
Feb ’25