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

Created

iOS app crash at -[UIView _wrappedProcessTraitChanges:withBehavior:] + 1288 (UIView.m:0)
Hello Apple engineers, could you help me understand whether this crash is a UIKit bug or something in our code that causes it. Based on the documentation it is an invalid fetch instruction, that's why I suspect UIKit. I've found a similar crash here on the forums reported a year ago, it seemed to be a UIKit bug - https://forums.developer.apple.com/forums/thread/729448. I've attached the full crash report (the app name was replaced with ): Thread 0 Crashed: 0 libobjc.A.dylib 0x000000019daf7c20 objc_msgSend + 32 (:-1) 1 UIKitCore 0x00000001a3020c50 -[UIView _wrappedProcessTraitChanges:withBehavior:] + 1288 (UIView.m:0) 2 UIKitCore 0x00000001a3020720 -[UIView _processChangesFromOldTraits:toCurrentTraits:withBehavior:] + 196 (UIView.m:7840) 3 UIKitCore 0x00000001a3020618 -[UIView _updateTraitCollectionAndProcessChangesWithBehavior:previousCollection:] + 112 (UIView.m:7831) 4 UIKitCore 0x00000001a2fa90c0 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 944 (UIView.m:19850) 5 QuartzCore 0x00000001a22dfc28 CA::Layer::layout_if_needed(CA::Transaction*) + 496 (CALayer.mm:10944) 6 QuartzCore 0x00000001a22df7b4 CA::Layer::layout_and_display_if_needed(CA::Transaction*) + 148 (CALayer.mm:2638) 7 QuartzCore 0x00000001a2336914 CA::Context::commit_transaction(CA::Transaction*, double, double*) + 472 (CAContextInternal.mm:2613) 8 QuartzCore 0x00000001a22b57c4 CA::Transaction::commit() + 648 (CATransactionInternal.mm:420) 9 QuartzCore 0x00000001a22f8a0c CA::Transaction::flush_as_runloop_observer(bool) + 88 (CATransactionInternal.mm:928) 10 UIKitCore 0x00000001a303f568 _UIApplicationFlushCATransaction + 52 (UIApplication.m:3326) 11 UIKitCore 0x00000001a303cb64 __setupUpdateSequence_block_invoke_2 + 332 (_UIUpdateScheduler.m:1652) 12 UIKitCore 0x00000001a303c9d8 _UIUpdateSequenceRun + 84 (_UIUpdateSequence.mm:136) 13 UIKitCore 0x00000001a303c628 schedulerStepScheduledMainSection + 172 (_UIUpdateScheduler.m:1171) 14 UIKitCore 0x00000001a303d59c runloopSourceCallback + 92 (_UIUpdateScheduler.m:1334) 15 CoreFoundation 0x00000001a080c328 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 28 (CFRunLoop.c:1970) 16 CoreFoundation 0x00000001a080c2bc __CFRunLoopDoSource0 + 176 (CFRunLoop.c:2014) 17 CoreFoundation 0x00000001a0809dc0 __CFRunLoopDoSources0 + 244 (CFRunLoop.c:2051) 18 CoreFoundation 0x00000001a0808fbc __CFRunLoopRun + 840 (CFRunLoop.c:2969) 19 CoreFoundation 0x00000001a0808830 CFRunLoopRunSpecific + 588 (CFRunLoop.c:3434) 20 GraphicsServices 0x00000001ec7e81c4 GSEventRunModal + 164 (GSEvent.c:2196) 21 UIKitCore 0x00000001a336eeb0 -[UIApplication _run] + 816 (UIApplication.m:3844) 22 UIKitCore 0x00000001a341d5b4 UIApplicationMain + 340 (UIApplication.m:5496) 23 UIKitCore 0x00000001a3757fa8 UIApplicationMain(_:_:_:_:) + 104 (UIKit.swift:565) 24 <Redacted> 0x00000001028bde64 specialized static UIApplicationDelegate.main() + 28 (/<compiler-generated>:16) 25 <Redacted> 0x00000001028bde64 static AppDelegate.$main() + 28 (AppDelegate.swift:0) 26 <Redacted> 0x00000001028bde64 main + 116 27 dyld 0x00000001c61f6ec8 start + 2724 (dyldMain.cpp:1334) 2024-12-27_20-53-28.2129_-0500-353aaa194e6232c0d1fae767296bdb8c47c30498.crash
3
1
657
Dec ’24
LiveActivity on device with Dynamic Island
I am developing a live activity for my app and am running into issues on devices with dynamic islands. The live activity will randomly get in a state where it displays a spinner and does not update. For devices without dynamic islands, the activity works perfectly fine. I have tried breaking down the views to determine the root cause, but at times it seems very random. These are all TestFlight builds as we are currently developing the feature. I have tried using the console app and looking at the various processes that have been called out in other threads and cannot see any actual errors being logged. Looking for any pointers on how to pinpoint the root cause of this issue.
2
1
564
Jan ’25
SwiftUI toolbar with IDs crash since macOS 15
I understand this is a known issue, but it’s truly unacceptable that it remains unresolved. Allowing users to customize toolbars is a fundamental macOS feature, and it has been broken since the release of macOS 15. How is it possible that this issue persists even in macOS 15.3 beta (24D5040f)? FB15513599 import SwiftUI struct ContentView: View { @State private var showEditItem = false var body: some View { VStack { VStack { Text("Instructions to reproduce the crash") .font(.title) .padding() Text(""" 1. Click on "Toggle Item" 2. In the menu go to File > New Window 3. In new window, click on "Toggle Item" """) } .padding() Button { showEditItem.toggle() } label: { Text("Toggle Item") } } .padding() .toolbar(id: "main") { ToolbarItem(id: "new") { Button { } label: { Text("New…") } } if showEditItem { ToolbarItem(id: "edit") { Button { } label: { Text("Edit…") } } } } } }
7
3
709
Jan ’25
Library not loaded: /System/Library/Frameworks/SwiftUICore.framework/SwiftUICore
Hello. I'm building an iOS application that builds a framework named Design.framework using Xcode 16.1. When I launch my application on iOS 17.X, i get the following runtime error dyld[47899]: Library not loaded: /System/Library/Frameworks/SwiftUICore.framework/SwiftUICore Referenced from: <E625A246-E36B-351A-B077-CC38BAB5E07B> /Users/v/Library/Developer/Xcode/DerivedData/iScribd-bexojbdreldwkvbpzioqueldnvng/Build/Products/Debug_AppStore-iphonesimulator/Design.framework/Design Reason: tried: '/Users/v/Library/Developer/Xcode/DerivedData/iScribd-bexojbdreldwkvbpzioqueldnvng/Build/Products/Debug_AppStore-watchsimulator/SwiftUICore.framework/SwiftUICore' (no such file), '/Users/v/Library/Developer/Xcode/DerivedData/iScribd-bexojbdreldwkvbpzioqueldnvng/Build/Products/Debug_AppStore-iphonesimulator/SwiftUICore.framework/SwiftUICore' (no such file), '/Users/v/Library/Developer/Xcode/DerivedData/iScribd-bexojbdreldwkvbpzioqueldnvng/Build/Products/Debug_AppStore-watchsimulator/PackageFrameworks/SwiftUICore.framework/SwiftUICore' (no such file), '/Users/v/Library/Developer/Xcode/DerivedData/iScribd-bexojbdreldwkvbpzioqueldnvng/Build/Products/Debug_AppStore-iphonesimulator/PackageFrameworks/SwiftUICore.framework/SwiftUICore' (no such file), '/Library/Developer/CoreSimulator/Volumes/iOS_21C62/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 17.2.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/SwiftUICore.framework/SwiftUICore' (no such file), '/System/Library/Frameworks/SwiftUICore.framework/SwiftUICore' (no such file, not in dyld cache), '/Library/Developer/CoreSimulator/Volumes/iOS_21C62/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 17.2.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/SwiftUICore.framework/SwiftUICore' (no such file) This error does not happen on iOS 18+. When i run dyld_info Design.framework/Design, i see that there is indeed a dependency on SwiftUICore.framework Design.framework/Design [arm64]: -platform: platform minOS sdk iOS-simulator 16.0 18.1 -uuid: 09880F25-DC86-3D8E-BCAE-1A283508D879 -segments: load-offset segment section sect-size seg-size max-prot init-prot 0x00000000 __TEXT 416KB r.x 0x00000000 __TEXT 416KB r.x 0x0000543C __text 355508 0x0005C0F0 __stubs 3168 0x0005CD50 __objc_methlist 32 0x0005CD70 __const 8296 0x0005EDD8 __swift5_typeref 15820 0x00062BB0 __cstring 6885 0x00064698 __constg_swiftt 2804 0x0006518C __swift5_builtin 40 0x000651C0 __swift5_reflstr 2952 0x00065D48 __swift5_assocty 816 0x00066078 __objc_methname 299 0x000661A4 __swift5_proto 368 0x00066314 __swift5_types 280 0x0006642C __swift5_fieldmd 3688 0x00067294 __swift5_protos 8 0x0006729C __swift5_capture 244 0x00067390 __unwind_info 2296 0x00067C88 __eh_frame 888 0x00068000 __DATA_CONST 16KB rw. 0x00068000 __DATA_CONST 16KB rw. 0x00068000 __got 3704 0x00068E78 __const 6864 0x0006A948 __objc_classlist 40 0x0006A970 __objc_imageinfo 8 0x0006C000 __DATA 32KB rw. 0x0006C000 __DATA 32KB rw. 0x0006C000 __objc_const 792 0x0006C318 __objc_selrefs 96 0x0006C378 __objc_classrefs 48 0x0006C3A8 __objc_data 208 0x0006C478 __data 4136 0x0006D4A0 __bss 12784 0x00070690 __common 4592 -linked_dylibs: attributes load path @rpath/CommonTools.framework/CommonTools /System/Library/Frameworks/Foundation.framework/Foundation /usr/lib/libobjc.A.dylib /usr/lib/libSystem.B.dylib /System/Library/Frameworks/CoreFoundation.framework/CoreFoundation /System/Library/Frameworks/CoreGraphics.framework/CoreGraphics /System/Library/Frameworks/CoreText.framework/CoreText weak-link /System/Library/Frameworks/DeveloperToolsSupport.framework/DeveloperToolsSupport /System/Library/Frameworks/SwiftUI.framework/SwiftUI /System/Library/Frameworks/SwiftUICore.framework/SwiftUICore /System/Library/Frameworks/UIKit.framework/UIKit /usr/lib/swift/libswiftCore.dylib weak-link /usr/lib/swift/libswiftCoreAudio.dylib /usr/lib/swift/libswiftCoreFoundation.dylib weak-link /usr/lib/swift/libswiftCoreImage.dylib weak-link /usr/lib/swift/libswiftCoreMedia.dylib weak-link /usr/lib/swift/libswiftDarwin.dylib weak-link /usr/lib/swift/libswiftDataDetection.dylib weak-link /usr/lib/swift/libswiftDispatch.dylib weak-link /usr/lib/swift/libswiftFileProvider.dylib weak-link /usr/lib/swift/libswiftMetal.dylib weak-link /usr/lib/swift/libswiftOSLog.dylib /usr/lib/swift/libswiftObjectiveC.dylib weak-link /usr/lib/swift/libswiftQuartzCore.dylib weak-link /usr/lib/swift/libswiftSpatial.dylib weak-link /usr/lib/swift/libswiftUniformTypeIdentifiers.dylib weak-link /usr/lib/swift/libswiftXPC.dylib /usr/lib/swift/libswift_Concurrency.dylib weak-link /usr/lib/swift/libswiftos.dylib weak-link /usr/lib/swift/libswiftsimd.dylib weak-link /usr/lib/swift/libswiftUIKit.dylib I can add a -weak_framework SwiftUICore to "Other Linker Flags" in my Design.framework Build Settings, and that will solve my problem, but I'd like to know what prompts my Design.framework to depend on SwiftUICore despite having a minimum set to iOS 16? And how am I supposed to handle devices prior to iOS 18 which do not have this library? is adding the linker flag the right way forward?
Topic: UI Frameworks SubTopic: SwiftUI
5
0
1.5k
Jan ’25
Mac Catalyst SwiftUI – . focused() not working
Hello, given this following simple SwiftUI setup: struct ContentView: View { var body: some View { CustomFocusView() } } struct CustomFocusView: View { @FocusState private var isFocused: Bool var body: some View { color .frame(width: 128, height: 128) .focusable(true) .focused($isFocused) .onTapGesture { isFocused.toggle() } .onKeyPress("a") { print("A pressed") return .handled } } var color: Color { isFocused ? .blue : .red } } If I run this via Mac – Designed for iPad, the CustomFocusView toggles focus as expected and cycles through red and blue. Now if I run this same exact code via Mac Catalyst absolutely nothing happens and so far I wasn't able to ever get this view to accept focused state. Is this expected? I would appreciate if anyone could hint me on how to get this working. Thank and best regards!
5
0
636
Jan ’25
Keyboard doesn't close if QuickType bar is open for password auto-fill in SwiftUI when using associated domains.
Description Our app (and sample app) are using associated domains to support deep linking. As an unintended side effect we get full support for auto-filling passwords with the QuickType bar on our login screen. However, after the username and password fields are filled and the user taps the Login button, the keyboard stays on screen. We have tried everything I can think of including @FocusState UIKit resignFirstResponder, and many other iterations of testing. Our login screen is in a fullscreencover or sheet. When the sheet dismisses the keyboard stays. In my sample app if I use a navigation stack and push the next view onto the stack, the keyboard closes. I can't provide a useful video because the iOS screen recorder will hide the keyboard when focus is in a SecureField. Note: If we remove the associated domain from the project everything works as expected. Code Example struct ContentView: View { @State private var name: String = "" @State private var password: String = "" @State private var showLogin = false @FocusState private var isFocused: Bool var body: some View { VStack { Button("Login") { showLogin.toggle() } } .fullScreenCover(isPresented: $showLogin) { VStack { TextField("Enter your name", text: $name) .textFieldStyle(.roundedBorder) .focused($isFocused) SecureField("Enter password", text: $password) .autocapitalization(.none) .autocorrectionDisabled(true) .textContentType(.password) .focused($isFocused) Button("Login") { isFocused = false showLogin = false } .buttonStyle(.borderedProminent) } } } } Steps to Reproduce Launch sample app Tap 'Login' Place keyboard focus in the first text field (name) Keyboard with QuickType bar opens Tap 'Passwords' Create a new password for this login item (choose any username) Passwords will close Tap 'Login' to close the sheet Force close the app Reopen the app Tap 'Login' Place keyboard focus in the first text field (name) Keyboard with QuickType” bar opens Tap the auto-fill password button (password for atomicrobot.com in my case) User name and password fields are filled out Keyboard with QuickType bar is still open; keyboard focus is in "password" field Tap 'Login' Sheet closes, keyboard is still open
Topic: UI Frameworks SubTopic: SwiftUI
5
1
402
Jan ’25
NSScrollView scrolling hitch
When scrolling a basic NSScrollView there seems to be a sudden jump after each flick. Scrolling does not appear smooth and is disorientating. A scroll jump seems to happen directly after letting go of a scroll flick using a trackpad/mouse. Right at that moment the scroll turns into a momentum scroll, slowly decreasing the speed. But the first frame after the gesture the content jumps forward, more than what is expected. Observations: Counterintuitively, scrolling appears to be smoother when disabling NSScrollView.isCompatibleWithResponsiveScrolling. If disabled using a custom NSScrollView subclass there is no large jump anymore. Scrolling also appears to be smoother using a SwiftUI ScrollView. I assume that has the same behaviour as a disabled isCompatibleWithResponsiveScrolling Ironically a WKWebView scrolls much smoother. No sudden jump is observable. It also seems to scroll with faster acceleration, but the individual frames do appear smoother. Why is this better than a native NSScrollView? Elastic scrolling at the bounds of the scroll view also appears much smoother for WKWebViews. When pulling to refresh there is a jump for NSScrollView/SwiftUI, but not for WKWebView. When using an NSScrollView with isCompatibleWithResponsiveScrolling disabled, scrolling appears just as smooth as WKWebView on macOS 13 Ventura and below. On macOS 14 Sonoma scrolling behaviour is suddenly different. Please see a sample project with 4 different scroll views side by side: https://github.com/floorish/ScrollTest Screen recordings show the sudden jumps when scrolling and when elastic scrolling. Tested on Intel & Arm Macs, macOS 11 Big Sur through 15 Sequoia, built with Xcode 16. Should isCompatibleWithResponsiveScrolling be disabled on Sonoma+? Are there any drawbacks? There is also no overdraw anymore since Monterey, as described in https://developer.apple.com/library/archive/releasenotes/AppKit/RN-AppKitOlderNotes/#10_9Scrolling Even with responsive scrolling disabled, why is WKWebView scrolling much smoother than NSScrollView?
Topic: UI Frameworks SubTopic: AppKit Tags:
8
2
831
Jan ’25
Popovers are broken on Catalyst builds without portrait support
On macOS 15.2, any Mac Catalyst project that does not support portrait iPad orientation will no longer be able to successfully show the contents of any popover controls. This does not appear to be a problem on earlier versions of macOS and it only affects Mac Catalyst builds, not "Designed for iPad" builds. STEPS TO REPRODUCE Create a project that utilizes Mac Catalyst. Create a simple button that shows a popover with simple content. Remove Portrait as a supported orientation. Run the project on macOS 15.2 as a Mac Catalyst build. Note that the content inside the popover is not shown the popover is shown. Run the project as Designed for iPad. Note that the popover content shows correctly.
4
2
467
Jan ’25
SwiftData document based on iOS18 shows blank screen
I have some apps using SwiftData document based. They work as expected under iOS17, but not under iOS18: install the app on a iPad 11 pro (first gen) from my MacBook open the app and open an existing fils (perfect under iOS17) it shows a blank, white screen, no data I can create a new document, blank screen, no data When I open that newly created file on a iOS 17 iPad pro, it works perfect, as expected The apps were created from scratch under macOS 14.7 with the corresponding Xcode/Swift/UI version. iOS devices under iOS17 Are there any known problems with document based SwiftData-apps under iOS18, are there any changes one has to made? Thank You so much for any help!
6
0
803
Jan ’25
Custom Intent ParameterSummary based on Widget Kind/ID
I'm trying to create two widgets, widget A and B. Currently A and B are very similar so they share the same Intent and Intent Timeline Provider. I use the Intent Configuration interface to set a parameter, in this example lets say its the background tint. On one of the widgets, widget A, I want to also set another String enum parameter (for a timescale), but I don't want this option to be there for widget B as it's not relevant. I'm aware of some of the options for configuring the ParameterSummary, but none that let me pass in or inject the "kind" string (or widget ID) of the widget that's being modified. I'll try to provide some code for examples. My Widget Definition (targeting >= iOS 17) struct WidgetA: Widget { // I'd like to access this parameter within the intent let kind: String = "WidgetA" var body: some WidgetConfiguration { AppIntentConfiguration(kind: kind, intent: WidgetIntent.self, provider: IntentTimelineProvider()) { entry in WidgetView(data: entry) } .configurationDisplayName("Widget A") .description("A widget.") .supportedFamilies([.systemMedium, .systemLarge]) } } struct WidgetB: Widget { let kind: String = "WidgetB" var body: some WidgetConfiguration { AppIntentConfiguration(kind: kind, intent: WidgetIntent.self, provider: IntentTimelineProvider()) { entry in WidgetView(data: entry) } .configurationDisplayName("Widget B") .description("B widget.") .supportedFamilies([.systemMedium, .systemLarge]) } } struct IntentTimelineProvider: AppIntentTimelineProvider { typealias Entry = WidgetIntentTimelineEntry typealias Intent = WidgetIntent ........ } struct WidgetIntent: AppIntent, WidgetConfigurationIntent { // This intent allows configuration of the widget background // This intent also allows for the widget to display interactive buttons for changing the Trend Type static var title: LocalizedStringResource = "Widget Configuration" static var description = IntentDescription("Description.") static var isDiscoverable: Bool { return false} init() {} init(trend:String) { self.trend = trend } // Used for implementing interactive Widget func perform() async throws -> some IntentResult { print("WidgetIntent perform \(trend)") #if os(iOS) WidgetState.setState(type: trend) #endif return .result() } @Parameter(title: "Trend Type", default: "Trend") var trend:String // I only want to show this parameter for Widget A and not Widget B @Parameter(title: "Trend Timescale", default: .week) var timescale: TimescaleTypeAppEnum? @Parameter(title: "Background Tint", default: BackgroundTintTypeAppEnum.none) var backgroundTint: BackgroundTintTypeAppEnum? static var parameterSummary: some ParameterSummary { // Summary("Test Info") { // \.$timescale // \.$backgroundTint // } // An example of a configurable widget parameter summary, but not based of kind/ID string When(\.$backgroundTint, .equalTo, BackgroundTintTypeAppEnum.none) { Summary("Test Info") { \.$timescale \.$backgroundTint } } otherwise : { Summary("Test Info") { \.$backgroundTint } } } } enum TimescaleTypeAppEnum: String, AppEnum { case week case fortnight static var typeDisplayRepresentation = TypeDisplayRepresentation(name: "Trend Timescale") static var caseDisplayRepresentations: [Self: DisplayRepresentation] = [ .week: "Past Week", .fortnight: "Past Fortnight" ] } enum BackgroundTintTypeAppEnum: String, AppEnum { case blue case none static var typeDisplayRepresentation = TypeDisplayRepresentation(name: "Background Tint") static var caseDisplayRepresentations: [Self: DisplayRepresentation] = [ .none: "None (Default)", .blue: "Blue" ] } I know I could achieve what I'm after by having a separate Intent and separate IntentTimelineProvider for each widget. But this all seems unnessecary for just a simple optional parameter based on what widget its configuring.... unless I'm missing the point about Intents, Widgets or something! I've done a fair bit of other searching but can't find an answer to this overall scenario. Many thanks for any help.
2
0
549
Jan ’25
Animate colours in a SwiftUI Canvas
Pseudo-code: PhaseAnimator([false,true], trigger: foo) { flash in ZStack { Capsule() .foregroundStyle(flash ? .red : .green) Canvas { context, size in context.draw(image: Image(name: "foo"), toFitRect: some_rectangle); context.draw(text: Text("foo"), toFitRect: another_rectangle); } .foregroundStyle(flash ? .black : .white) } } animation: { flash in return .linear(duration: 0.5); } The Capsule's colour animates, but the Canvas's doesn't. The Canvas drawing code is only ever called with flash==false. What do I have to do to the Canvas so that it redraws with the intermediate colours during the animation?
3
0
410
Jan ’25
NSLayoutManager laying out overlapping text into the same NSTextContainer even when there are more containers available.
In summation: I have a nasty bug where my layout manager is laying out text visually overlapping on top of other text, i.e., into a container that it should have left in the rear view as it continues to lay out into ensuing containers. Details below... I'm coding a word processing app with some custom pagination that involves multiple pages, within which there can be multiple NSTextView/NSTextContainer pairs that represent single column or dual column runs of text. I generate pagination data by using a measuring NSLayoutManager. This process ensures that no containers overlap, and that they are sized correctly for their associated ranges of text (i.e., non-overlapping, continuous ranges from a single NSTextStorage). I determine frame sizes by a series of checks, most importantly, by finding the last glyph in a column. Prior to the code below, remainingColumnRange represents the remaining range of my textStorage that is of a consistent column type (i.e., single, left column, or right column). My measuring passes consist of my measuringLayoutManager laying out text into its textContainers, the final of which is an extra overflowContainer (i.e., == measuringLayoutManager.textContainers.last!) which I only use to find the last glyph in the second to last container (measuringContainer, which is thus == measuringLayoutManager.textContainers[count - 2]) let glyphRangeOfLastColumnChar = measuringLayoutManager.glyphRange(forCharacterRange: remainingColumnRange, actualCharacterRange: nil) let lastGlyphIndex = NSMaxRange(glyphRangeOfLastColumnChar) - 1 measuringLayoutManager.ensureLayout(for: measuringContainer) // Not sure if this is necessary, but I've added it to insure I'm getting accurate measurements. if measuringLayoutManager.textContainer(forGlyphAt: lastGlyphOfColumnIndex, effectiveRange: &actualGlyphRangeInContainer) == overflowContainer { actualCharRangeInContainer = measuringLayoutManager.characterRange(forGlyphRange: actualGlyphRangeInContainer, actualGlyphRange: nil) let overflowLoc = actualCharRangeInContainer.location remainingColumnRange = NSRange(location: overflowLoc, length: remainingColumnRange.length - overflowLoc) currentPage += 1 } else { lineFragmentRectForLastChar = measuringLayoutManager.lineFragmentRect(forGlyphAt: lastGlyphIndex, effectiveRange: nil) // Resize measuring container if needed. let usedHeight = lineFragmentRectForLastChar.maxY if usedHeight < measuringContainer.size.height { measuringContainer.size = CGSize(width: measuringContainer.size.width, height: usedHeight) } else if usedHeight == measuringContainer.size.height { currentPage += 1 // we perfectly filled the page } else { // This would be an error case, because all cases should have been handled prior to arriving here. I throw an error. I have never fallen through here. throw MyClass.anError } } // I use the above data to create a PageLayoutItem, which is a struct that has frame data (CGRect/x,y,w,h), a containerIndex (Int), pageNumber (Int), textRange (NSRange), columnType (custom enum). // After this I remove the overflowContainer, and continue to iterate through. This is inefficient but I'm simplifying my code to identify the root issue. I don't explicitly use these containers when done with my pagination process. Rather, I use the PageLayoutItems I have created to generate/resize/remove textContainers/textViews for the UI as needed. My UI-interfacing/generating NSLayoutManager, which is of course assigned to the same NSTextStorage as the measuring layout manager, then iterates through my paginator model class' pageLayoutItems array to generate/resize/remove. I have verified my pagination data. None of my frames overlap. They are sized exactly the same as they should be per my measurement passes. The number of containers/views needed is correct. But here's the issue: My views render the text that SHOULD appear in my final textContainer/textView as visually overlapping the text in my second to last textContainer/textView. I see a garble of text. When I iterate through my UI textContainers, I get this debug print: TextContainer 0 glyphRange: {0, 172} TextContainer 1 glyphRange: {172, 55} TextContainer 2 glyphRange: {227, 100} // this is wrong, final 31 chars should be in container 3 TextContainer 3 glyphRange: {327, 0} // empty range here, odd I have tried setting textContainers for glyph ranges explicitly, via: // Variable names just for clarity here layoutManager.setTextContainer(correctTextView.textContainer!, forGlyphRange: correctGlyphRangeForThisContainer) Debug prints show that I'm setting the right ranges there. But they don't retain. I have tried resizing my final text container to be much larger in case that was the issue. No dice. My final range of text/glyphs still lays out in the wrong container and overlaps the other content laid out there. Any help here?? I've scoured the forums and have been dealing with this bug for two weeks straight with no hope in sight.
4
0
1.1k
Jan ’25
The PKCanvasView Created by PDFPageOverlayViewProvider cannot work normally
By setting the PKCanvasView background color to blue, I can tell that the PKCanvasView for each PDFPage is created normally, but it does not respond to touch. Specifically, whether it is finger or applepencil, all the responses of the page occur from PDFView(such as zoom and scroll), and PKCanvasView can not draw, please how to solve? class PDFAnnotatableViewController: UIViewController, PDFViewDelegate { private let pdfView = PDFView() private var pdfDocument: PDFDocument? let file: FileItem private var userSettings: UserSettings @Binding var selectedPage: Int @Binding var currentMode: Mode @Binding var latestPdfChatResponse: LatestPDFChatResponse @State private var pdfPageCoordinator = PDFPageCoordinator() @ObservedObject var userMessage: ChatMessage init(file: FileItem, userSettings: UserSettings, drawDataList: Binding<[DrawDataItem]>, selectedPage: Binding<Int>, currentMode: Binding<Mode>, latestPdfChatResponse: Binding<LatestPDFChatResponse>, userMessage: ChatMessage) { self.file = file self.userSettings = userSettings self._selectedPage = selectedPage self._currentMode = currentMode self._latestPdfChatResponse = latestPdfChatResponse self.userMessage = userMessage super.init(nibName: nil, bundle: nil) DispatchQueue.global(qos: .userInitiated).async { if let document = PDFDocument(url: file.pdfLocalUrl) { DispatchQueue.main.async { self.pdfDocument = document self.pdfView.document = document self.goToPage(selectedPage: selectedPage.wrappedValue - 1) } } } } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func viewDidLoad() { super.viewDidLoad() setupPDFView() } private func setupPDFView() { pdfView.delegate = self pdfView.autoScales = true pdfView.displayMode = .singlePage pdfView.displayDirection = .vertical pdfView.backgroundColor = .white pdfView.usePageViewController(true) pdfView.displaysPageBreaks = false pdfView.displaysAsBook = false pdfView.minScaleFactor = 0.8 pdfView.maxScaleFactor = 3.5 pdfView.pageOverlayViewProvider = pdfPageCoordinator if let document = pdfDocument { pdfView.document = document goToPage(selectedPage: selectedPage) } pdfView.frame = view.bounds pdfView.autoresizingMask = [.flexibleWidth, .flexibleHeight] view.addSubview(pdfView) NotificationCenter.default.addObserver( self, selector: #selector(handlePageChange), name: .PDFViewPageChanged, object: pdfView ) } // Dealing with page turning @objc private func handlePageChange(notification: Notification) { guard let currentPage = pdfView.currentPage, let document = pdfView.document else { return } let currentPageIndex = document.index(for: currentPage) if currentPageIndex != selectedPage - 1 { DispatchQueue.main.async { self.selectedPage = currentPageIndex + 1 } } } func goToPage(selectedPage: Int) { guard let document = pdfView.document else { return } if let page = document.page(at: selectedPage) { pdfView.go(to: page) } } // Switch function func togglecurrentMode(currentMode: Mode){ DispatchQueue.main.async { if self.currentMode == .none{ self.pdfView.usePageViewController(true) self.pdfView.isUserInteractionEnabled = true } else if self.currentMode == .annotation { if let page = self.pdfView.currentPage { if let canvasView = self.pdfPageCoordinator.getCanvasView(forPage: page) { canvasView.isUserInteractionEnabled = true canvasView.tool = PKInkingTool(.pen, color: .red, width: 20) canvasView.drawingPolicy = .anyInput canvasView.setNeedsDisplay() } } } } } } class MyPDFPage: PDFPage { var drawing: PKDrawing? func setDrawing(_ drawing: PKDrawing) { self.drawing = drawing } func getDrawing() -> PKDrawing? { return self.drawing } } class PDFPageCoordinator: NSObject, PDFPageOverlayViewProvider { var pageToViewMapping = [PDFPage: PKCanvasView]() func pdfView(_ view: PDFView, overlayViewFor page: PDFPage) -> UIView? { var resultView: PKCanvasView? = nil if let overlayView = pageToViewMapping[page] { resultView = overlayView } else { let canvasView = PKCanvasView(frame: view.bounds) canvasView.drawingPolicy = .anyInput canvasView.tool = PKInkingTool(.pen, color: .systemYellow, width: 20) canvasView.backgroundColor = .blue pageToViewMapping[page] = canvasView resultView = canvasView } if let page = page as? MyPDFPage, let drawing = page.drawing { resultView?.drawing = drawing } return resultView } func pdfView(_ pdfView: PDFView, willEndDisplayingOverlayView overlayView: UIView, for page: PDFPage) { guard let overlayView = overlayView as? PKCanvasView, let page = page as? MyPDFPage else { return } page.drawing = overlayView.drawing pageToViewMapping.removeValue(forKey: page) } func savePDFDocument(_ pdfDocument: PDFDocument) -> Data { for i in 0..<pdfDocument.pageCount { if let page = pdfDocument.page(at: i) as? MyPDFPage, let drawing = page.drawing { let newAnnotation = PDFAnnotation(bounds: drawing.bounds, forType: .stamp, withProperties: nil) let codedData = try! NSKeyedArchiver.archivedData(withRootObject: drawing, requiringSecureCoding: true) newAnnotation.setValue(codedData, forAnnotationKey: PDFAnnotationKey(rawValue: "drawingData")) page.addAnnotation(newAnnotation) } } let options = [PDFDocumentWriteOption.burnInAnnotationsOption: true] if let resultData = pdfDocument.dataRepresentation(options: options) { return resultData } return Data() } func getCanvasView(forPage page: PDFPage) -> PKCanvasView? { return pageToViewMapping[page] } } Is there an error in my code? Please tell me how to make PKCanvasView painting normally?
1
0
474
Feb ’25
Issue: Chart Scroll Not Working in iOS 18 with Chart Overlay Tap Gesture
Summary I have a SwiftUI Chart that worked correctly in iOS 17, allowing both horizontal scrolling and tap gesture selection. However, in iOS 18, the same exact chart will not allow for both tap gestures and scrolling to work -- it's either we allow scrolling or we allow tap gestures but not both. We have tried everything to try to circumvent this issue but have had to resort to old methods of creating the chart. This is an issue that has negatively impacted our customers as well. Again, the charts were working fine on iOS 17, but on iOS 18 the chart scroll + tap gesture capability is not working. Expected Behavior (iOS 17) Users can scroll horizontally through the chart. Users can tap on data points to highlight them. The selected data point updates when tapped. Observed Behavior (iOS 18) The chart no longer scrolls when chartOverlay with the Tap Gesture is applied. Tap selection still works as expected. Code Snippet Below is the working implementation from iOS 17: private var iOS17ChartView: some View { Chart { RectangleMark( yStart: .value(String(firstLevelAlertBand), firstLevelAlertBand), yEnd: .value("100", 100) ) .foregroundStyle(Theme.Colors.green.opacity(0.15)) RectangleMark( yStart: .value(String(secondLevelAlertBand), secondLevelAlertBand), yEnd: .value(String(firstLevelAlertBand), firstLevelAlertBand) ) .foregroundStyle(Theme.Colors.orange.opacity(0.15)) RectangleMark( yStart: .value("0", 0), yEnd: .value(String(secondLevelAlertBand), secondLevelAlertBand) ) .foregroundStyle(Theme.Colors.red.opacity(0.15)) ForEach(telemetryData, id: \.timestamp) { entry in if let utcDate = dateFormatter.date(from: entry.timestamp) { let localDate = convertToUserTimeZone(date: utcDate) let tankLevel = entry.tankLevel ?? 0 LineMark( x: .value("Date", localDate), y: .value("Tank Level", tankLevel) ) .foregroundStyle(statusColor) AreaMark( x: .value("Date", localDate), y: .value("Tank Level", tankLevel) ) .foregroundStyle(statusColor.opacity(0.50)) PointMark( x: .value("Date", localDate), y: .value("Tank Level", tankLevel) ) .foregroundStyle(selectedDataPoint?.date == localDate ? Theme.Colors.primaryColor : statusColor) .symbolSize(selectedDataPoint?.date == localDate ? 120 : 80) PointMark( x: .value("Date", localDate), y: .value("Tank Level", tankLevel) ) //.foregroundStyle(.white).symbolSize(10) .foregroundStyle(Theme.Colors.white(colorScheme: colorScheme)) .symbolSize(12) } } } .chartXScale(domain: (firstTimestamp ?? Date())...(latestTimestamp ?? Date())) .chartXVisibleDomain(length: visibleDomainSize) .chartScrollableAxes(.horizontal) .chartScrollPosition(x: $chartScrollPositionX) .chartXAxis { AxisMarks(values: .stride(by: xAxisStrideUnit, count: xAxisCount())) { value in if let utcDate = value.as(Date.self) { let localDate = convertToUserTimeZone(date: utcDate) let formatStyle = self.getFormatStyle(for: interval) AxisValueLabel { Text(localDate, format: formatStyle) .font(Theme.Fonts.poppinsRegularExtraSmall) .foregroundStyle(Theme.Colors.black(colorScheme: colorScheme)) } AxisTick() .foregroundStyle(Theme.Colors.black(colorScheme: colorScheme).opacity(1)) } } } .chartOverlay { proxy in GeometryReader { geometry in Rectangle().fill(Color.clear).contentShape(Rectangle()) .onTapGesture { location in let xPosition = location.x - geometry[proxy.plotAreaFrame].origin.x // Use proxy to get the x-axis value at the tapped position if let selectedDate: Date = proxy.value(atX: xPosition) { if let closestEntry = telemetryData.min(by: { abs(dateFormatter.date(from: $0.timestamp)!.timeIntervalSince1970 - selectedDate.timeIntervalSince1970) < abs(dateFormatter.date(from: $1.timestamp)!.timeIntervalSince1970 - selectedDate.timeIntervalSince1970) }) { selectedDataPoint = (convertToUserTimeZone(date: dateFormatter.date(from: closestEntry.timestamp)!), closestEntry.tankLevel ?? 0) if let dateXPos = proxy.position(forX: convertToUserTimeZone(date: dateFormatter.date(from: closestEntry.timestamp)!)), let tankLevelYPos = proxy.position(forY: closestEntry.tankLevel ?? 0) { // Offset the x-position based on the scroll position let adjustedXPos = dateXPos - proxy.position(forX: chartScrollPositionX)! withAnimation(.spring()) { selectedPointLocation = CGPoint(x: adjustedXPos, y: tankLevelYPos - 60) // Offset popup above the point showPopup = true } } } } } } .onChange(of: chartScrollPositionX) { newValue in // Dynamically update the popup position when scroll changes if let selectedDataPoint = selectedDataPoint { if let dateXPos = proxy.position(forX: selectedDataPoint.date) { let adjustedXPos = dateXPos - proxy.position(forX: chartScrollPositionX)! selectedPointLocation.x = adjustedXPos } } } } } Please help! Nick
1
1
636
Feb ’25
.presentationDetents not working on iOS 17
Hello, I have the following code: .sheet(isPresented: $viewModel.isExerciseSelected) { ExerciseEditSheetView(viewModel: viewModel) .presentationDetents([.fraction(0.4)]) } This code correctly sizes the sheet on my iOS 18 simulator, but does not work on my iOS 17 simulator as well as my actual phone running iOS 17. Instead the sheet always fullscreens. Although its not the end of the world it is pretty annoying. Is there anything I'm missing? Thanks in advance.
Topic: UI Frameworks SubTopic: SwiftUI
2
0
323
Feb ’25
Since iOS 18.3, icons are no longer generated correctly with QLThumbnailGenerator
Since iOS 18.3, icons are no longer generated correctly with QLThumbnailGenerator. No error is returned either. But this error message now appears in the console: Error returned from iconservicesagent image request: <ISTypeIcon: 0x3010f91a0>,Type: com.adobe.pdf - <ISImageDescriptor: 0x302f188c0> - (36.00, 36.00)@3x v:1 l:5 a:0:0:0:0 t:() b:0 s:2 ps:0 digest: B19540FD-0449-3E89-AC50-38F92F9760FE error: Error Domain=NSOSStatusErrorDomain Code=-609 "Client is disallowed from making such an icon request" UserInfo={NSLocalizedDescription=Client is disallowed from making such an icon request} Does anyone know this error? Is there a workaround? Are there new permissions to consider? Here is the code how icons are generated: let request = QLThumbnailGenerator.Request(fileAt: url, size: size, scale: scale, representationTypes: self.thumbnailType) request.iconMode = true let generator = QLThumbnailGenerator.shared generator.generateRepresentations(for: request) { [weak self] thumbnail, _, error in }
16
5
1.7k
Feb ’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
539
Feb ’25
Simulating key press event to type text in UITextField
in iOS, user can set focus on UItextField and tapping a key in the virtual keyboard updates the text in the textfield. This user action causes the relevant delegates of UITextFieldDelegate to get invoked, i.e the handlers associated with action of user entering some text in the textfield. I m trying to simulate this user action where I am trying to do this programatically. I want to simulate it in a way such that all the handlers/listeners which otherwise would have been invoked as a result of user typing in the textfield should also get invoked now when i am trying to do it programatically. I have a specific usecase of this in my application. Below is how I m performing this simulation. I m manually updating the text field associated(UITextField.text) and updating its value. And then I m invoking the delegate manually as textField.delegate?.textField?(textField, shouldChangeCharactersIn: nsRange, replacementString: replacementString) I wanted to know If this is the right way to do this. Is there something better available that can be used, such that simulation has the same affect as the user performing the update?
Topic: UI Frameworks SubTopic: UIKit Tags:
4
0
403
Feb ’25
Swift Charts: How to prevent scroll position jump when loading more data dynamically
I'm implementing infinite scrolling with Swift Charts where additional historical data loads when scrolling near the beginning of the dataset. However, when new data is loaded, the chart's scroll position jumps unexpectedly. Current behavior: Initially loads 10 data points, displaying the latest 5 When scrolling backwards with only 3 points remaining off-screen, triggers loading of 10 more historical points After loading, the scroll position jumps to the 3rd position of the new dataset instead of maintaining the current view Expected behavior: Scroll position should remain stable when new data is loaded User's current view should not change during data loading Here's my implementation logic using some mock data: import SwiftUI import Charts struct DataPoint: Identifiable { let id = UUID() let date: Date let value: Double } class ChartViewModel: ObservableObject { @Published var dataPoints: [DataPoint] = [] private var isLoading = false init() { loadMoreData() } func loadMoreData() { guard !isLoading else { return } isLoading = true let newData = self.generateDataPoints( endDate: self.dataPoints.first?.date ?? Date(), count: 10 ) self.dataPoints.insert(contentsOf: newData, at: 0) self.isLoading = false print("\(dataPoints.count) data points.") } private func generateDataPoints(endDate: Date, count: Int) -> [DataPoint] { var points: [DataPoint] = [] let calendar = Calendar.current for i in 0..<count { let date = calendar.date( byAdding: .day, value: -i, to: endDate ) ?? endDate let value = Double.random(in: 0...100) points.append(DataPoint(date: date, value: value)) } return points.sorted { $0.date < $1.date } } } struct ScrollableChart: View { @StateObject private var viewModel = ChartViewModel() @State private var scrollPosition: Date @State private var scrollDebounceTask: Task<Void, Never>? init() { self.scrollPosition = .now.addingTimeInterval(-4*24*3600) } var body: some View { Chart(viewModel.dataPoints) { point in BarMark( x: .value("Time", point.date, unit: .day), y: .value("Value", point.value) ) } .chartScrollableAxes(.horizontal) .chartXVisibleDomain(length: 5 * 24 * 3600) .chartScrollPosition(x: $scrollPosition) .chartXScale(domain: .automatic(includesZero: false)) .frame(height: 300) .onChange(of: scrollPosition) { oldPosition, newPosition in scrollDebounceTask?.cancel() scrollDebounceTask = Task { try? await Task.sleep(for: .milliseconds(300)) if !Task.isCancelled { checkAndLoadMoreData(currentPosition: newPosition) } } } } private func checkAndLoadMoreData(currentPosition: Date?) { guard let currentPosition, let earliestDataPoint = viewModel.dataPoints.first?.date else { return } let timeInterval = currentPosition.timeIntervalSince(earliestDataPoint) if timeInterval <= 3 * 24 * 3600 { viewModel.loadMoreData() } } } I attempted to compensate for this jump by adding: scrollPosition = scrollPosition.addingTimeInterval(10 * 24 * 3600) after viewModel.loadMoreData(). However, this caused the chart to jump in the opposite direction by 10 days, rather than maintaining the current position. What's the problem with my code and how to fix it?
6
0
601
Feb ’25
iOS app crash at -[UIView _wrappedProcessTraitChanges:withBehavior:] + 1288 (UIView.m:0)
Hello Apple engineers, could you help me understand whether this crash is a UIKit bug or something in our code that causes it. Based on the documentation it is an invalid fetch instruction, that's why I suspect UIKit. I've found a similar crash here on the forums reported a year ago, it seemed to be a UIKit bug - https://forums.developer.apple.com/forums/thread/729448. I've attached the full crash report (the app name was replaced with ): Thread 0 Crashed: 0 libobjc.A.dylib 0x000000019daf7c20 objc_msgSend + 32 (:-1) 1 UIKitCore 0x00000001a3020c50 -[UIView _wrappedProcessTraitChanges:withBehavior:] + 1288 (UIView.m:0) 2 UIKitCore 0x00000001a3020720 -[UIView _processChangesFromOldTraits:toCurrentTraits:withBehavior:] + 196 (UIView.m:7840) 3 UIKitCore 0x00000001a3020618 -[UIView _updateTraitCollectionAndProcessChangesWithBehavior:previousCollection:] + 112 (UIView.m:7831) 4 UIKitCore 0x00000001a2fa90c0 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 944 (UIView.m:19850) 5 QuartzCore 0x00000001a22dfc28 CA::Layer::layout_if_needed(CA::Transaction*) + 496 (CALayer.mm:10944) 6 QuartzCore 0x00000001a22df7b4 CA::Layer::layout_and_display_if_needed(CA::Transaction*) + 148 (CALayer.mm:2638) 7 QuartzCore 0x00000001a2336914 CA::Context::commit_transaction(CA::Transaction*, double, double*) + 472 (CAContextInternal.mm:2613) 8 QuartzCore 0x00000001a22b57c4 CA::Transaction::commit() + 648 (CATransactionInternal.mm:420) 9 QuartzCore 0x00000001a22f8a0c CA::Transaction::flush_as_runloop_observer(bool) + 88 (CATransactionInternal.mm:928) 10 UIKitCore 0x00000001a303f568 _UIApplicationFlushCATransaction + 52 (UIApplication.m:3326) 11 UIKitCore 0x00000001a303cb64 __setupUpdateSequence_block_invoke_2 + 332 (_UIUpdateScheduler.m:1652) 12 UIKitCore 0x00000001a303c9d8 _UIUpdateSequenceRun + 84 (_UIUpdateSequence.mm:136) 13 UIKitCore 0x00000001a303c628 schedulerStepScheduledMainSection + 172 (_UIUpdateScheduler.m:1171) 14 UIKitCore 0x00000001a303d59c runloopSourceCallback + 92 (_UIUpdateScheduler.m:1334) 15 CoreFoundation 0x00000001a080c328 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 28 (CFRunLoop.c:1970) 16 CoreFoundation 0x00000001a080c2bc __CFRunLoopDoSource0 + 176 (CFRunLoop.c:2014) 17 CoreFoundation 0x00000001a0809dc0 __CFRunLoopDoSources0 + 244 (CFRunLoop.c:2051) 18 CoreFoundation 0x00000001a0808fbc __CFRunLoopRun + 840 (CFRunLoop.c:2969) 19 CoreFoundation 0x00000001a0808830 CFRunLoopRunSpecific + 588 (CFRunLoop.c:3434) 20 GraphicsServices 0x00000001ec7e81c4 GSEventRunModal + 164 (GSEvent.c:2196) 21 UIKitCore 0x00000001a336eeb0 -[UIApplication _run] + 816 (UIApplication.m:3844) 22 UIKitCore 0x00000001a341d5b4 UIApplicationMain + 340 (UIApplication.m:5496) 23 UIKitCore 0x00000001a3757fa8 UIApplicationMain(_:_:_:_:) + 104 (UIKit.swift:565) 24 <Redacted> 0x00000001028bde64 specialized static UIApplicationDelegate.main() + 28 (/<compiler-generated>:16) 25 <Redacted> 0x00000001028bde64 static AppDelegate.$main() + 28 (AppDelegate.swift:0) 26 <Redacted> 0x00000001028bde64 main + 116 27 dyld 0x00000001c61f6ec8 start + 2724 (dyldMain.cpp:1334) 2024-12-27_20-53-28.2129_-0500-353aaa194e6232c0d1fae767296bdb8c47c30498.crash
Replies
3
Boosts
1
Views
657
Activity
Dec ’24
LiveActivity on device with Dynamic Island
I am developing a live activity for my app and am running into issues on devices with dynamic islands. The live activity will randomly get in a state where it displays a spinner and does not update. For devices without dynamic islands, the activity works perfectly fine. I have tried breaking down the views to determine the root cause, but at times it seems very random. These are all TestFlight builds as we are currently developing the feature. I have tried using the console app and looking at the various processes that have been called out in other threads and cannot see any actual errors being logged. Looking for any pointers on how to pinpoint the root cause of this issue.
Replies
2
Boosts
1
Views
564
Activity
Jan ’25
SwiftUI toolbar with IDs crash since macOS 15
I understand this is a known issue, but it’s truly unacceptable that it remains unresolved. Allowing users to customize toolbars is a fundamental macOS feature, and it has been broken since the release of macOS 15. How is it possible that this issue persists even in macOS 15.3 beta (24D5040f)? FB15513599 import SwiftUI struct ContentView: View { @State private var showEditItem = false var body: some View { VStack { VStack { Text("Instructions to reproduce the crash") .font(.title) .padding() Text(""" 1. Click on "Toggle Item" 2. In the menu go to File > New Window 3. In new window, click on "Toggle Item" """) } .padding() Button { showEditItem.toggle() } label: { Text("Toggle Item") } } .padding() .toolbar(id: "main") { ToolbarItem(id: "new") { Button { } label: { Text("New…") } } if showEditItem { ToolbarItem(id: "edit") { Button { } label: { Text("Edit…") } } } } } }
Replies
7
Boosts
3
Views
709
Activity
Jan ’25
Library not loaded: /System/Library/Frameworks/SwiftUICore.framework/SwiftUICore
Hello. I'm building an iOS application that builds a framework named Design.framework using Xcode 16.1. When I launch my application on iOS 17.X, i get the following runtime error dyld[47899]: Library not loaded: /System/Library/Frameworks/SwiftUICore.framework/SwiftUICore Referenced from: <E625A246-E36B-351A-B077-CC38BAB5E07B> /Users/v/Library/Developer/Xcode/DerivedData/iScribd-bexojbdreldwkvbpzioqueldnvng/Build/Products/Debug_AppStore-iphonesimulator/Design.framework/Design Reason: tried: '/Users/v/Library/Developer/Xcode/DerivedData/iScribd-bexojbdreldwkvbpzioqueldnvng/Build/Products/Debug_AppStore-watchsimulator/SwiftUICore.framework/SwiftUICore' (no such file), '/Users/v/Library/Developer/Xcode/DerivedData/iScribd-bexojbdreldwkvbpzioqueldnvng/Build/Products/Debug_AppStore-iphonesimulator/SwiftUICore.framework/SwiftUICore' (no such file), '/Users/v/Library/Developer/Xcode/DerivedData/iScribd-bexojbdreldwkvbpzioqueldnvng/Build/Products/Debug_AppStore-watchsimulator/PackageFrameworks/SwiftUICore.framework/SwiftUICore' (no such file), '/Users/v/Library/Developer/Xcode/DerivedData/iScribd-bexojbdreldwkvbpzioqueldnvng/Build/Products/Debug_AppStore-iphonesimulator/PackageFrameworks/SwiftUICore.framework/SwiftUICore' (no such file), '/Library/Developer/CoreSimulator/Volumes/iOS_21C62/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 17.2.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/SwiftUICore.framework/SwiftUICore' (no such file), '/System/Library/Frameworks/SwiftUICore.framework/SwiftUICore' (no such file, not in dyld cache), '/Library/Developer/CoreSimulator/Volumes/iOS_21C62/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 17.2.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/SwiftUICore.framework/SwiftUICore' (no such file) This error does not happen on iOS 18+. When i run dyld_info Design.framework/Design, i see that there is indeed a dependency on SwiftUICore.framework Design.framework/Design [arm64]: -platform: platform minOS sdk iOS-simulator 16.0 18.1 -uuid: 09880F25-DC86-3D8E-BCAE-1A283508D879 -segments: load-offset segment section sect-size seg-size max-prot init-prot 0x00000000 __TEXT 416KB r.x 0x00000000 __TEXT 416KB r.x 0x0000543C __text 355508 0x0005C0F0 __stubs 3168 0x0005CD50 __objc_methlist 32 0x0005CD70 __const 8296 0x0005EDD8 __swift5_typeref 15820 0x00062BB0 __cstring 6885 0x00064698 __constg_swiftt 2804 0x0006518C __swift5_builtin 40 0x000651C0 __swift5_reflstr 2952 0x00065D48 __swift5_assocty 816 0x00066078 __objc_methname 299 0x000661A4 __swift5_proto 368 0x00066314 __swift5_types 280 0x0006642C __swift5_fieldmd 3688 0x00067294 __swift5_protos 8 0x0006729C __swift5_capture 244 0x00067390 __unwind_info 2296 0x00067C88 __eh_frame 888 0x00068000 __DATA_CONST 16KB rw. 0x00068000 __DATA_CONST 16KB rw. 0x00068000 __got 3704 0x00068E78 __const 6864 0x0006A948 __objc_classlist 40 0x0006A970 __objc_imageinfo 8 0x0006C000 __DATA 32KB rw. 0x0006C000 __DATA 32KB rw. 0x0006C000 __objc_const 792 0x0006C318 __objc_selrefs 96 0x0006C378 __objc_classrefs 48 0x0006C3A8 __objc_data 208 0x0006C478 __data 4136 0x0006D4A0 __bss 12784 0x00070690 __common 4592 -linked_dylibs: attributes load path @rpath/CommonTools.framework/CommonTools /System/Library/Frameworks/Foundation.framework/Foundation /usr/lib/libobjc.A.dylib /usr/lib/libSystem.B.dylib /System/Library/Frameworks/CoreFoundation.framework/CoreFoundation /System/Library/Frameworks/CoreGraphics.framework/CoreGraphics /System/Library/Frameworks/CoreText.framework/CoreText weak-link /System/Library/Frameworks/DeveloperToolsSupport.framework/DeveloperToolsSupport /System/Library/Frameworks/SwiftUI.framework/SwiftUI /System/Library/Frameworks/SwiftUICore.framework/SwiftUICore /System/Library/Frameworks/UIKit.framework/UIKit /usr/lib/swift/libswiftCore.dylib weak-link /usr/lib/swift/libswiftCoreAudio.dylib /usr/lib/swift/libswiftCoreFoundation.dylib weak-link /usr/lib/swift/libswiftCoreImage.dylib weak-link /usr/lib/swift/libswiftCoreMedia.dylib weak-link /usr/lib/swift/libswiftDarwin.dylib weak-link /usr/lib/swift/libswiftDataDetection.dylib weak-link /usr/lib/swift/libswiftDispatch.dylib weak-link /usr/lib/swift/libswiftFileProvider.dylib weak-link /usr/lib/swift/libswiftMetal.dylib weak-link /usr/lib/swift/libswiftOSLog.dylib /usr/lib/swift/libswiftObjectiveC.dylib weak-link /usr/lib/swift/libswiftQuartzCore.dylib weak-link /usr/lib/swift/libswiftSpatial.dylib weak-link /usr/lib/swift/libswiftUniformTypeIdentifiers.dylib weak-link /usr/lib/swift/libswiftXPC.dylib /usr/lib/swift/libswift_Concurrency.dylib weak-link /usr/lib/swift/libswiftos.dylib weak-link /usr/lib/swift/libswiftsimd.dylib weak-link /usr/lib/swift/libswiftUIKit.dylib I can add a -weak_framework SwiftUICore to "Other Linker Flags" in my Design.framework Build Settings, and that will solve my problem, but I'd like to know what prompts my Design.framework to depend on SwiftUICore despite having a minimum set to iOS 16? And how am I supposed to handle devices prior to iOS 18 which do not have this library? is adding the linker flag the right way forward?
Topic: UI Frameworks SubTopic: SwiftUI
Replies
5
Boosts
0
Views
1.5k
Activity
Jan ’25
Mac Catalyst SwiftUI – . focused() not working
Hello, given this following simple SwiftUI setup: struct ContentView: View { var body: some View { CustomFocusView() } } struct CustomFocusView: View { @FocusState private var isFocused: Bool var body: some View { color .frame(width: 128, height: 128) .focusable(true) .focused($isFocused) .onTapGesture { isFocused.toggle() } .onKeyPress("a") { print("A pressed") return .handled } } var color: Color { isFocused ? .blue : .red } } If I run this via Mac – Designed for iPad, the CustomFocusView toggles focus as expected and cycles through red and blue. Now if I run this same exact code via Mac Catalyst absolutely nothing happens and so far I wasn't able to ever get this view to accept focused state. Is this expected? I would appreciate if anyone could hint me on how to get this working. Thank and best regards!
Replies
5
Boosts
0
Views
636
Activity
Jan ’25
Keyboard doesn't close if QuickType bar is open for password auto-fill in SwiftUI when using associated domains.
Description Our app (and sample app) are using associated domains to support deep linking. As an unintended side effect we get full support for auto-filling passwords with the QuickType bar on our login screen. However, after the username and password fields are filled and the user taps the Login button, the keyboard stays on screen. We have tried everything I can think of including @FocusState UIKit resignFirstResponder, and many other iterations of testing. Our login screen is in a fullscreencover or sheet. When the sheet dismisses the keyboard stays. In my sample app if I use a navigation stack and push the next view onto the stack, the keyboard closes. I can't provide a useful video because the iOS screen recorder will hide the keyboard when focus is in a SecureField. Note: If we remove the associated domain from the project everything works as expected. Code Example struct ContentView: View { @State private var name: String = "" @State private var password: String = "" @State private var showLogin = false @FocusState private var isFocused: Bool var body: some View { VStack { Button("Login") { showLogin.toggle() } } .fullScreenCover(isPresented: $showLogin) { VStack { TextField("Enter your name", text: $name) .textFieldStyle(.roundedBorder) .focused($isFocused) SecureField("Enter password", text: $password) .autocapitalization(.none) .autocorrectionDisabled(true) .textContentType(.password) .focused($isFocused) Button("Login") { isFocused = false showLogin = false } .buttonStyle(.borderedProminent) } } } } Steps to Reproduce Launch sample app Tap 'Login' Place keyboard focus in the first text field (name) Keyboard with QuickType bar opens Tap 'Passwords' Create a new password for this login item (choose any username) Passwords will close Tap 'Login' to close the sheet Force close the app Reopen the app Tap 'Login' Place keyboard focus in the first text field (name) Keyboard with QuickType” bar opens Tap the auto-fill password button (password for atomicrobot.com in my case) User name and password fields are filled out Keyboard with QuickType bar is still open; keyboard focus is in "password" field Tap 'Login' Sheet closes, keyboard is still open
Topic: UI Frameworks SubTopic: SwiftUI
Replies
5
Boosts
1
Views
402
Activity
Jan ’25
NSScrollView scrolling hitch
When scrolling a basic NSScrollView there seems to be a sudden jump after each flick. Scrolling does not appear smooth and is disorientating. A scroll jump seems to happen directly after letting go of a scroll flick using a trackpad/mouse. Right at that moment the scroll turns into a momentum scroll, slowly decreasing the speed. But the first frame after the gesture the content jumps forward, more than what is expected. Observations: Counterintuitively, scrolling appears to be smoother when disabling NSScrollView.isCompatibleWithResponsiveScrolling. If disabled using a custom NSScrollView subclass there is no large jump anymore. Scrolling also appears to be smoother using a SwiftUI ScrollView. I assume that has the same behaviour as a disabled isCompatibleWithResponsiveScrolling Ironically a WKWebView scrolls much smoother. No sudden jump is observable. It also seems to scroll with faster acceleration, but the individual frames do appear smoother. Why is this better than a native NSScrollView? Elastic scrolling at the bounds of the scroll view also appears much smoother for WKWebViews. When pulling to refresh there is a jump for NSScrollView/SwiftUI, but not for WKWebView. When using an NSScrollView with isCompatibleWithResponsiveScrolling disabled, scrolling appears just as smooth as WKWebView on macOS 13 Ventura and below. On macOS 14 Sonoma scrolling behaviour is suddenly different. Please see a sample project with 4 different scroll views side by side: https://github.com/floorish/ScrollTest Screen recordings show the sudden jumps when scrolling and when elastic scrolling. Tested on Intel & Arm Macs, macOS 11 Big Sur through 15 Sequoia, built with Xcode 16. Should isCompatibleWithResponsiveScrolling be disabled on Sonoma+? Are there any drawbacks? There is also no overdraw anymore since Monterey, as described in https://developer.apple.com/library/archive/releasenotes/AppKit/RN-AppKitOlderNotes/#10_9Scrolling Even with responsive scrolling disabled, why is WKWebView scrolling much smoother than NSScrollView?
Topic: UI Frameworks SubTopic: AppKit Tags:
Replies
8
Boosts
2
Views
831
Activity
Jan ’25
Popovers are broken on Catalyst builds without portrait support
On macOS 15.2, any Mac Catalyst project that does not support portrait iPad orientation will no longer be able to successfully show the contents of any popover controls. This does not appear to be a problem on earlier versions of macOS and it only affects Mac Catalyst builds, not "Designed for iPad" builds. STEPS TO REPRODUCE Create a project that utilizes Mac Catalyst. Create a simple button that shows a popover with simple content. Remove Portrait as a supported orientation. Run the project on macOS 15.2 as a Mac Catalyst build. Note that the content inside the popover is not shown the popover is shown. Run the project as Designed for iPad. Note that the popover content shows correctly.
Replies
4
Boosts
2
Views
467
Activity
Jan ’25
SwiftData document based on iOS18 shows blank screen
I have some apps using SwiftData document based. They work as expected under iOS17, but not under iOS18: install the app on a iPad 11 pro (first gen) from my MacBook open the app and open an existing fils (perfect under iOS17) it shows a blank, white screen, no data I can create a new document, blank screen, no data When I open that newly created file on a iOS 17 iPad pro, it works perfect, as expected The apps were created from scratch under macOS 14.7 with the corresponding Xcode/Swift/UI version. iOS devices under iOS17 Are there any known problems with document based SwiftData-apps under iOS18, are there any changes one has to made? Thank You so much for any help!
Replies
6
Boosts
0
Views
803
Activity
Jan ’25
Custom Intent ParameterSummary based on Widget Kind/ID
I'm trying to create two widgets, widget A and B. Currently A and B are very similar so they share the same Intent and Intent Timeline Provider. I use the Intent Configuration interface to set a parameter, in this example lets say its the background tint. On one of the widgets, widget A, I want to also set another String enum parameter (for a timescale), but I don't want this option to be there for widget B as it's not relevant. I'm aware of some of the options for configuring the ParameterSummary, but none that let me pass in or inject the "kind" string (or widget ID) of the widget that's being modified. I'll try to provide some code for examples. My Widget Definition (targeting >= iOS 17) struct WidgetA: Widget { // I'd like to access this parameter within the intent let kind: String = "WidgetA" var body: some WidgetConfiguration { AppIntentConfiguration(kind: kind, intent: WidgetIntent.self, provider: IntentTimelineProvider()) { entry in WidgetView(data: entry) } .configurationDisplayName("Widget A") .description("A widget.") .supportedFamilies([.systemMedium, .systemLarge]) } } struct WidgetB: Widget { let kind: String = "WidgetB" var body: some WidgetConfiguration { AppIntentConfiguration(kind: kind, intent: WidgetIntent.self, provider: IntentTimelineProvider()) { entry in WidgetView(data: entry) } .configurationDisplayName("Widget B") .description("B widget.") .supportedFamilies([.systemMedium, .systemLarge]) } } struct IntentTimelineProvider: AppIntentTimelineProvider { typealias Entry = WidgetIntentTimelineEntry typealias Intent = WidgetIntent ........ } struct WidgetIntent: AppIntent, WidgetConfigurationIntent { // This intent allows configuration of the widget background // This intent also allows for the widget to display interactive buttons for changing the Trend Type static var title: LocalizedStringResource = "Widget Configuration" static var description = IntentDescription("Description.") static var isDiscoverable: Bool { return false} init() {} init(trend:String) { self.trend = trend } // Used for implementing interactive Widget func perform() async throws -> some IntentResult { print("WidgetIntent perform \(trend)") #if os(iOS) WidgetState.setState(type: trend) #endif return .result() } @Parameter(title: "Trend Type", default: "Trend") var trend:String // I only want to show this parameter for Widget A and not Widget B @Parameter(title: "Trend Timescale", default: .week) var timescale: TimescaleTypeAppEnum? @Parameter(title: "Background Tint", default: BackgroundTintTypeAppEnum.none) var backgroundTint: BackgroundTintTypeAppEnum? static var parameterSummary: some ParameterSummary { // Summary("Test Info") { // \.$timescale // \.$backgroundTint // } // An example of a configurable widget parameter summary, but not based of kind/ID string When(\.$backgroundTint, .equalTo, BackgroundTintTypeAppEnum.none) { Summary("Test Info") { \.$timescale \.$backgroundTint } } otherwise : { Summary("Test Info") { \.$backgroundTint } } } } enum TimescaleTypeAppEnum: String, AppEnum { case week case fortnight static var typeDisplayRepresentation = TypeDisplayRepresentation(name: "Trend Timescale") static var caseDisplayRepresentations: [Self: DisplayRepresentation] = [ .week: "Past Week", .fortnight: "Past Fortnight" ] } enum BackgroundTintTypeAppEnum: String, AppEnum { case blue case none static var typeDisplayRepresentation = TypeDisplayRepresentation(name: "Background Tint") static var caseDisplayRepresentations: [Self: DisplayRepresentation] = [ .none: "None (Default)", .blue: "Blue" ] } I know I could achieve what I'm after by having a separate Intent and separate IntentTimelineProvider for each widget. But this all seems unnessecary for just a simple optional parameter based on what widget its configuring.... unless I'm missing the point about Intents, Widgets or something! I've done a fair bit of other searching but can't find an answer to this overall scenario. Many thanks for any help.
Replies
2
Boosts
0
Views
549
Activity
Jan ’25
Animate colours in a SwiftUI Canvas
Pseudo-code: PhaseAnimator([false,true], trigger: foo) { flash in ZStack { Capsule() .foregroundStyle(flash ? .red : .green) Canvas { context, size in context.draw(image: Image(name: "foo"), toFitRect: some_rectangle); context.draw(text: Text("foo"), toFitRect: another_rectangle); } .foregroundStyle(flash ? .black : .white) } } animation: { flash in return .linear(duration: 0.5); } The Capsule's colour animates, but the Canvas's doesn't. The Canvas drawing code is only ever called with flash==false. What do I have to do to the Canvas so that it redraws with the intermediate colours during the animation?
Replies
3
Boosts
0
Views
410
Activity
Jan ’25
NSLayoutManager laying out overlapping text into the same NSTextContainer even when there are more containers available.
In summation: I have a nasty bug where my layout manager is laying out text visually overlapping on top of other text, i.e., into a container that it should have left in the rear view as it continues to lay out into ensuing containers. Details below... I'm coding a word processing app with some custom pagination that involves multiple pages, within which there can be multiple NSTextView/NSTextContainer pairs that represent single column or dual column runs of text. I generate pagination data by using a measuring NSLayoutManager. This process ensures that no containers overlap, and that they are sized correctly for their associated ranges of text (i.e., non-overlapping, continuous ranges from a single NSTextStorage). I determine frame sizes by a series of checks, most importantly, by finding the last glyph in a column. Prior to the code below, remainingColumnRange represents the remaining range of my textStorage that is of a consistent column type (i.e., single, left column, or right column). My measuring passes consist of my measuringLayoutManager laying out text into its textContainers, the final of which is an extra overflowContainer (i.e., == measuringLayoutManager.textContainers.last!) which I only use to find the last glyph in the second to last container (measuringContainer, which is thus == measuringLayoutManager.textContainers[count - 2]) let glyphRangeOfLastColumnChar = measuringLayoutManager.glyphRange(forCharacterRange: remainingColumnRange, actualCharacterRange: nil) let lastGlyphIndex = NSMaxRange(glyphRangeOfLastColumnChar) - 1 measuringLayoutManager.ensureLayout(for: measuringContainer) // Not sure if this is necessary, but I've added it to insure I'm getting accurate measurements. if measuringLayoutManager.textContainer(forGlyphAt: lastGlyphOfColumnIndex, effectiveRange: &actualGlyphRangeInContainer) == overflowContainer { actualCharRangeInContainer = measuringLayoutManager.characterRange(forGlyphRange: actualGlyphRangeInContainer, actualGlyphRange: nil) let overflowLoc = actualCharRangeInContainer.location remainingColumnRange = NSRange(location: overflowLoc, length: remainingColumnRange.length - overflowLoc) currentPage += 1 } else { lineFragmentRectForLastChar = measuringLayoutManager.lineFragmentRect(forGlyphAt: lastGlyphIndex, effectiveRange: nil) // Resize measuring container if needed. let usedHeight = lineFragmentRectForLastChar.maxY if usedHeight < measuringContainer.size.height { measuringContainer.size = CGSize(width: measuringContainer.size.width, height: usedHeight) } else if usedHeight == measuringContainer.size.height { currentPage += 1 // we perfectly filled the page } else { // This would be an error case, because all cases should have been handled prior to arriving here. I throw an error. I have never fallen through here. throw MyClass.anError } } // I use the above data to create a PageLayoutItem, which is a struct that has frame data (CGRect/x,y,w,h), a containerIndex (Int), pageNumber (Int), textRange (NSRange), columnType (custom enum). // After this I remove the overflowContainer, and continue to iterate through. This is inefficient but I'm simplifying my code to identify the root issue. I don't explicitly use these containers when done with my pagination process. Rather, I use the PageLayoutItems I have created to generate/resize/remove textContainers/textViews for the UI as needed. My UI-interfacing/generating NSLayoutManager, which is of course assigned to the same NSTextStorage as the measuring layout manager, then iterates through my paginator model class' pageLayoutItems array to generate/resize/remove. I have verified my pagination data. None of my frames overlap. They are sized exactly the same as they should be per my measurement passes. The number of containers/views needed is correct. But here's the issue: My views render the text that SHOULD appear in my final textContainer/textView as visually overlapping the text in my second to last textContainer/textView. I see a garble of text. When I iterate through my UI textContainers, I get this debug print: TextContainer 0 glyphRange: {0, 172} TextContainer 1 glyphRange: {172, 55} TextContainer 2 glyphRange: {227, 100} // this is wrong, final 31 chars should be in container 3 TextContainer 3 glyphRange: {327, 0} // empty range here, odd I have tried setting textContainers for glyph ranges explicitly, via: // Variable names just for clarity here layoutManager.setTextContainer(correctTextView.textContainer!, forGlyphRange: correctGlyphRangeForThisContainer) Debug prints show that I'm setting the right ranges there. But they don't retain. I have tried resizing my final text container to be much larger in case that was the issue. No dice. My final range of text/glyphs still lays out in the wrong container and overlaps the other content laid out there. Any help here?? I've scoured the forums and have been dealing with this bug for two weeks straight with no hope in sight.
Replies
4
Boosts
0
Views
1.1k
Activity
Jan ’25
The PKCanvasView Created by PDFPageOverlayViewProvider cannot work normally
By setting the PKCanvasView background color to blue, I can tell that the PKCanvasView for each PDFPage is created normally, but it does not respond to touch. Specifically, whether it is finger or applepencil, all the responses of the page occur from PDFView(such as zoom and scroll), and PKCanvasView can not draw, please how to solve? class PDFAnnotatableViewController: UIViewController, PDFViewDelegate { private let pdfView = PDFView() private var pdfDocument: PDFDocument? let file: FileItem private var userSettings: UserSettings @Binding var selectedPage: Int @Binding var currentMode: Mode @Binding var latestPdfChatResponse: LatestPDFChatResponse @State private var pdfPageCoordinator = PDFPageCoordinator() @ObservedObject var userMessage: ChatMessage init(file: FileItem, userSettings: UserSettings, drawDataList: Binding<[DrawDataItem]>, selectedPage: Binding<Int>, currentMode: Binding<Mode>, latestPdfChatResponse: Binding<LatestPDFChatResponse>, userMessage: ChatMessage) { self.file = file self.userSettings = userSettings self._selectedPage = selectedPage self._currentMode = currentMode self._latestPdfChatResponse = latestPdfChatResponse self.userMessage = userMessage super.init(nibName: nil, bundle: nil) DispatchQueue.global(qos: .userInitiated).async { if let document = PDFDocument(url: file.pdfLocalUrl) { DispatchQueue.main.async { self.pdfDocument = document self.pdfView.document = document self.goToPage(selectedPage: selectedPage.wrappedValue - 1) } } } } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func viewDidLoad() { super.viewDidLoad() setupPDFView() } private func setupPDFView() { pdfView.delegate = self pdfView.autoScales = true pdfView.displayMode = .singlePage pdfView.displayDirection = .vertical pdfView.backgroundColor = .white pdfView.usePageViewController(true) pdfView.displaysPageBreaks = false pdfView.displaysAsBook = false pdfView.minScaleFactor = 0.8 pdfView.maxScaleFactor = 3.5 pdfView.pageOverlayViewProvider = pdfPageCoordinator if let document = pdfDocument { pdfView.document = document goToPage(selectedPage: selectedPage) } pdfView.frame = view.bounds pdfView.autoresizingMask = [.flexibleWidth, .flexibleHeight] view.addSubview(pdfView) NotificationCenter.default.addObserver( self, selector: #selector(handlePageChange), name: .PDFViewPageChanged, object: pdfView ) } // Dealing with page turning @objc private func handlePageChange(notification: Notification) { guard let currentPage = pdfView.currentPage, let document = pdfView.document else { return } let currentPageIndex = document.index(for: currentPage) if currentPageIndex != selectedPage - 1 { DispatchQueue.main.async { self.selectedPage = currentPageIndex + 1 } } } func goToPage(selectedPage: Int) { guard let document = pdfView.document else { return } if let page = document.page(at: selectedPage) { pdfView.go(to: page) } } // Switch function func togglecurrentMode(currentMode: Mode){ DispatchQueue.main.async { if self.currentMode == .none{ self.pdfView.usePageViewController(true) self.pdfView.isUserInteractionEnabled = true } else if self.currentMode == .annotation { if let page = self.pdfView.currentPage { if let canvasView = self.pdfPageCoordinator.getCanvasView(forPage: page) { canvasView.isUserInteractionEnabled = true canvasView.tool = PKInkingTool(.pen, color: .red, width: 20) canvasView.drawingPolicy = .anyInput canvasView.setNeedsDisplay() } } } } } } class MyPDFPage: PDFPage { var drawing: PKDrawing? func setDrawing(_ drawing: PKDrawing) { self.drawing = drawing } func getDrawing() -> PKDrawing? { return self.drawing } } class PDFPageCoordinator: NSObject, PDFPageOverlayViewProvider { var pageToViewMapping = [PDFPage: PKCanvasView]() func pdfView(_ view: PDFView, overlayViewFor page: PDFPage) -> UIView? { var resultView: PKCanvasView? = nil if let overlayView = pageToViewMapping[page] { resultView = overlayView } else { let canvasView = PKCanvasView(frame: view.bounds) canvasView.drawingPolicy = .anyInput canvasView.tool = PKInkingTool(.pen, color: .systemYellow, width: 20) canvasView.backgroundColor = .blue pageToViewMapping[page] = canvasView resultView = canvasView } if let page = page as? MyPDFPage, let drawing = page.drawing { resultView?.drawing = drawing } return resultView } func pdfView(_ pdfView: PDFView, willEndDisplayingOverlayView overlayView: UIView, for page: PDFPage) { guard let overlayView = overlayView as? PKCanvasView, let page = page as? MyPDFPage else { return } page.drawing = overlayView.drawing pageToViewMapping.removeValue(forKey: page) } func savePDFDocument(_ pdfDocument: PDFDocument) -> Data { for i in 0..<pdfDocument.pageCount { if let page = pdfDocument.page(at: i) as? MyPDFPage, let drawing = page.drawing { let newAnnotation = PDFAnnotation(bounds: drawing.bounds, forType: .stamp, withProperties: nil) let codedData = try! NSKeyedArchiver.archivedData(withRootObject: drawing, requiringSecureCoding: true) newAnnotation.setValue(codedData, forAnnotationKey: PDFAnnotationKey(rawValue: "drawingData")) page.addAnnotation(newAnnotation) } } let options = [PDFDocumentWriteOption.burnInAnnotationsOption: true] if let resultData = pdfDocument.dataRepresentation(options: options) { return resultData } return Data() } func getCanvasView(forPage page: PDFPage) -> PKCanvasView? { return pageToViewMapping[page] } } Is there an error in my code? Please tell me how to make PKCanvasView painting normally?
Replies
1
Boosts
0
Views
474
Activity
Feb ’25
Issue: Chart Scroll Not Working in iOS 18 with Chart Overlay Tap Gesture
Summary I have a SwiftUI Chart that worked correctly in iOS 17, allowing both horizontal scrolling and tap gesture selection. However, in iOS 18, the same exact chart will not allow for both tap gestures and scrolling to work -- it's either we allow scrolling or we allow tap gestures but not both. We have tried everything to try to circumvent this issue but have had to resort to old methods of creating the chart. This is an issue that has negatively impacted our customers as well. Again, the charts were working fine on iOS 17, but on iOS 18 the chart scroll + tap gesture capability is not working. Expected Behavior (iOS 17) Users can scroll horizontally through the chart. Users can tap on data points to highlight them. The selected data point updates when tapped. Observed Behavior (iOS 18) The chart no longer scrolls when chartOverlay with the Tap Gesture is applied. Tap selection still works as expected. Code Snippet Below is the working implementation from iOS 17: private var iOS17ChartView: some View { Chart { RectangleMark( yStart: .value(String(firstLevelAlertBand), firstLevelAlertBand), yEnd: .value("100", 100) ) .foregroundStyle(Theme.Colors.green.opacity(0.15)) RectangleMark( yStart: .value(String(secondLevelAlertBand), secondLevelAlertBand), yEnd: .value(String(firstLevelAlertBand), firstLevelAlertBand) ) .foregroundStyle(Theme.Colors.orange.opacity(0.15)) RectangleMark( yStart: .value("0", 0), yEnd: .value(String(secondLevelAlertBand), secondLevelAlertBand) ) .foregroundStyle(Theme.Colors.red.opacity(0.15)) ForEach(telemetryData, id: \.timestamp) { entry in if let utcDate = dateFormatter.date(from: entry.timestamp) { let localDate = convertToUserTimeZone(date: utcDate) let tankLevel = entry.tankLevel ?? 0 LineMark( x: .value("Date", localDate), y: .value("Tank Level", tankLevel) ) .foregroundStyle(statusColor) AreaMark( x: .value("Date", localDate), y: .value("Tank Level", tankLevel) ) .foregroundStyle(statusColor.opacity(0.50)) PointMark( x: .value("Date", localDate), y: .value("Tank Level", tankLevel) ) .foregroundStyle(selectedDataPoint?.date == localDate ? Theme.Colors.primaryColor : statusColor) .symbolSize(selectedDataPoint?.date == localDate ? 120 : 80) PointMark( x: .value("Date", localDate), y: .value("Tank Level", tankLevel) ) //.foregroundStyle(.white).symbolSize(10) .foregroundStyle(Theme.Colors.white(colorScheme: colorScheme)) .symbolSize(12) } } } .chartXScale(domain: (firstTimestamp ?? Date())...(latestTimestamp ?? Date())) .chartXVisibleDomain(length: visibleDomainSize) .chartScrollableAxes(.horizontal) .chartScrollPosition(x: $chartScrollPositionX) .chartXAxis { AxisMarks(values: .stride(by: xAxisStrideUnit, count: xAxisCount())) { value in if let utcDate = value.as(Date.self) { let localDate = convertToUserTimeZone(date: utcDate) let formatStyle = self.getFormatStyle(for: interval) AxisValueLabel { Text(localDate, format: formatStyle) .font(Theme.Fonts.poppinsRegularExtraSmall) .foregroundStyle(Theme.Colors.black(colorScheme: colorScheme)) } AxisTick() .foregroundStyle(Theme.Colors.black(colorScheme: colorScheme).opacity(1)) } } } .chartOverlay { proxy in GeometryReader { geometry in Rectangle().fill(Color.clear).contentShape(Rectangle()) .onTapGesture { location in let xPosition = location.x - geometry[proxy.plotAreaFrame].origin.x // Use proxy to get the x-axis value at the tapped position if let selectedDate: Date = proxy.value(atX: xPosition) { if let closestEntry = telemetryData.min(by: { abs(dateFormatter.date(from: $0.timestamp)!.timeIntervalSince1970 - selectedDate.timeIntervalSince1970) < abs(dateFormatter.date(from: $1.timestamp)!.timeIntervalSince1970 - selectedDate.timeIntervalSince1970) }) { selectedDataPoint = (convertToUserTimeZone(date: dateFormatter.date(from: closestEntry.timestamp)!), closestEntry.tankLevel ?? 0) if let dateXPos = proxy.position(forX: convertToUserTimeZone(date: dateFormatter.date(from: closestEntry.timestamp)!)), let tankLevelYPos = proxy.position(forY: closestEntry.tankLevel ?? 0) { // Offset the x-position based on the scroll position let adjustedXPos = dateXPos - proxy.position(forX: chartScrollPositionX)! withAnimation(.spring()) { selectedPointLocation = CGPoint(x: adjustedXPos, y: tankLevelYPos - 60) // Offset popup above the point showPopup = true } } } } } } .onChange(of: chartScrollPositionX) { newValue in // Dynamically update the popup position when scroll changes if let selectedDataPoint = selectedDataPoint { if let dateXPos = proxy.position(forX: selectedDataPoint.date) { let adjustedXPos = dateXPos - proxy.position(forX: chartScrollPositionX)! selectedPointLocation.x = adjustedXPos } } } } } Please help! Nick
Replies
1
Boosts
1
Views
636
Activity
Feb ’25
.presentationDetents not working on iOS 17
Hello, I have the following code: .sheet(isPresented: $viewModel.isExerciseSelected) { ExerciseEditSheetView(viewModel: viewModel) .presentationDetents([.fraction(0.4)]) } This code correctly sizes the sheet on my iOS 18 simulator, but does not work on my iOS 17 simulator as well as my actual phone running iOS 17. Instead the sheet always fullscreens. Although its not the end of the world it is pretty annoying. Is there anything I'm missing? Thanks in advance.
Topic: UI Frameworks SubTopic: SwiftUI
Replies
2
Boosts
0
Views
323
Activity
Feb ’25
Since iOS 18.3, icons are no longer generated correctly with QLThumbnailGenerator
Since iOS 18.3, icons are no longer generated correctly with QLThumbnailGenerator. No error is returned either. But this error message now appears in the console: Error returned from iconservicesagent image request: <ISTypeIcon: 0x3010f91a0>,Type: com.adobe.pdf - <ISImageDescriptor: 0x302f188c0> - (36.00, 36.00)@3x v:1 l:5 a:0:0:0:0 t:() b:0 s:2 ps:0 digest: B19540FD-0449-3E89-AC50-38F92F9760FE error: Error Domain=NSOSStatusErrorDomain Code=-609 "Client is disallowed from making such an icon request" UserInfo={NSLocalizedDescription=Client is disallowed from making such an icon request} Does anyone know this error? Is there a workaround? Are there new permissions to consider? Here is the code how icons are generated: let request = QLThumbnailGenerator.Request(fileAt: url, size: size, scale: scale, representationTypes: self.thumbnailType) request.iconMode = true let generator = QLThumbnailGenerator.shared generator.generateRepresentations(for: request) { [weak self] thumbnail, _, error in }
Replies
16
Boosts
5
Views
1.7k
Activity
Feb ’25
HealthKit heart rate zones
I would like to get user's heart rate zones values in my watchOS app, is there any way to get it from HealthKit? Furthermore, is it possible to display the Apple heart rate zones widget in my watchOS app?
Replies
2
Boosts
0
Views
368
Activity
Feb ’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.
Replies
2
Boosts
0
Views
539
Activity
Feb ’25
Simulating key press event to type text in UITextField
in iOS, user can set focus on UItextField and tapping a key in the virtual keyboard updates the text in the textfield. This user action causes the relevant delegates of UITextFieldDelegate to get invoked, i.e the handlers associated with action of user entering some text in the textfield. I m trying to simulate this user action where I am trying to do this programatically. I want to simulate it in a way such that all the handlers/listeners which otherwise would have been invoked as a result of user typing in the textfield should also get invoked now when i am trying to do it programatically. I have a specific usecase of this in my application. Below is how I m performing this simulation. I m manually updating the text field associated(UITextField.text) and updating its value. And then I m invoking the delegate manually as textField.delegate?.textField?(textField, shouldChangeCharactersIn: nsRange, replacementString: replacementString) I wanted to know If this is the right way to do this. Is there something better available that can be used, such that simulation has the same affect as the user performing the update?
Topic: UI Frameworks SubTopic: UIKit Tags:
Replies
4
Boosts
0
Views
403
Activity
Feb ’25
Swift Charts: How to prevent scroll position jump when loading more data dynamically
I'm implementing infinite scrolling with Swift Charts where additional historical data loads when scrolling near the beginning of the dataset. However, when new data is loaded, the chart's scroll position jumps unexpectedly. Current behavior: Initially loads 10 data points, displaying the latest 5 When scrolling backwards with only 3 points remaining off-screen, triggers loading of 10 more historical points After loading, the scroll position jumps to the 3rd position of the new dataset instead of maintaining the current view Expected behavior: Scroll position should remain stable when new data is loaded User's current view should not change during data loading Here's my implementation logic using some mock data: import SwiftUI import Charts struct DataPoint: Identifiable { let id = UUID() let date: Date let value: Double } class ChartViewModel: ObservableObject { @Published var dataPoints: [DataPoint] = [] private var isLoading = false init() { loadMoreData() } func loadMoreData() { guard !isLoading else { return } isLoading = true let newData = self.generateDataPoints( endDate: self.dataPoints.first?.date ?? Date(), count: 10 ) self.dataPoints.insert(contentsOf: newData, at: 0) self.isLoading = false print("\(dataPoints.count) data points.") } private func generateDataPoints(endDate: Date, count: Int) -> [DataPoint] { var points: [DataPoint] = [] let calendar = Calendar.current for i in 0..<count { let date = calendar.date( byAdding: .day, value: -i, to: endDate ) ?? endDate let value = Double.random(in: 0...100) points.append(DataPoint(date: date, value: value)) } return points.sorted { $0.date < $1.date } } } struct ScrollableChart: View { @StateObject private var viewModel = ChartViewModel() @State private var scrollPosition: Date @State private var scrollDebounceTask: Task<Void, Never>? init() { self.scrollPosition = .now.addingTimeInterval(-4*24*3600) } var body: some View { Chart(viewModel.dataPoints) { point in BarMark( x: .value("Time", point.date, unit: .day), y: .value("Value", point.value) ) } .chartScrollableAxes(.horizontal) .chartXVisibleDomain(length: 5 * 24 * 3600) .chartScrollPosition(x: $scrollPosition) .chartXScale(domain: .automatic(includesZero: false)) .frame(height: 300) .onChange(of: scrollPosition) { oldPosition, newPosition in scrollDebounceTask?.cancel() scrollDebounceTask = Task { try? await Task.sleep(for: .milliseconds(300)) if !Task.isCancelled { checkAndLoadMoreData(currentPosition: newPosition) } } } } private func checkAndLoadMoreData(currentPosition: Date?) { guard let currentPosition, let earliestDataPoint = viewModel.dataPoints.first?.date else { return } let timeInterval = currentPosition.timeIntervalSince(earliestDataPoint) if timeInterval <= 3 * 24 * 3600 { viewModel.loadMoreData() } } } I attempted to compensate for this jump by adding: scrollPosition = scrollPosition.addingTimeInterval(10 * 24 * 3600) after viewModel.loadMoreData(). However, this caused the chart to jump in the opposite direction by 10 days, rather than maintaining the current position. What's the problem with my code and how to fix it?
Replies
6
Boosts
0
Views
601
Activity
Feb ’25