Post

Replies

Boosts

Views

Activity

Reply to Mac Catalyst How to Play NSBeep
NSBeep() and AudioServicesPlayAlertSound(kSystemSoundID_UserPreferredAlert) seem to play the same sound. kSystemSoundID_UserPreferredAlert isn't available under Mac Catalyst but it has a value of 0x00001000. The following code works under Mac Catalyst (at least in my tests with macOS 13.4). AudioServicesPlayAlertSound(0x00001000); // same sound as NSBeep() You may need to import AudioToolbox.
Topic: App & System Services SubTopic: General Tags:
Jul ’23
Reply to Reading Codable properties raised a fatal error.
It seems that SwiftData and Codable model properties are seriously broken (at least as of Xcode 15.0 beta 3). I'm getting the same error shown in the above question for any model property whose type is anything marked as Codable. I am finding that in many cases the custom encode method is never called when storing a value in an instance of the model. And when reading the property, the init(from:) isn't encoded as expected. I was able to get things to work for CLLocationCoordinate2D with the following implementation: extension CLLocationCoordinate2D: Codable { // These exact case values must be used to decode a location enum CodingKeys: CodingKey { case latitude case longitude } public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) let lat = try container.decode(CLLocationDegrees.self, forKey: .latitude) let lon = try container.decode(CLLocationDegrees.self, forKey: .longitude) self = CLLocationCoordinate2D(latitude: lat, longitude: lon) } // Never called by SwiftData public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(self.latitude, forKey: .latitude) try container.encode(self.longitude, forKey: .longitude) } }
Jul ’23
Reply to How do you get the cursor to appear programmatically in a custom UITextInput with UITextInteraction?
After lots of digging, I've finally come up with a solution that works under iOS 15 - iOS 17 (tested with iOS 15.4, 16.4, 17.4, and 17.5). Under iOS 17, the use of UITextSelectionDisplayInteraction activated only worked if my custom UITextInput already had some text in it. I don't know fi that's an issue with my custom input view or not but since my hack for iOS 15/16 also worked with iOS 17 I didn't spend any time trying to figure it out. I created an extension to UITextInput that add a method to activate the cursor that you can call from the becomeFirstResponder of the custom input view. It makes use of a class that fakes a tap gesture enough to make the code work. This is an ugly hack that makes use of several private APIs. It works in development. I've made no attempt to use this code in a production App Store app. @objcMembers class MyFakeTap: NSObject { private let myView: UIView init(view: UIView) { self.myView = view super.init() } func tapCount() -> Int { return 1 } func touchesForTap() -> [UITouch] { return [] } var view: UIView? { myView } var state: Int { get { return 1 } set { } } func locationInView(_ view: UIView?) -> CGPoint { return .init(x: 5, y: 5) } } extension UITextInput { private var textSelectionInteraction: UITextInteraction? { if let clazz = NSClassFromString("UITextSelectionInteraction") { return textInputView?.interactions.first { $0.isKind(of: clazz) } as? UITextInteraction } else { return nil } } func activateCursor() { if let textSelectionInteraction { let tap = MyFakeTap(view: self.textInputView ?? UIView()) textSelectionInteraction.perform(NSSelectorFromString("_handleMultiTapGesture:"), with: tap) } } } Note that under iOS 15, if you call activateCursor() from becomeFirstResponder, be sure you only do so if the view is not already the first responder. Otherwise you will end up in an infinite loop of calls to becomeFirstResponder. override func becomeFirstResponder() -> Bool { // Under iOS 15 we end up in an infinite loop due to activeCursor indirectly calling becomeFirstResponder. // So don't go any further if already the first responder. if isFirstResponder { return true } let didBecomeResponder = super.becomeFirstResponder() if didBecomeResponder { self.activateCursor() } return didBecomeResponder }
Topic: UI Frameworks SubTopic: UIKit Tags:
May ’24
Reply to Does UITextInteraction have a way to dismiss the edit menu?
I created an extension to UITextInput that adds methods to let you show/hide the menu. This assumes the custom UITextInput is making use of UITextInteraction. The following works for iOS 15+ (tested with iOS 15.4, 16.4, 17.4, and 17.5). This has only been tested in development. It arguably uses a private API so I don't know (yet) if this will be approved for App Store apps. extension UITextInput { @available(iOS 16.0, *) var editMenuInteraction: UIEditMenuInteraction? { return textInputView?.interactions.first { $0 is UIEditMenuInteraction } as? UIEditMenuInteraction } func showEditMenu() { if let textInputView { if #available(iOS 16.0, *) { // There's no direct API to show the menu. Normally you setup a UIEditMenuInteraction but the // UITextInteraction sets up its own. So we need to find that interaction and call its // presentEditMenu with a specially crafted UIEditMenuConfiguration. if let interaction = self.editMenuInteraction { if let selectedRange = self.selectedTextRange { let rect = self.firstRect(for: selectedRange) if !rect.isNull { let pt = CGPoint(x: rect.midX, y: rect.minY - textInputView.frame.origin.y) // !!!: Possible future failure // This magic string comes from: // -[UITextContextMenuInteraction _querySelectionCommandsForConfiguration:suggestedActions:completionHandler:] // It can be seen by looking at the assembly for the _querySelectionCommandsForConfiguration method. // About 24 lines down is a reference to this string literal. let cfg = UIEditMenuConfiguration(identifier: "UITextContextMenuInteraction.TextSelectionMenu", sourcePoint: pt) interaction.presentEditMenu(with: cfg) } } } } else { if let selectedRange = self.selectedTextRange { let rect = self.firstRect(for: selectedRange) if !rect.isNull { UIMenuController.shared.showMenu(from: textInputView, rect: rect.insetBy(dx: 0, dy: -textInputView.frame.origin.y)) } } } } } func hideEditMenu() { if let textInputView { if #available(iOS 16.0, *) { if let interaction = self.editMenuInteraction { interaction.dismissMenu() } } else { if UIMenuController.shared.isMenuVisible { UIMenuController.shared.hideMenu(from: textInputView) } } } } }
Topic: UI Frameworks SubTopic: UIKit Tags:
May ’24
Reply to UI Scene
Start with the UIKit documentation. There you will find a link for App and Environment. That in turn has a link for Managing your app’s life cycle and Scenes.
Topic: UI Frameworks SubTopic: UIKit
Jun ’25
Reply to Icon Composer icons together with iOS 18 icons
In my existing app, after opening the project in Xcode 26 and adding the new glass icon file (AppIconGlass.icon in my case) to the project, I needed to do the following: Select your target Select the General tab Scroll down to the "App Icons and Launch Screen" section. In the "App Icon" field, enter the name of the new glass icon, minus the extension (AppIconGlass in my case). Enable the "App Icons Source - Include all app icon assets" option. That's it. Just make sure you do not have any manual entries in your Info.plist related to the app icons. Xcode will add them automatically as part of the build. And it will do so in a way that shows the new glass icon for iOS 26 and the previous app icons for iOS 18 and earlier.
Jun ’25
Reply to macOS 26 beta: Natural Scrolling (disable) option removed from System Settings?
I'm running a MacBook Pro with a built in trackpad on macOS 15.5. I just used UTM to create a VM for macOS 26. Initially the Settings app in the macOS 26 VM did not show either a mouse or trackpad. I shutdown the VM. Then in UTM I highlighted the macOS 26 VM on the left and then clicked the settings icon (three little sliders icon) in the top-right corner of UTM. Then select Virtualization. The Pointer setting was set to Mac Trackpad. I changed it to Generic Mouse. Then I saved the change and launched the VM. I brought up the Settings app in the running macOS 26 VM and I then I saw Mouse between Keyboard and Printers & Scanners. I selected Mouse to bring up the mouse settings which includes the Natural Scrolling toggle. I turned it off and life was good again. I don't know if the above will work with your Mac and mouse but it worked with my Mac and trackpad. To me there is nothing "natural" about "Natural Scrolling". Too many decades of muscle memory of proper scrolling. :)
Topic: App & System Services SubTopic: Core OS Tags:
Jun ’25
Reply to How do I take an Icon Composer file and bring it into Xcode?
To add the new icon file to your project, go to the Xcode project navigator and right-click on the folder that you want to put the icon file in. It doesn't need to be in any specific folder in your project. Choose "Add Files to "MyProject"... from the context menu. Then select and add the new icon file. Once the icon file is added to your project, see https://developer.apple.com/forums/thread/787576 for a thread on setting up your target to use the new icon. It also explains how use both the new and the old icons (assuming you want to support macOS/iOS 26 as well as prior versions).
Jun ’25