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

UIKit Documentation

Post

Replies

Boosts

Views

Activity

UICollectionView scrolls back to 0 using comp layout
PLATFORM AND VERSION: iOS Development environment: Xcode 15.2 macOS 13.6.3 iOS 17.2 DESCRIPTION OF PROBLEM Using a UICollectionView with compositional layout, once the view is created, programmatically scroll to a row using the scrollToItem method. For this report I created a simple dataSource with 400 rows and 1 section. Scrolling to row 200 in viewDidLoad or viewWillAppear after calling collectionView.layoutIfNeeded(). The compositional layout's orthogonalScrollingBehavior is set to .groupPagingCentered The collectionView behaves as expected and shows row 200. Issue The issue is that when swiping too quickly to go to row 199 or prior the entire collectionView is reset to row 0. This does not happen when swiping to go on row 201 or above. STEPS TO REPRODUCE Run the app on iPhone. Swipe right quickly (to go to row prior to 200) Expected result: row 199 or 198 is shown Actual result: collectionView is reset to row 0
0
0
25
5h
uncaught exception 'NSInternalInconsistencyException', reason: 'Layout requested for visible navigation bar.
Hey,bros: I meet this crash,but I can't find any useful infos to fix this crash.And this crash only happens on iOS17. Here are the crash infos: *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Layout requested for visible navigation bar, <UINavigationBar: 0x106128820; frame = (0 0; 430 44); opaque = NO; autoresize = W; tintColor = UIExtendedGrayColorSpace. Here are the stacks: Thread 0 name: com.apple.main-thread CoreFoundation ___exceptionPreprocess (in CoreFoundation) libobjc.A.dylib _objc_exception_throw (in libobjc.A.dylib) Foundation -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] (in Foundation) UIKitCore -[UINavigationBar layoutSubviews] (in UIKitCore) UIKitCore -[UIView(CALayerDelegate) layoutSublayersOfLayer:] (in UIKitCore) longbridge-ios-app -[UIView(Thread) ex_layoutSublayersOfLayer:] (in longbridge-ios-app:UIView+Thread.m:36) UIKitCore -[UINavigationBar layoutSublayersOfLayer:] (in UIKitCore) QuartzCore CA::Layer::layout_if_needed(CA::Transaction*) (in QuartzCore) UIKitCore -[UIView(Hierarchy) layoutBelowIfNeeded] (in UIKitCore) UIKitCore -[UINavigationController _positionNavigationBarHidden:edge:initialOffset:] (in UIKitCore) UIKitCore -[UINavigationController _positionNavigationBarHidden:edge:] (in UIKitCore) UIKitCore -[UINavigationController _updateBarsForCurrentInterfaceOrientationAndForceBarLayout:] (in UIKitCore) UIKitCore -[UIViewController viewDidMoveToWindow:shouldAppearOrDisappear:] (in UIKitCore) UIKitCore -[UINavigationController viewDidMoveToWindow:shouldAppearOrDisappear:] (in UIKitCore) UIKitCore -[UIView(Internal) _didMoveFromWindow:toWindow:] (in UIKitCore) UIKitCore ___45-[UIView(Hierarchy) _postMovedFromSuperview:]_block_invoke (in UIKitCore) CoreAutoLayout -[NSISEngine withBehaviors:performModifications:] (in CoreAutoLayout) UIKitCore -[UIView _postMovedFromSuperview:] (in UIKitCore) UIKitCore -[UIView(Internal) _addSubview:positioned:relativeTo:] (in UIKitCore) UIKitCore -[UITransitionView transition:fromView:toView:removeFromView:] (in UIKitCore) UIKitCore -[UIViewControllerBuiltinTransitionViewAnimator animateTransition:] (in UIKitCore) UIKitCore ____UIViewControllerTransitioningRunCustomTransition_block_invoke_3 (in UIKitCore) UIKitCore +[UIKeyboardSceneDelegate _pinInputViewsForKeyboardSceneDelegate:onBehalfOfResponder:duringBlock:] (in UIKitCore) UIKitCore ____UIViewControllerTransitioningRunCustomTransition_block_invoke_2 (in UIKitCore) UIKitCore +[UIView(Animation) _setAlongsideAnimations:toRunByEndOfBlock:] (in UIKitCore) UIKitCore __UIViewControllerTransitioningRunCustomTransition (in UIKitCore) UIKitCore ___56-[UIPresentationController runTransitionForCurrentState]_block_invoke_3 (in UIKitCore) UIKitCore -[_UIAfterCACommitBlock run] (in UIKitCore) UIKitCore -[_UIAfterCACommitQueue flush] (in UIKitCore) UIKitCore __runAfterCACommitDeferredBlocks (in UIKitCore) UIKitCore __cleanUpAfterCAFlushAndRunDeferredBlocks (in UIKitCore) UIKitCore __UIApplicationFlushCATransaction (in UIKitCore) UIKitCore __UIUpdateSequenceRun (in UIKitCore) UIKitCore _schedulerStepScheduledMainSection (in UIKitCore) UIKitCore _runloopSourceCallback (in UIKitCore) CoreFoundation _CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION (in CoreFoundation) CoreFoundation ___CFRunLoopDoSource0 (in CoreFoundation) CoreFoundation ___CFRunLoopDoSources0 (in CoreFoundation) CoreFoundation ___CFRunLoopRun (in CoreFoundation) CoreFoundation _CFRunLoopRunSpecific (in CoreFoundation) GraphicsServices _GSEventRunModal (in GraphicsServices) UIKitCore -[UIApplication _run] (in UIKitCore) UIKitCore _UIApplicationMain (in UIKitCore) longbridge-ios-app main (in longbridge-ios-app:main.m:22) dyld start (in dyld)
0
0
21
1d
Diffable datasource and how to add new items / use snapshots properly
First question: I'd like to get clarification on what code I have to write to add a new item to a UICollectionView. So far, I have managed to seed my datasource with some sample data, which correctly appears in the collectionView. snapshot.appendSections(sections) // assume there is only 1 section. snapshot.appendItems(["addBoat"], toSection: .Boat)// placeholder for an add button snapshot.appendItems(backingStore.BoatIdentifiers(), toSection: .Boats)// add the boats in the backing store. dataSource.apply(snapshot, animatingDifferences: false) After the user enters the necessary data for a new boat, my code creates a new item instance (a struct). The sample project DiffableDataSourceSample README states: While a diffable data source can determine the changes between its current snapshot and a new one, it doesn't monitor the data collection for changes. Instead, it's the responsibility of the app to detect data changes and tell the diffable data source about those changes, by applying a new snapshot. So does that really mean that when I add one new item, I have to 1) add it to my backing store, and then 2) create a new snapshot with all the data in my backing store so that I can 3) apply that to the dataSource? Intuitively, if my backing store has a couple thousand items, step 2 feels it could be very costly. I was expecting that I could just append the new item to the existing snapshot (the one I created when seeding the data). But that means I have to keep the snapshot alive (and pass it around viewControllers). Something like this: snapshot.appendItems(newBoat.id, toSection: .Boats) Second, related question: I need to check that a new item doesn't already exist. Do I need to check in the existing snapshot? Like this: if let index = snapshot.indexOfItem(newBoat.id) { ... // boat is duplicate, don't allow adding it. } Or do I check only in my backing store, and then do steps 1 2 and 3 above? Thanks for any help.
1
0
28
2d
iOS18 WebView Crash
There is a 'WebCore::CachedResource::removeClient(WebCore::CachedResourceClient&)' crash in iOS 18 version. Could you please tell me if there are any changes to the WebView regarding this crash and how to debug it?
0
0
57
2d
UISplitViewController column-style, bug in setViewController?
&TLDR; I see no documentation stating that during the life of UISplitViewController, that I cannot call setViewController(xxxxx, column) more than once on the same column. Yet in my experience calling it a second time does not work, unless I set the value to nil before calling it again to set it to the new value... I'm using a UISplitViewController in newer column-style layout and when bootstrapping the app, I create the UISplitViewController and use setViewController(primaryViewController, .primary) and setViewController(detailViewController, .secondary). In order to use the primaryViewController for the compact scenario, I return the .primary column in the UISplitViewControllerDelegate method: splitViewController(_ svc: UISplitViewController, topColumnForCollapsingToProposedTopColumn proposedTopColumn: UISplitViewController.Column) -> UISplitViewController.Column Tapping a cell in the primaryViewController, results in a call to splitViewController.show(.secondary) This works for both split view (primary + secondary) as well as in compact mode. Tapping a cell in the secondary viewController results in pushing a new sub-detail onto the navigation sack. Again so far so good. If I switch back to split mode (primary+secondary) it still looks good with secondary showing the sub-detail. If I then collapse into compact view, the primary is once again shown. If I tap a different cell in the primary, the same call to: splitViewController.show(.secondary) results in the sub-detail viewController being shown. Instead, I want the secondary to show the detail for the new selected cell...not the sub-detail of the prior cell. To fix this, I popped the navigation stack for the secondary to rootViewController if secondaryNavigationController.topViewController as? DetailViewController == nil { secondaryNavigationController.popToRootViewController(animated: false) next, I attempted to replace splitViewController's secondary viewController by assigning the secondaryNavigationController which now has the DetailViewController as the top (and only) viewController. splitViewController.setViewController(secondaryNavigationController, for: .secondary) after this assignment, calling splitViewController.viewController(for: .secondary) returns the old sub-detail viewController, not the expected DetailViewController! IMO this is a bug. I found the following solution. First set it to nil, then set it to the desired value. splitViewController.setViewController(nil, for: .secondary) splitViewController.setViewController(secondaryNavigationController, for: .secondary)
0
0
41
2d
Why is KeyboardLayoutGuide not applied in viewIsAppearing() in iOS 15.0?
viewIsAppearing is available in iOS 13 and above, and I understand that setting the layout in this lifecycle is a good way to do it. But when I use it, confirmed that UIKeyboardLayout does not work properly in viewIsAppearing in iOS versions lower than 16.0. There is a small problem not only with viewIsAppearing, but also with viewDidLoad(). In viewDidLoad(), the component to which UIKeyboardLayoutGuide is applied is visible when the screen first appears. However, there is a problem where the layout with UIKeyboardLayoutGuide applied is not visible when returning after changing the navigation screen (Push & Pop). I confirmed that UIKeyboardLayoutGuide is applied properly in viewIsAppearing and viewDidLoad in iOS 16.0 and higher versions. Below is the code and environment for reproducing the problem situation. ==================================================== Envirionment: M1 Pro(Sonoma v14.5), Xcode(v15.4), iPhone Simulator (iOS v15.0, v15.2) import UIKit import SnapKit final class ViewController: UIViewController { private let uiTextField: UITextField = { $0.placeholder = "search" $0.backgroundColor = .red return $0 }(UITextField()) override func viewIsAppearing(_ animated: Bool) { super.viewIsAppearing(animated) view.backgroundColor = .white layout() } private func layout() { view.addSubview(uiTextField) uiTextField.snp.makeConstraints { $0.height.equalTo(50) $0.horizontalEdges.equalToSuperview() $0.bottom.equalTo(view.keyboardLayoutGuide.snp.top) } } override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { view.endEditing(true) } } I wonder if I haven't checked what I need to check yet, or if there are other problems.
0
0
66
3d
drawHierarchy speed
In iOS, I'm trying to do multiple (upwards of 10) screen snapshots (UIImages) per second to be combined into a video and am running into some challenges. UIView's drawHierarchy seems to be to slow when run on the main thread, causing jitter with the UI experience. I've noticed that I can run it on a background thread, but only when passing a value of FALSE for the afterScreenUpdates parameter. This does cause the main thread warning to be written to the console (when the main thread checker is turned off), but does work. And I don't have to worry about blocking the main thread as it is on a background thread. The question is, will running drawHierarchy on the background thread potentially cause memory corruption when traversing the entire tree of a UIWindow. And if so, what is the advised most performant method of capturing iOS screenshots? drawHierarchy Doc: https://developer.apple.com/documentation/uikit/uiview/1622589-drawhierarchy TIA
4
0
90
3d
UICollectionViewDiffableDataSource deadlock scenario on iOS 18
We encountered a deadlock scenario when our app runs on iOS 18. It happens when we use the async version of UICollectionViewDiffableDataSource.apply() and request a section snapshot from within the UICollectionViewCompositionalLayoutSectionProvider. The deadlock doesn't happen when using the completion handler version of UICollectionViewDiffableDataSource.apply() or when requesting a full snapshot from the data source. On iOS 17 there is no issue. import UIKit class ViewController: UICollectionViewController { private enum SectionIdentifier: Hashable { case test } private enum ItemIdentifier: Hashable { case test(UUID) } private typealias Snapshot = NSDiffableDataSourceSnapshot<SectionIdentifier, ItemIdentifier> private typealias SectionSnapshot = NSDiffableDataSourceSectionSnapshot<ItemIdentifier> private typealias DataSource = UICollectionViewDiffableDataSource<SectionIdentifier, ItemIdentifier> private lazy var dataSource: DataSource = { let cellRegistration = UICollectionView.CellRegistration<UICollectionViewCell, UIColor> { cell, _, color in cell.contentView.backgroundColor = color } return DataSource(collectionView: collectionView) { [weak self] collectionView, indexPath, itemIdentifier in return collectionView.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item: .red) } }() override func viewDidLoad() { super.viewDidLoad() collectionView.dataSource = dataSource collectionView.collectionViewLayout = UICollectionViewCompositionalLayout { [weak self] section, environment -> NSCollectionLayoutSection? in // calling `UICollectionViewDiffableDataSource.snapshot(for:)` from the `UICollectionViewCompositionalLayoutSectionProvider` leads to deadlock on iOS 18 let numberOfItems = self?.dataSource.snapshot(for: .test).items.count ?? 0 // deadlock // calling `UICollectionViewDiffableDataSource.snapshot()` causes no issue // let numberOfItems = self?.dataSource.snapshot().numberOfItems(inSection: .test) ?? 0 // works let item = NSCollectionLayoutItem(layoutSize: .init(widthDimension: .absolute(100), heightDimension: .absolute(100))) let group = NSCollectionLayoutGroup.horizontal(layoutSize: .init(widthDimension: .fractionalWidth(1), heightDimension: .absolute(100)), repeatingSubitem: item, count: numberOfItems) group.interItemSpacing = .fixed(5) return .init(group: group) } var snapshot = Snapshot() snapshot.appendSections([.test]) snapshot.appendItems([.test(UUID()), .test(UUID()), .test(UUID()), .test(UUID())]) // using the async wrapper `UICollectionViewDiffableDataSource.apply()` on any thread leads to deadlock on iOS 18 Task { await dataSource.apply(snapshot) } // deadlock // Task { @MainActor in await dataSource.apply(snapshot) } // deadlock // using `UICollectionViewDiffableDataSource.apply()` with completion handling causes no issue // Task { dataSource.apply(snapshot) } // works // dataSource.apply(snapshot) // works } } Full example project at https://github.com/antiraum/iOS18_UICollectionViewDiffableDataSource_deadlock
0
0
80
4d
Specify queue for collapse/expand of section snapshots
Is there any way to use outline disclosure sections in a UICollectionViewDiffableDataSource without having to make all apply(_ snapshot:) calls from the main thread? Whenever I collapse or expand a section I get a warning about mixing main queue/off-main queue updates: Warning: applying updates in a non-thread confined manner is dangerous and can lead to deadlocks. Please always submit updates either always on the main queue or always off the main queue I understand what this means, but I am applying my section snapshots on a background queue to avoid interrupting the UI, it seems like the expand/collapse updates use the main queue and I cannot find any way to make it use the same queue as my other updates. Does this mean I must apply my own updates on the main queue now as well?
2
0
93
4d
Problem with new UITabbarController on IOS 18 beta (Bug?)
In my project, I create a UITabbarController using the storyboard. Later, I change the images and title of the individual tabbar items in the code. This works with the new tabbar controller from iOS 18, with one exception: If the iPad switches to a horizontally compact mode and thereby triggers the old tabbar (at the bottom of the screen), the images and titles from the storyboard appear there, not the values ​​changed in the code. Am I doing something wrong or is this a bug?
4
0
171
5d
UITextView with large `textContainerInset` scrolls upwards when selecting text
Whenever a UITextView has a large textContainerInset it starts scrolling upwards rapidly when selecting text. For my use case, I have a custom view that I display in the text view to show the comment you are writing a reply to. To accomplish this, I use the textContainerInset and set the top portion of that inset to a large value. Here is a video showing the issue: https://streamable.com/9z57ok I've also made a very simple sample project to demonstrate and here is the main view controller code: https://gist.github.com/rickharrison/9cf563bcb22130bfafc7d3b5d37c55f2 Is it possible to disable scrolling of the UITextView while the selection UI is shown? Or is there another way I should inset the text to edit? I found another thread with this issue from years ago, but no solution - https://developer.apple.com/forums/thread/129882
2
0
80
5d
UICollectionView has weird separator insets in landscape mode
Here is a screenshot of the app: And here follows the code: it's a view controller with a collection view with plain list layout and a diffable data source. It has 1 section with 2 rows. The 1st row's content configuration is UIListContentConfiguration.cell(), and it has some text. The 2nd row has an empty content configuration instead. As you can see, if you run the app, the separator insets are different for the two rows. Did I make a mistake? If this is expected behavior instead, is there an easy fix? import UIKit class ViewController: UIViewController { var collectionView: UICollectionView! var dataSource: UICollectionViewDiffableDataSource<String, String>! var snapshot: NSDiffableDataSourceSnapshot<String, String> { var snapshot = NSDiffableDataSourceSnapshot<String, String>() snapshot.appendSections(["main"]) snapshot.appendItems(["one", "two"]) return snapshot } override func viewDidLoad() { super.viewDidLoad() configureHierarchy() configureDataSource() } func configureHierarchy() { collectionView = .init( frame: view.bounds, collectionViewLayout: createLayout() ) view.addSubview(collectionView) collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight] } func createLayout() -> UICollectionViewLayout { return UICollectionViewCompositionalLayout { section, layoutEnvironment in let config = UICollectionLayoutListConfiguration(appearance: .plain) return NSCollectionLayoutSection.list(using: config, layoutEnvironment: layoutEnvironment) } } func configureDataSource() { let firstCellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, String> { cell, indexPath, itemIdentifier in var contentConfiguration = UIListContentConfiguration.cell() contentConfiguration.text = "Hello" cell.contentConfiguration = contentConfiguration } let secondCellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, String> { cell, indexPath, itemIdentifier in } let emptyCellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, String> { cell, indexPath, itemIdentifier in } dataSource = .init(collectionView: collectionView) { collectionView, indexPath, itemIdentifier in switch itemIdentifier { case "one": collectionView.dequeueConfiguredReusableCell(using: firstCellRegistration, for: indexPath, item: itemIdentifier) case "two": collectionView.dequeueConfiguredReusableCell(using: secondCellRegistration, for: indexPath, item: itemIdentifier) default: collectionView.dequeueConfiguredReusableCell(using: emptyCellRegistration, for: indexPath, item: itemIdentifier) } } dataSource.apply(self.snapshot, animatingDifferences: false) } } Xcode 15.4, iPhone 15 Pro simulator, iOS 17.5, MacBook Air M1 8GB, macOS 14.5.
1
0
85
6d
PKCanvasView won't react to updates of its contentScaleFactor
Dear All, I am currently facing a challenge with updating the contentScaleFactor of my PKCanvasView, which is embedded within a custom UIScrollView. I have configured the PKCanvasView to have both its maximum and minimum zoomScale set to 1.0, ensuring that users can only zoom into the UIScrollView and not directly into the PKCanvasView. Upon the completion of a zoom action in my UIScrollView, a notification is published and received by the PKCanvasView. This triggers a function intended to update the contentScaleFactor. However, this process is not functioning as expected. Despite consulting with several engineers at WWDC24 and exploring multiple potential solutions, the issue remains unresolved. Interestingly, when zooming into a PDF in the Documents app on iOS 17, the strokes are re-rendered perfectly, while in previous iOS versions, this bug persists in the Documents app. I would greatly appreciate any insights or solutions from those who might have encountered and resolved this issue. Thank you in advance for your assistance. Best regards, Timon
0
1
88
6d
When using UIView.drawHierarchy, older CPU performs far better than newer CPU
Hi all, My app uses SpriteKit views which are rendered into images for various uses. For some reason, the same code performs worse on a newer CPU than on an older one. My A13 Bionic flies through the task at high resolution and 60FPS while CPU usage is <60%, while the A15 Bionic chokes and sputters at a lower resolution and 30FPS. Because of how counterintuitive this is, it took me a while to isolate the call directly responsible--with UIView.drawHierarchy commented out, both devices returned to their baseline performances. guard let sceneView = skScene.view else { return } let size = CGSize(width: outputResolution, height: outputResolution) return UIGraphicsImageRenderer(size: size).image { context in let rect = CGRect(origin: .zero, size: size) sceneView.drawHierarchy(in: rect, afterScreenUpdates: false) } Does anyone know why this is the case, and how to fix it? I tried using UIView.snapshotView, which is supposedly much faster, but it only returns blank images. Am I using it wrong or does it simply not work in this context? sceneView.snapshotView(afterScreenUpdates: false)?.draw(rect) Any hints or pointers would be greatly appreciated
0
0
75
1w
Voice over reads elements for covered UIWindow
I have few UIWindow in my application, one has alert level and another one has normal. When I present one window above another the voice over reads elements that are covered by the alert window. How I can suppress this behaviour and make the voice over read elements on a visible key window only. I don't like an option to hide accessibility for the bottom window, so I am wondering are there any other ways to do it?
1
0
57
1w