Post

Replies

Boosts

Views

Activity

Reply to TextKit 2 calls NSTextLayoutFragment's draw method too often
Thank you for clarifying @DTS Engineer. I have submitted more information and clarified some misunderstandings in my feedback report. This info is especially useful to know: The view re-draw process is driven by the UI framework, and so there is nothing you can do at the TextKit level. Though, since I'm in the default UITextView zone, I'm not sure I can do much to optimize drawing here. I'm hoping to get some more info from Apple that could help me avoid redundant drawing, as I'd rather avoid building custom text views. As for performance regressions in my app: I have already analyzed the CPU usage and optimized it quite a bit. This process was performed in several stages, last of which was to try and optimize by utilizing the viewport, and while working on that I have figured the draw is called many times. (Just for reference, the initial implementation I had was resulting in about 10-15 FPS while scrolling, and the code I used wasn't that bad; it was just called way too often.) The logic I have in the draw(at:in:) method override is very optimized now, but as you can imagine, even the most optimized variant could run slowly when called many times, especially for bigger paragraphs.
Topic: UI Frameworks SubTopic: UIKit Tags:
Aug ’25
Reply to TextKit 2 calls NSTextLayoutFragment's draw method too often
@DTS Engineer thank you for confirming the behavior on other system versions. Upon reading your response, I realized how my original post could be misinterpreted. I haven't meant that draw(at:in:) gets called for all paragraphs that exist in the document at all times during scrolling. It is correct that TextKit 2 only renders those paragraphs that are in the viewport. However, for those paragraphs that are in the viewport, the draw method gets called many times ("on every scroll step in the UITextView"). That is what you indicated with: ... (Repeats many times.) in your logs above. This seems incorrect because the behavior is different between last couple iOS versions, and between the paragraphs in the same text (or even between texts, it seems). For example, when I test this demo app on iOS 17 (iPhone 13 Pro), the draw method was called only once for every paragraph as they were entering the viewport, and the draw method for those was called next time when the paragraph would leave and then enter the viewport again. Only for the 2nd paragraph was the draw method called on every scroll step while it is in the viewport. Interestingly, if I edit textContents (from my demo app) to only have a single paragraph whose length is ~9k characters, I have a single paragraph whose draw is called exactly once. I can scroll through the text as much as I want, and draw is never called again. This makes me thing that draw could be called only once (since text is rendered without any issues), while on the other hand you say: relevant fragments are drawn a lot of times, but that's part of the drawing process of updating the whole view port. So I'm not sure what's correct. Though based on CPU spikes I'm facing, I must say this seems strange.
Topic: UI Frameworks SubTopic: UIKit Tags:
Aug ’25
Reply to TextKit 2 calls NSTextLayoutFragment's draw method too often
Hello @DTS Engineer, No, this is definitely not expected Since you answer was specifically quoting iOS 18 and iOS 26 beta 4, what about the older iOS versions? If the behavior expected on those versions? I am curious how you confirmed the behavior though. I have confirmed this behavior by returning a custom NSTextLayoutFragment subclass in NSTextLayoutManager's delegate method textLayoutManager(_:textLayoutFragmentFor:in:). The layout fragment subclass then executes a print statement in its draw(at:in:) method override. Do you have a test project that unveils the issue to share? I just created a demo project for you. I've attached it to my feedback report, but I've also uploaded it to GitHub: https://github.com/galijot/NSTextLayoutFragment-PerformanceIssues I've faced slightly different results today on iOS 17 in this demo project, where just the 2nd paragraph's draw(at:in:) would be called on every scroll step, and other paragraphs would be rendered once per its appearance in the viewport. iOS 18 still calls it on each scroll step, on all paragraphs.
Topic: UI Frameworks SubTopic: UIKit Tags:
Aug ’25
Reply to iOS 26 how to disable swipe to navigate back
I haven't tested this to be sure, but if you are in UIKit, you'd want to look into UIGestureRecognizerDelegate and its methods through which you control which gesture should fail in favor of another gesture. For this to work, you'd have to be the delegate of UINavigationController's interactivePopGestureRecognizer - which again I haven't tested, but I'm assuming Apple hadn't created a brand-new gesture for this iOS 26's swipe-anywhere-to-go-back gesture. (I'm uncertain about SwiftUI though.)
Topic: UI Frameworks SubTopic: UIKit
Jul ’25
Reply to CATiledLayer flashes and re-draws entirely when re-drawing a single tile
Thanks @Etresoft. I'm aware the two references I posted are over a decade old; I went even further when exploring this, since the class itself was written long time ago, and it's not a common thing to find resources for. Interestingly though, I haven't found this SCTiledImage previously! I'll have to give it a shot. Thanks for clarifying Core Image and Metal usage. I have used both Metal and Core Image for various things previously, but for my current use-case, CATiledLayer is indeed the perfect fit - or correct me if you think otherwise. My use-case is displaying a very large image, which I do by rendering it in tiles. The new use-case I have is that tiles should be downloaded from a server, where they are pre-generated. @DTS Engineer thanks a lot for the info! I have created a demo project, attached it to the report I have submitted, and I've uploaded it to GitHub as well. Here it is: https://github.com/galijot/CATiledLayer-flashing-demo Note: flashing that I'm describing is the result of CATiledLayer (from my assumption) discarding its current contents and asynchronously re-drawing the whole view. When reloading many tiles (one after each is downloaded), this results in flashing. Oh and btw: in my original post I accidentally wrote "iPhone 5s" instead of "iPhone 6s" (not that it probably matters much, but still). The rest of this message will be verbatim of some more info I have posted on the report. The project has two print statements which are crucial for debugging this: "draw at position" which is printed in draw(_:) method of TiledView, which prints the position (X,Y and LOD) that it is about to draw, as well as the rect in which it draws. "reload at position" which is printed in reloadTile(at:), which prints the position that it is about to re-draw, as well as the calculated rect for the tile. You can search for "FIXME" in the project, which will lead you to the code that displays 4 possible options for testing this: re-drawing with setNeedsDisplayInRect re-drawing with setNeedsDisplay caching layer's contents and applying them after setNeedsDisplayInRect caching layer's contents and applying them after setNeedsDisplay
Topic: UI Frameworks SubTopic: General Tags:
May ’25
Reply to iOS 18: Detect iPhone Mirroring
For anyone who's interested, Apple has replied to my feedback with a rejection: Thank you for your feedback, it is noted. Engineering has determined that there are currently no plans to address this issue. You can close this feedback by selecting "Close Feedback" via the Actions button found above. This Feedback will no longer be monitored, and incoming messages will not be reviewed.
Sep ’24
Reply to UITableView dataSource is not set (17.4.1 (21E236) only)
We're facing the same crash in our iOS app. Interestingly, we set the tableView's dataSource to self in a view controller class in the method in which the crash is reported - so it's very weird seeing that it is nil. We have: if tableView.dataSource == nil { tableView.dataSource = self /* some other logic */ } tableView.reloadData() tableView.layoutIfNeeded() There is a catch, however: after this code, we need to wait for one run loop cycle using DispatchQueue.main.async, for the layout to complete (hence layoutIfNeeded call), after which scrollToRow is attempted. In DispatchQueue.main.async's closure, table view is weakly captured, so it should be non-nil if the view controller is non-nil, as nothing else is retaining it. Memory of every instance in this view controller is carefully managed. Once the view controller gets removed from its navigation controller, everything goes out of memory; no retain cycles, no work being performed after the controller is deallocated. Combine is used for async tasks and every task gets cancelled once the view controller is deinitialized. There may be a mistake in our code that we haven't found yet, but I'm reporting this since I'm seeing we're not the only one with the issue. The crash so far occurred only on iOS 17.5.1
Topic: UI Frameworks SubTopic: UIKit Tags:
Jul ’24