Construct and manage graphical, event-driven user interfaces for iOS or tvOS apps using UIKit.

Posts under UIKit tag

200 Posts
Sort by:

Post

Replies

Boosts

Views

Activity

Zooming between SwiftUI and Storyboards
This is probably abusing the system more than it should be but maybe it is somehow possible. I have: An objective-C based storyboard iPad OS app. I'm beginning to adopt SwiftUI. I have a hosting controller with a content view that has a lazygrid of cards, which have an NSManagedObject for data. On tapping a card, a detail view opens, if in multi-tasking, a new window, if not, pushing the navigation controller (this detail view still exists in UIKit/ObjC, and is handled by sending a notification with the ObjectID, which then triggers a storyboard segue to the detail.) I have zoom transitions on all my things. They work great in Obj.C, especially now with the bar button source. On my iPhone target, I still have an old tableview, and I'm able to zoom properly - if someone changes the detail view's managed object (through a history menu), the zoom context looks up where the tableview is, and scrolls to it while popping. I'd like to somehow do this on the lazygrid - first) to just have an individual card be the zoom source, it should be able to know what the source view is to say in the prepareForSegue method just to zoom at all. and second) if the detail has changed the current ObjectID (which gets passed around as a notification), to somehow scroll the lazygrid to the right object before popping. I've looked at https://developer.apple.com/tutorials/SwiftUI/interfacing-with-uikit but this seems like swiftUI is the host. I have it the other way around, uikit hosting swiftUI pushing uikit. TIA for any pointers
1
0
79
1w
UIGraphicsImageRenderer display blank image with iOS 26
Hello, First of all, I've already made a bug report here : https://feedbackassistant.apple.com/feedback/19731998 I'm facing a problem while using UIGraphicsImageRenderer to create an image, that is use to create a UIColor with a pattern via UIColor(patternImage:). It's well displayed for iOS 18.2 and lower, whereas the whole color is blank with iOS 26. -> Please find a sample project linked to the bug report ViewController.swift post that illustrates the issue, in the ViewController.swift file. I'll also link screenshots of the sample app, one built with iOS 18.2 and another with iOS 26.0. Reproduction steps : I create an image with UIGraphicsImageRenderer : let image = UIGraphicsImageRenderer().image { context in // Do anything here } Then I use this image to create a UIColor : UIColor(patternImage: image) I apply this color to the fillColor of a CAShapeLayer : shapeLayer.fillColor = UIColor(patternImage: image) Expected result : Run on iOS 26 and lower and the layer filled with the pattern color is correctly displayed, as it is on iOS 18.2 and lower. Observed result : Run on iOS 26 and the layer is filled with a blank/white color instead of the intended pattern color. Has anyone been facing the problem ? Thanks, Thibault Poujat
Topic: UI Frameworks SubTopic: UIKit Tags:
1
0
51
1w
iOS 26 (beta) Nav Bar Item Image Insets bug
I've noticed that in iOS 26, Navigation Bar Items' Image Insets parameters as set in Xcode are not being read correctly. Specifically, it appears that on iOS 26 beta, negative inset numbers are being read as positive. Feedback report FB19838333 includes a sample project demonstrating this bug.
0
0
130
2w
Compositional Layout's broken `visibleItemsInvalidationHandler`
Hello everybody! TLDR: Issues with visibleItemsInvalidationHandler. Minimal code to reproduce available. I've been working with Compositional Layout for a while now and recently I've found myself needing to implement custom animation based on scroll position of UI elements. Once I found visibleItemsInvalidationHandler it felt like the exact solution that I needed. Once I implement I've found out it doesn't quite behave as you'd expect. To put it simply, it seems like the animations only work if your whole layout does not use .estimated nor .uniformAcrossSiblings. As soon as you use them then the animations will stop working, I've debugged it deeper and it seems like the invalidation context generated by it does not include the indexPath of the cells, which is always included in the version in which it works. Feel free to swap the line 51 with its comment to flip between the working and failing version of it. Playground Example My final question therefore is... Is this the expected behavior? The documentation doesn't give any clues about such behavior and although I've tried relentlessly to find a workaround for this specific hiccup I was not successful with it.
1
0
96
2w
TextKit 2 calls NSTextLayoutFragment's draw method too often
The following is verbatim of a feedback report (FB19809442) I submitted, shared here as someone else might be interested to see it (I hate the fact that we can't see each other's feedbacks). On iOS 16, TextKit 2 calls NSTextLayoutFragment's draw(at:in:) method once for the first paragraph, but for every other paragraph, it calls it continuously on every scroll step in the UITextView. (The first paragraph is not cached; its draw is called again when it is about to be displayed again, but then it is again called only once per its lifecycle.) On iOS 17, the behavior is similar; the draw method gets called once for the 1st and 2nd paragraph, and for every other paragraph it again gets called continuously as a user scrolls a UITextView. On iOS 18 (and iOS 26 beta 4), TextKit 2 calls the layout fragment's draw(at:in:) on every scroll step in the UITextView, for all paragraphs. This results in terrible performance. TextKit 2 is promised to bring many performance benefits by utilizing the viewport - a new concept that represents the visible area of a text view, along with a small overscroll. However, having the draw method being constantly called almost negates all the performance benefits that viewport brings. Imagine what could happen if someone needs to add just a bit of logic to that draw method. FPS drops significantly and UX is terribly degraded. I tried optimizing this by only rendering those text line fragments which are in the viewport, by using NSTextViewportLayoutController.viewportBounds and converting NSTextLineFragment.typographicBounds to the viewport-relative coordinate space (i.e. the coordinate space of the UITextView itself). However, this patch only works on iOS 18 where the draw method is called too many times, as the viewport changes. (I may have some other problems in my implementation, but I gave up on improving those, as this can't work reliably on all OS versions since the underlying framework isn't calling the method consistently.) Is this expected? What are our options for improving performance in these areas?
6
0
143
1w
SwiftData Fatal error: Editors must register their identifiers before invoking operations on this store
I have a UIKit app where I've adopted SwiftData and I'm struggling with a crash coming in from some of my users. I'm not able to reproduce it myself and as it only happens to a small fraction of my user base, it seems like a race condition of some sort. This is the assertion message: SwiftData/DefaultStore.swift:453: Fatal error: API Contract Violation: Editors must register their identifiers before invoking operations on this store SwiftData.DefaultStore: 00CF060A-291A-4E79-BEC3-E6A6B20F345E did not. (ID is unique per crash) This is the ModelActor that crashes: @available(iOS 17, *) @ModelActor actor ConsumptionDatabaseStorage: ConsumptionSessionStorage { struct Error: LocalizedError { var errorDescription: String? } private let sortDescriptor = [SortDescriptor(\SDConsumptionSession.startTimeUtc, order: .reverse)] static func createStorage(userId: String) throws -> ConsumptionDatabaseStorage { guard let appGroupContainer = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: UserDefaults.defaultAppGroupIdentifier) else { throw Error(errorDescription: "Invalid app group container ID") } func createModelContainer(databaseUrl: URL) throws -> ModelContainer { return try ModelContainer(for: SDConsumptionSession.self, SDPriceSegment.self, configurations: ModelConfiguration(url: databaseUrl)) } let databaseUrl = appGroupContainer.appendingPathComponent("\(userId).sqlite") do { return self.init(modelContainer: try createModelContainer(databaseUrl: databaseUrl)) } catch { // Creating the model storage failed. Remove the database file and try again. try? FileManager.default.removeItem(at: databaseUrl) return self.init(modelContainer: try createModelContainer(databaseUrl: databaseUrl)) } } func isStorageEmpty() async -> Bool { (try? self.modelContext.fetchCount(FetchDescriptor<SDConsumptionSession>())) ?? 0 == 0 // <-- Crash here! } func sessionsIn(interval: DateInterval) async throws -> [ConsumptionSession] { let fetchDescriptor = FetchDescriptor(predicate: #Predicate<SDConsumptionSession> { sdSession in if let startDate = sdSession.startTimeUtc { return interval.start <= startDate && interval.end > startDate } else { return false } }, sortBy: self.sortDescriptor) let consumptionSessions = try self.modelContext.fetch(fetchDescriptor) // <-- Crash here! return consumptionSessions.map { ConsumptionSession(swiftDataSession: $0) } } func updateSessions(sessions: [ConsumptionSession]) async throws { if #unavailable(iOS 18) { // Price segments are duplicated if re-inserted so unfortunately we have to delete and reinsert sessions. // On iOS 18, this is enforced by the #Unique macro on SDPriceSegment. let sessionIds = Set(sessions.map(\.id)) try self.modelContext.delete(model: SDConsumptionSession.self, where: #Predicate<SDConsumptionSession> { sessionIds.contains($0.id) }) } for session in sessions { self.modelContext.insert(SDConsumptionSession(consumptionSession: session)) } if self.modelContext.hasChanges { try self.modelContext.save() } } func deleteAllSessions() async { if #available(iOS 18, *) { try? self.modelContainer.erase() } else { self.modelContainer.deleteAllData() } } } The actor conforms to this protocol: protocol ConsumptionSessionStorage { func isStorageEmpty() async -> Bool func hasCreditCardSessions() async -> Bool func sessionsIn(interval: DateInterval) async throws -> [ConsumptionSession] func updateSessions(sessions: [ConsumptionSession]) async throws func deleteAllSessions() async } The crash is coming in from line 30 and 41, in other words, when trying to fetch data from the database. There doesn't seem to be any common trait for the crashes. They occur across iOS versions and device types. Any idea what might cause this?
4
0
144
2w
Do we need to explicitly register all high-level interaction events for every widget in UIKit?
I have a question about how UIKit expects us to handle interaction events at scale. From what I understand so far: For UIControls (UIButton, UISwitch, UITextField, etc.), we explicitly register with addTarget(_:action:for:). For gestures, we add UIGestureRecognizer instances to views. For UIView subclasses, we can override touch methods like touchesBegan/touchesEnded. All of this must be done on the main thread, since UIKit isn’t thread-safe. Now here’s my main concern If I have a complex UI with hundreds or thousands of widgets, am I expected to perform these registrations individually for each widget and each high-level event (tap, long press, editing changed, etc.)? Or does UIKit provide a more centralized mechanism? In short: Is per-widget, per-event registration the “normal” UIKit approach, or are there best practices for scaling event handling without writing thousands of addTarget or addGestureRecognizer calls? Thanks!
Topic: UI Frameworks SubTopic: UIKit Tags:
3
0
91
2w
Summary of iOS/iPadOS 26 UIKit bugs related to UISearchController & UISearchBar using scope buttons
All of these issues appear when the search controller is set on the view controller's navigationItem and the search controller's searchBar has its scopeButtonTitles set. So far the following issues are affecting my app on iOS/iPadOS 26 as of beta 7: When the scopeBarActivation of UISearchController is set to .onSearchActivation, the preferredSearchBarPlacement of the navigationItem is set to .integratedButton, and the searchBarPlacementAllowsToolbarIntegration is set to false (forcing the search icon to appear in the nav bar), on both iPhones and iPads, the scope buttons never appear. They don't appear when the search is activated. They don't appear when any text is entered into the search bar. FB19771313 I attempted to work around that issue by setting the scopeBarActivation to .manual. I then show the scope bar in the didPresentSearchController delegate method and hide the scope bar in the willDismissSearchController. On an iPhone this works though the display is a bit clunky. On an iPad, the scope bar does appear via the code in didPresentSearchController, but when any scope bar button is tapped, the search controller is dismissed. This happens when the app is horizontally regular. When the app on the iPad is horizontally compact, the buttons work but the search bar's text is not correctly aligned within the search bar. Quite the mess really. I still need to post a bug report for this issue. But if issue 1 above is fixed then I don't need this workaround. When the scopeBarActivation of UISearchController is set to .onSearchActivation, the preferredSearchBarPlacement of the navigationItem is set to .stacked, and the hidesSearchBarWhenScrolling property of the navigationItem is set to false (always show the search bar), and this is all used in a UITableViewController, then upon initial display of the view controller on an iPhone or iPad, you are unable to tap on the first row of the table view except on the very bottom of the row. The currently hidden scope bar is stealing the touches. If you activate and then cancel the search (making the scope bar appear and then disappear) then you are able to tap on the first row as expected. The initially hidden scope bar also bleeds through the first row of the table. It's faint but you can tell it's not quite right. Again, this is resolved by activating and then canceling the search once. FB17888632 When the scopeBarActivation of UISearchController is set to .onSearchActivation, the preferredSearchBarPlacement of the navigationItem is set to integrated or .integratedButton, and the toolbar is shown, then on iPhones (where the search bar/icon appears in the toolbar) the scope buttons appear (at the top of the screen) the first time the search is activated. But if you cancel the search and then activate it again, the search bar never appears a second (or later) time. On an iPad the search bar/icon appears in the nav bar and you end up with the same issue as #1 above. FB17890125 Issues 3 and 4 were reported against beta 1 and still haven't been fixed. But if issue 1 is resolved on iPhone, iPad, and Mac (via Mac Catalyst), then I personally won't be affected by issues 2, 3, or 4 any more (but of course all 4 issues need to be fixed). And by resolved, I mean that the scope bar appears and disappears when it is supposed to each and every time the search is activated and cancelled (not just the first time). The scope bar doesn't interfere with touch events upon initial display of the view controller. And there are no visual glitches no matter what the horizontal size class is on an iPad. I really hope the UIKit team can get these resolved before iOS/iPadOS 26 GM.
1
1
105
1w
Display segmented control in iOS 26 with the previous rounded rectangle style
The default style for a segmented picker in iOS 26 is now a capsule shape. Is it possible to get back the previous rounded rectangle shape? In SwiftUI I have tried using the clipShape, containerShape, and background modifiers, applying a RoundedRectangle shape with a corner radius, to no avail. In UIKit I have tried to adjust the corner configuration for the UISegmentedControl, also to no avail. Am I missing something really obvious or is it impossible to change the style here? 🤔
2
0
141
2w
UITabBarController bottom accessory doesn't resize properly when horizontal size class changes from compact to regular
A bottom accessory view is set on the UITabBarController. When changing the window size either by dragging the resizing grip or when going to portrait mode, the accessory view shrinks down to the smaller width. When resizing the window to make it larger, the accessory view doesn’t resize to the full available width. During a workshop setup by Apple, folks from Apple told me that the view set as the content view of the UITabAccessory should not have its frame changed, either by using Auto Layout or by setting the frame. It seems logical since the view in the bottom accessory is supposed to resize accordingly to several factors, like when going inline inside the tab bar. Am I missing something? Maybe there is additional setup required not mentioned in the dedicated video. Feedback is FB19017330. It contains a sample project and videos demonstrating the issue. Reproduced on Xcode 26 beta 6 (17A5305f) I copy and paste the sample code that setups the bottom accessory for good measure. final class MiniPlayer: UIView { let label = UILabel() override init(frame: CGRect) { super.init(frame: frame) label.text = "Mini Player" label.numberOfLines = 0 label.textAlignment = .center addSubview(label) label.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ label.centerXAnchor.constraint(equalTo: centerXAnchor), label.centerYAnchor.constraint(equalTo: centerYAnchor) ]) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } // MARK: - View Controller class ViewController: UIViewController { let rootTabBarController = UITabBarController() let miniPlayer = MiniPlayer() override func viewDidLoad() { super.viewDidLoad() let items: [UITab] = [ UITab(title: "Tab 1", image: UIImage(systemName: "archivebox.fill"), identifier: "tab-1") { _ in UIViewController() }, UITab(title: "Tab 2", image: UIImage(systemName: "books.vertical.fill"), identifier: "tab-2") { _ in UIViewController() }, UISearchTab { _ in UIViewController() } ] rootTabBarController.tabs = items rootTabBarController.view.backgroundColor = .secondarySystemBackground view.addSubview(rootTabBarController.view) rootTabBarController.view.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ rootTabBarController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor), rootTabBarController.view.topAnchor.constraint(equalTo: view.topAnchor), rootTabBarController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor), rootTabBarController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor) ]) rootTabBarController.bottomAccessory = UITabAccessory(contentView: miniPlayer) } }
0
0
148
2w
iPadOS 26 Disable "open recent"
How can I remove the "recents" section from long-pressing on my app icon? I've added the following to my AppDelegate, which removes it from the top MenuBar, but not from the app icon context menu. My app has registered a custom filetype, but it is not a document based app. Opening files imports them into the app's user library, and so does not make sense to have a "recents" list. override func buildMenu(with builder: any UIMenuBuilder) { super.buildMenu(with: builder) builder.remove(menu: .openRecent) }
1
0
154
2w
UIScene.ConnectionOptions.shouldHandleActiveWorkoutRecovery Missing?
According to the WWDC25 Presentation Track workouts with HealthKit on iOS and iPadOS, there is supposed to be a new property for restoring an active workout after a crash on iOS/iPadOS. The developer documentation also supports this. However, this property does not seem to exist in the latest Xcode 26 beta, even in projects targeting iOS 26.0 as the minimum version. Am I missing something? Has this property not been made available yet? It is actually looking like all of the new iOS 26.0 properties are missing UIScene.ConnectionOptions on my system.
3
0
134
2w
How to reduce cell height (vertical margins) when using UIListContentConfiguration
The default cell height is 44pt in iOS 18 and 52pt in iOS 26. I'm trying to reduce the height back to 44pt in one screen that needs to fit as much content on screen as possible. How do you do that when using UIListContentConfiguration? I expected this would do the trick but alas it doesn't reduce the cell height. let cellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, Item> { cell, indexPath, item in cell.contentConfiguration = { var config = UIListContentConfiguration.valueCell() config.text = "Title" config.secondaryText = "Value" // This only removes horizontal margins, does not change vertical margins config.axesPreservingSuperviewLayoutMargins = [] config.directionalLayoutMargins = .zero return config }() }
Topic: UI Frameworks SubTopic: UIKit Tags:
4
0
107
2w
[iOS 26] UIVideoEditorController preview's top bar is overlapping with parent view controller
Applicaiton is built with WebKit and native camera view controller . Issue is seen always when presenting a UIVideoEditorController class on top of MainVC(webKit). Please refer the attached image Code: self.videoEditor.modalPresentationStyle = UIModalPresentationFullScreen; [viewController presentViewController:self.videoEditor animated: YES completion:nil];
3
0
92
4d
Recommended Approach for Handling Multiple UIButton Events: Single Handler vs Multiple Selectors?
I’m working with UIButton and finding different examples for event handling. Currently, I have a single action method like this, which receives the sender and the UIEvent: @objc func buttonHandler(_ sender: UIButton, forEvent event: UIEvent) { if let touches = event.allTouches, let touch = touches.first { switch touch.phase { case .began: print("TouchDown") case .ended: if sender.bounds.contains(touch.location(in: sender)) { print("TouchUpInside") } else { print("TouchUpOutside") } case .cancelled: print("TouchCancel") default: break } } if event.type == .presses { print("PrimaryActionTriggered") } } Is this considered best/recommended practice in UIKit, or should I use separate selector methods for each event type (e.g. .touchDown, .touchUpInside, .touchUpOutside) using addTarget(_:action:for:)? Are there any advantages or disadvantages to using a single handler with UIEvent versus multiple selectors for UIControlEvents? Thanks in advance!
Topic: UI Frameworks SubTopic: UIKit Tags:
0
0
47
2w
How to identify which UIControl.Event triggered a common selector for UIButton?
I’m working with UIButton and I’d like to register multiple UIControl.Events (e.g. .touchUpInside, .touchDown, .touchCancel, .primaryActionTriggered) using the same selector. For example: button.addTarget(self, action: #selector(handleButtonEvent(_:forEvent:)), for: [.touchUpInside, .touchDown, .touchCancel, .primaryActionTriggered]) @objc func handleButtonEvent(_ sender: UIButton, forEvent event: UIEvent) { // How do I tell which UIControl.Event triggered this? } From my understanding: If I use the single-parameter version (@objc func handleButtonEvent(_ sender: UIButton)), I can’t distinguish which event fired. If I use the two-parameter version with UIEvent, I can inspect touch.phase or event.type, but that feels indirect. Questions: Is there a recommended way to directly know which UIControl.Event caused the selector to fire? Is sharing a single selector across multiple control events considered a good practice, or is it more common to register separate selectors per event? Would appreciate guidance on what Apple recommends here.
Topic: UI Frameworks SubTopic: UIKit Tags:
1
0
39
2w
Recommended way to detect double-tap on UIButton and scope of UIControl.Event
I’m trying to detect a double-tap action on a UIButton. There seem to be two possible approaches: Using a UITapGestureRecognizer with numberOfTapsRequired = 2. Using the .touchDownRepeat event of UIControl.Event. What is the recommended approach for reliably handling double-taps on UIButton? Are there any practical differences in terms of behavior, performance, or best practices between these two methods? Additionally, I noticed that UIControl.Event defines a large set of events (like .editingChanged, .valueChanged, etc.). Can all these events be applied to any UIControl subclass such as UIButton, or are they only valid for specific controls like UITextField, UISlider, etc.? If not all events are supported by all controls, what is the rationale behind exposing them under a shared UIControl.Event enum? Thanks in advance!
Topic: UI Frameworks SubTopic: UIKit Tags:
3
0
59
2w
New window scenes on iPad always take the size of the activating window
I'm using multiple scenes in my iPad app. When I open a new scene from my main window, that new window is always the same size as the previous window. When I make the main window very small and then create a new scene, that new window is also tiny. When I make the main window very big, you guessed it. UIWindowScene.sizeRestrictions does not seem to help here. How can I give new windows a default size (it's okay if they're resizable after presenting)? This is such a weird behavior. Video of the problem in action: https://mastodon.social/@nicoreese/115033539035249909
Topic: UI Frameworks SubTopic: UIKit Tags:
1
0
115
3w