Post

Replies

Boosts

Views

Activity

How can we performantly scroll to a target location using TextKit 2?
How can we performantly scroll to a target location using TextKit 2? Hi everyone, I'm building a custom text editor using TextKit 2 and would like to scroll to a target location efficiently. For instance, I would like to move to the end of a document seamlessly, similar to how users can do in standard text editors by using CMD + Down. Background: NSTextView and TextEdit on macOS can navigate to the end of large documents in milliseconds. However, after reading the documentation and experimenting with various ideas using TextKit 2's APIs, it's not clear how third-party developers are supposed to achieve this. My Code: Here's the code I use to move the selection to the end of the document and scroll the viewport to reveal the selection. override func moveToEndOfDocument(_ sender: Any?) { textLayoutManager.ensureLayout(for: textLayoutManager.documentRange) let targetLocation = textLayoutManager.documentRange.endLocation let beforeTargetLocation = textLayoutManager.location(targetLocation, offsetBy: -1)! textLayoutManager.textViewportLayoutController.layoutViewport() guard let textLayoutFragment = textLayoutManager.textLayoutFragment(for: beforeTargetLocation) else { return } guard let textLineFragment = textLayoutFragment.textLineFragment(for: targetLocation, isUpstreamAffinity: true) else { return } let lineFrame = textLayoutFragment.layoutFragmentFrame let lineFragmentFrame = textLineFragment.typographicBounds.offsetBy(dx: 0, dy: lineFrame.minY) scrollToVisible(lineFragmentFrame) } While this code works as intended, it is very inefficient because ensureLayout(_:) is incredibly expensive and can take seconds for large documents. Issues Encountered: In my attempts, I have come across the following two issues. Estimated Frames: The frames of NSTextLayoutFragment and NSTextLineFragment are approximate and not precise enough for scrolling unless the text layout fragment has been fully laid out. Laying out all text is expensive: The frames become accurate once NSTextLayoutManager's ensureLayout(for:) method has been called with a range covering the entire document. However, ensureLayout(for:) is resource-intensive and can take seconds for large documents. NSTextView, on the other hand, accomplishes the same scrolling to the end of a document in milliseconds. I've tried using NSTextViewportLayoutController's relocateViewport(to:) without success. It's unclear to me whether this function is intended for a use case like mine. If it is, I would appreciate some guidance on its proper usage. Configuration: I'm testing on macOS Sonoma 14.5 (23F79), Swift (AppKit), Xcode 15.4 (15F31d). I'm working on a multi-platform project written in AppKit and UIKit, so I'm looking for either a single solution that works in both AppKit and UIKit or two solutions, one for each UI framework. Question: How can third-party developers scroll to a target location, specifically the end of a document, performantly using TextKit 2? Steps to Reproduce: The issue can be reproduced using the example project (download from link below) by following these steps: Open the example project. Run the example app on a Mac. The example app shows an uneditable text view in a scroll view. The text view displays a long text. Press the "Move to End of Document" toolbar item. Notice that the text view has scrolled to the bottom, but this took several seconds (~3 seconds on my MacBook Pro 16-inch, 2021). The duration will be shown in Xcode's log. You can open the ExampleTextView.swift file and find the implementation of moveToEndOfDocument(_:). Comment out line 84 where the ensureLayout(_:) is called, rerun the app, and then select "Move to End of Document" again. This time, you will notice that the text view moves fast but does not end up at the bottom of the document. You may also open the large-file.json in the project, the same file that the example app displays, in TextEdit, and press CMD+Down to move to the end of the document. Notice that TextEdit does this in mere milliseconds. Example Project: The example project is located on GitHub: https://github.com/simonbs/apple-developer-forums/tree/main/how-can-we-performantly-scroll-to-a-target-location-using-textkit-2 Any advice or guidance on how to achieve this with TextKit 2 would be greatly appreciated. Thanks in advance! Best regards, Simon
10
9
1.6k
Aug ’24
How do I Developer ID-sign and export a macOS app with an entitlement through the command-line without being signed in with my Apple ID in Xcode?
Hi, I'm trying to Developer ID-sign and export a macOS app through my CI/CD pipeline on GitHub Actions, but I've run into an issue signing my app once it has one or more entitlements, in this case, an app group entitlement. I'm using xcodebuild to archive the app and export that archive, signing it along the way. This works fine as long as my app does not have an entitlement, but once it has an entitlement, exporting the archive fails. To elaborate a bit on this, I first make sure the development certificate and the Developer ID certificate are installed on the machine. These are installed using the security CLI, and I'm confident that they are installed correctly. Then I proceed to archive the app using xcodebuild as shown below. xcodebuild archive\ -scheme MyApp\ -configuration Release\ -sdk macosx\ -archivePath MyApp.xcarchive\ -destination "platform=OS X,arch=x86_64"\ -allowProvisioningUpdates\ -authenticationKeyIssuerID XYZ1234\ -authenticationKeyID XYZ\ -authenticationKeyPath AuthKey.p8 Then I export the archive using xcodebuild as shown below. xcodebuild -exportArchive\ -archivePath MyApp.xcarchive\ -exportPath export\ -exportOptionsPlist ExportOptions.plist\ -allowProvisioningUpdates\ -authenticationKeyIssuerID XYZ1234\ -authenticationKeyID XYZ\ -authenticationKeyPath AuthKey.p8 When my app has one or more entitlements, this fails with the following error message: Error: error: exportArchive: Cloud signing permission error Error: error: exportArchive: No profiles for 'com.example.MyApp' were found Note that I am not signed into an Apple ID in Xcode when signing and exporting my app through my CI/CD pipeline, as there does not seem to be a way to sign into an Apple ID in Xcode through the CLI. Instead, I'm authenticating with an App Store Connect API key. Developer ID-signing and exporting my app does work when I do it through Xcode, even if the app has an entitlement, so the signing of the app is configured correctly. Upon inspecting the contents of ~/Library/MobileDevice/Provisioning Profiles after signing and exporting the app through Xcode, I notice that Xcode automatically creates two provisioning profiles with the titles: Mac Team Provisioning Profile: com.example.MyApp Mac Team Direct Provisioning Profile: com.example.MyApp These two provisioning profiles are not created when signing and exporting the app through the xcodebuild command-line tool, and I suspect that is part of the problem. I'd be OK with manually creating these provisioning profiles through Apple's developer portal and installing them as part of my CI/CD pipeline, but I can't seem to find a way to create this type of provisioning profile through the portal. Neither the macOS App Development, Mac App Store Connect, nor Developer ID provisioning profile types yield this kind of provisioning profile. All of this, leaves me with the question: How do I Developer ID-sign and export a macOS app with an entitlement through the command-line without being signed in with my Apple ID in Xcode?
4
0
1.4k
Apr ’24