I'm facing an accessibility issue, where when I call UIViewController.addChild(_:) and pass in another instance of a UIViewController, the VoiceOver focus is jumping to the "Back" button in the navigation bar. How might one go about avoid this behaviour and having the accessibility/voiceover focus remain where it was at the time of adding the child?
UIKit
RSS for tagConstruct and manage graphical, event-driven user interfaces for iOS or tvOS apps using UIKit.
Post
Replies
Boosts
Views
Activity
I am building an app about photos and
I want to create a photo sharing feature like Apple's Photos App.
Please see Steps to Reproduce and attached project.
The current share method has the following issues
The file name of the shared photo changes to “FullSizeRender”.
The creation and update dates of shared photos will change to the date they were edited or shared.
I want to ensure that the following conditions are definitely met
Share the latest edited version.
The creation date should be when the original photo was first created.
How can I improve the code?
STEPS TO REPRODUCE
class PHAssetShareManager {
static func shareAssets(_ assets: [PHAsset], from viewController: UIViewController, sourceView: UIView) {
let manager = PHAssetResourceManager.default()
var filesToShare: [URL] = []
let group = DispatchGroup()
for asset in assets {
group.enter()
getAssetFile(asset, resourceManager: manager) { fileURL in
if let fileURL = fileURL {
filesToShare.append(fileURL)
}
group.leave()
}
}
group.notify(queue: .main) {
self.presentShareSheet(filesToShare, from: viewController, sourceView: sourceView)
}
}
private static func getAssetFile(_ asset: PHAsset, resourceManager: PHAssetResourceManager, completion: @escaping (URL?) -> Void) {
print("getAssetFile")
let resources: [PHAssetResource]
switch asset.mediaType {
case .image:
if asset.mediaSubtypes.contains(.photoLive) {
// let editedResources = PHAssetResource.assetResources(for: asset).filter { $0.type == .fullSizePairedVideo }
// let originalResources = PHAssetResource.assetResources(for: asset).filter { $0.type == .pairedVideo }
let editedResources = PHAssetResource.assetResources(for: asset).filter { $0.type == .fullSizePhoto }
let originalResources = PHAssetResource.assetResources(for: asset).filter { $0.type == .photo }
resources = editedResources.isEmpty ? originalResources : editedResources
} else {
let editedResources = PHAssetResource.assetResources(for: asset).filter { $0.type == .fullSizePhoto }
let originalResources = PHAssetResource.assetResources(for: asset).filter { $0.type == .photo }
resources = editedResources.isEmpty ? originalResources : editedResources
}
case .video:
let editedResources = PHAssetResource.assetResources(for: asset).filter { $0.type == .fullSizeVideo }
let originalResources = PHAssetResource.assetResources(for: asset).filter { $0.type == .video }
resources = editedResources.isEmpty ? originalResources : editedResources
default:
print("Unsupported media type")
completion(nil)
return
}
guard let resource = resources.first else {
print("No resource found")
completion(nil)
return
}
let fileName = resource.originalFilename
let tempDirectoryURL = FileManager.default.temporaryDirectory
let localURL = tempDirectoryURL.appendingPathComponent(fileName)
// Delete existing files and reset cache
if FileManager.default.fileExists(atPath: localURL.path) {
do {
try FileManager.default.removeItem(at: localURL)
} catch {
print("Error removing existing file: \(error)")
}
}
let options = PHAssetResourceRequestOptions()
options.isNetworkAccessAllowed = true
resourceManager.writeData(for: resource, toFile: localURL, options: options) { (error) in
if let error = error {
print("Error writing asset data: \(error)")
completion(nil)
} else {
completion(localURL)
}
}
}
private static func presentShareSheet(_ items: [Any], from viewController: UIViewController, sourceView: UIView) {
print("presentShareSheet")
let activityViewController = UIActivityViewController(activityItems: items, applicationActivities: nil)
if UIDevice.current.userInterfaceIdiom == .pad {
activityViewController.popoverPresentationController?.sourceView = sourceView
activityViewController.popoverPresentationController?.sourceRect = sourceView.bounds
}
viewController.present(activityViewController, animated: true, completion: nil)
}
}```
Hi,
We are going to create a tvOS App with portrait display(HDMI screen will rotate 90 degree).
It seems there is no rotate setting in tvOS18, neither Xcode provide relative support.
As our investigation, we might need to rotate each UIKit component 90 degree by code to archive it.
Is there any better suggestion?
Thanks.
I am trying to change UITabBar background color runtime as theme changed. It is already working in iOS 17 as I am updating UITabBar.appearance().barTintColor and tintColor
But for iOS first i need to change because I don't want that new elevated tabbar so I create custom tabbar controller as described in
https://stackoverflow.com/questions/78631030/how-to-disable-the-new-uitabbarcontroller-view-style-in-ipados-18
Accepted Answer by awulf.
And by doing this, My tabbar looks same like Old and it is working in iPhone and ipad for iOS 16, iOS 17 and iOS 18 too.
But the issue is that I am unable to change my tabbar background color.
I have also checked this forum: https://forums.developer.apple.com/forums/thread/761056
But not able to change
I have set below 3 properties but no effect
let appearance = UITabBar.appearance()
appearance.backgroundColor =
appearance.barTintColor =
appearance.tintColor =
I have created CustomTabBarController in storyboard and all working fine
Also the appearance changed only once per application lifecycle.
It will change color by restarting the app then it will pick last selected theme and the colors are changed.
but not able to change colors runtime
I have also did below code for reloading purpose
tabBar.setNeedsLayout()
tabBar.setNeedsDisplay()
But nothing work
I notice that the UIControlEventEditingChanged action will be called twice under Chinese or Japanese when click confirm button of the UITextfield's keyboard. While for other languages such as English it only be called once. Could anyone can explain the detail for it?
In iOS18, WKWebView's default cookie SameSite value is Lax. Prior to iOS18, the default value is None.
Is this intentional, or a bug? This change is not documented anywhere.
I made a sample XCode project (ViewController code below) to show this change. It loads www.apple.com into a WKWebView and prints cookies. That site has several cookies, but it only explicitly sets SameSite to None for one cookie, s_vi. Every other cookie relies on default WKWebView behavior. When looking at cookies, either in the console or in Safari's Web Inspector, the SameSite value differs. If older than iOS18, every cookie has SameSite of None. If iOS18, all cookies except s_vi have SameSIte of Lax.
I also tried manually setting the following cookies:
testCookie-none with SameSite set to None
testCookie-lax with SameSite set to Lax
testCookie-strict with SameSite set to Strict
testCookie- with SameSite set to an empty string
When looking at these cookies, testCookie-none and testCookie- have their SameSite of None if older than iOS18, but are both Lax in iOS18. So, it seems we cannot manually set the SameSIte to None either.
I realize updating the server to return the SameSite value would resolve this. However, in my app where I'm struggling with this issue, that server is Salesforce. Only they can update their response headers. Since this change isn't documented by Apple, I am assuming it is a bug and not intentional. Are there any workarounds? Any input by Apple on a fix?
Below is the ViewController code, and images of the cookies in Safari's Web Inspector.
import UIKit
import WebKit
class ViewController: UIViewController, WKNavigationDelegate {
var webView: WKWebView!
override func loadView() {
// Create WKWebView
let config = WKWebViewConfiguration()
webView = WKWebView(frame: .zero, configuration: config)
// Allow inspection in Safari debugger
webView.isInspectable = true
// Track the request to load our website
webView.navigationDelegate = self
// Manually add four cookies:
// testCookie-none with SameSite set to None
// testCookie-lax with SameSite set to Lax
// testCookie-strict with SameSite set to Strict
// testCookie- with SameSite set to an empty string
addTestCookies()
view = webView
}
override func viewDidLoad() {
super.viewDidLoad()
// Load a website
let urlString = "https://www.apple.com"
self.webView.load(URLRequest(url: URL(string:urlString)!))
}
// Once the website loads, print the cookies.
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
WKWebsiteDataStore.default().httpCookieStore.getAllCookies { cookies in
for cookie in cookies {
print(cookie)
}
}
}
/*
Manually add the following cookies for domain .apple.com
testCookie-none with SameSite set to None
testCookie-lax with SameSite set to Lax
testCookie-strict with SameSite set to Strict
testCookie- with SameSite set to an empty string
In older iOS versions, both testCookie-none and testCookie- will have their SameSite as none.
In iOS18, no cookie will have SameSite as None.
*/
func addTestCookies()
{
let httpCookieStore = WKWebsiteDataStore.default().httpCookieStore
for sameSitePolicy in ["none", "lax", "strict", ""] {
httpCookieStore.setCookie(HTTPCookie(properties: [
HTTPCookiePropertyKey.path: "/",
HTTPCookiePropertyKey.name: "testCookie-"+sameSitePolicy,
HTTPCookiePropertyKey.value: "1",
HTTPCookiePropertyKey.domain: ".apple.com",
HTTPCookiePropertyKey.secure: true,
HTTPCookiePropertyKey.sameSitePolicy: sameSitePolicy
])!)
}
}
}
Previously this code would trigger fine on pressesBegan in iOS 17 and earlier versions, but no longer works in iOS 18. How can I start capturing pressesBegan in iOS 18? It seems like UIResponder is just not capturing the keyboard anymore?
struct ContentView: View {
var body: some View {
KeyBoardView()
}
}
//To Use in SwiftUI
struct KeyBoardView: UIViewRepresentable{
func makeUIView(context: Context) -> KeyEventView {
KeyEventView()
}
func updateUIView(_ uiView: KeyEventView, context: Context) {
}
class KeyEventView: UIView {
init() {
super.init(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func pressesBegan(_ presses: Set<UIPress>, with event: UIPressesEvent?)
{
print("test")
}
}
}
We're trying to implement a backup/restore data feature in our business productivity iPad app using UIDocumentPickerViewController and AppleArchive, but discovered odd behavior of [UIDocumentPickerViewController initForOpeningContentTypes: asCopy:YES] when reading large archive files from a USB drive.
We've duplicated this behavior with iPadOS 16.6.1 and 17.7 when building our app with Xcode 15.4 targeting minimum deployment of iPadOS 16. We haven't tested this with bleeding edge iPadOS 18.
Here's our Objective-C code which presents the picker:
NSArray* contentTypeArray = @[UTTypeAppleArchive];
UIDocumentPickerViewController* docPickerVC = [[UIDocumentPickerViewController alloc] initForOpeningContentTypes:contentTypeArray asCopy:YES];
docPickerVC.delegate = self;
docPickerVC.allowsMultipleSelection = NO;
docPickerVC.shouldShowFileExtensions = YES;
docPickerVC.modalPresentationStyle = UIModalPresentationPopover;
docPickerVC.popoverPresentationController.sourceView = self.view;
[self presentViewController:docPickerVC animated:YES completion:nil];
The UIDocumentPickerViewController remains visible until the selected external archive file has been copied from the USB drive to the app's local tmp sandbox. This may take several seconds due to the slow access speed of the USB drive. During this time the UIDocumentPickerViewController does NOT disable its tableview rows displaying files found on the USB drive. Even the most patient user will tap the desired filename a second (or third or fourth) time since the user's initial tap appears to have been ignored by UIDocumentPickerViewController, which lacks sufficient UI feedback showing it's busy copying the selected file.
When the user taps the file a second time, UIDocumentPickerViewController apparently begins to copy the archive file once again. The end result is a truncated copy of the selected file based on the time between taps. For instance, a 788 MB source archive may be copied as a 56 MB file. Here, the UIDocumentPickerDelegate receives a 56 MB file instead of the original 788 MB of data.
Not surprisingly, AppleArchive fails to decrypt the local copy of the archive because it's missing data. Instead of failing gracefully, AppleArchive crashes in AAArchiveStreamClose() (see forums post 765102 for details).
Does anyone know if there's a workaround for this strange behavior of UIDocumentPickerViewController?
Hi all,
Firebase statistics show that some crashes seem to occur suddenly. Can you give me some suggestions?
Users also meet the following requirements:
iOS18 and above
RTL language
From the stack frame, the crash occurred in the transition animation project, but I did not reproduce this stack frame
A very small number of users can experience it twice
The crashed page is relatively complex, and it is a mixture of auto-layout and frame
I retrieved some other articles, but they don’t seem to be the same problem
https://developer.apple.com/forums/thread/693118
https://stackoverflow.com/questions/56027014/collectionview-crashing-in-nsisengine-after-a-few-scrolls
Thank you for reading, looking forward to your reply ;)
CoreAutoLayout: _engineVar_rawRemove
I am using Storyboard to present a UIImagePickerController on a view controller (a simple initial view without any navigation or tab bar). Due to a requirement, we need to exclude the arm64 architecture. However, when I try to present the screen, it gets stuck. I am unable to select an image, and the screen becomes inaccessible. It's happening only on simulator.
Does anyone have any solution.
Can anyone help me to resolve this issue?
I have two collectionviews in a tableview and each time you try to scroll the collecctionview after clicking a cell, it crashes with the following error:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Expected dequeued view to be returned to the collection view in preparation for display. When the collection view's data source is asked to provide a view for a given index path, ensure that a single view is dequeued and returned to the collection view. Avoid dequeuing views without a request from the collection view. For retrieving an existing view in the collection view, use -[UICollectionView cellForItemAtIndexPath:] or -[UICollectionView supplementaryViewForElementKind:atIndexPath:]
We're trying to implement a backup/restore data feature in our business productivity iPad app using UIDocumentPickerViewController and AppleArchive, but discovered AppleArchive crashes instead of failing gracefully when decrypting a corrupt archive.
As described in forum post 765101, UIDocumentPickerViewController can handoff a corrupt copy of an archive to UIDocumentPickerDelegate under specific circumstances.
We've duplicated this behavior with iPadOS 16.6.1 and 17.7 when building our app with Xcode 15.4 targeting minimum deployment of iPadOS 16. We haven't tested this with the bleeding edge iPadOS 18.
Our app is primarily Objective-C, but it utilizes the Swift-based AppleArchive 'EncryptingAndDecryptingDirectories' sample code associated with WWDC21 session:
10233: Bring Encrypted Archives and Performance Improvements to Your App with Accelerate.
The WWDC21 'EncryptingAndDecryptingDirectories' Swift sample project crashes in a similar manner when a corrupt archive file created by UIDocumentPickerViewController is dropped into the sample app's window for decryption (see attached crash log).
Does anyone know if there's a workaround for the 'EncryptingAndDecryptingDirectories' sample project to prevent AppleArchive from crashing when decrypting a corrupt archive?
crash log.txt
I just finished updating my app to support the new screen sizes for the iPhone 16 series devices (iPhone16 Pro and iPhone 16 Pro Max). In testing the changes, I've come across what I hope is simply a bug in the Xcode 16 simulators. I don't have any actual devices (yet) that I can use to test.
In landscape mode, my app presents a "tab" on the right side of the screen that can be tapped to bring out another view. Activating the tab is based on capturing the screen coordinates from a "tap".
In Xcode 16, using the iPhone16 simulators on IOS 18, the area of the tab does not recognize that a tap has occurred. After hours of banging my head against my laptop, it dawned on me that the area of the screen that hosts the "tab" is in exactly the same location as "unavailable area" that represents the camera on the simulator. I have arrived at the assumption that the issue is that the simulator is treating the same space at the bottom of the device as it treats that reserved area at the top of the screen.
Has anyone else experienced this?
Is this a bug in the simulator or is this expected behavior on the iPhone 16 series devices?
iPadOS18 Tab Bar has been moved to top of screen. Everything looks ok when translucent=true. But there is an unwanted white space at bottom of screen when setting translucent=false.
Is it iPadOS bug?
See white area at bottom of attached screenshot.
When I write code in shareExtension like below:
dispatch_async(dispatch_get_global_queue(0, 0), ^{
UIResponder *responder = [self nextResponder];
while ((responder = [responder nextResponder]) != nil) {
if ([responder respondsToSelector:@selector(openURL:)] == YES) {
[responder performSelector:@selector(openURL:) withObject:[NSURL URLWithString:APP_SCHEME]];
break;
}
}
});
Will output in the console:
BUG IN CLIENT OF UIKIT: The caller of UIApplication.openURL(:) needs to migrate to the non-deprecated UIApplication.open(:options:completionHandler:). Force returning false (NO).
As you can see, when I swipe to see the context actions in the collectionview, the searchbar flicks. Same story when, in a popover, i change the PreferredContentSize.
Hello Dear Developers,
Is it only to me or in iOS 18 there's a problem with Settings.bundle, Im using my settings.bundle to set my enivement for my app but after upgrading to iOS 18 I can't see my Settings.Bundle in my settings iphone,
just to mention that I also updated my xcode to 16.
Anyone who has occur with this problem? :)
I have issue with unwanted changing offset in collection view to top or to near top.
It is happening in collection view with vertical scroll when estimatedItemSize is not set to zero (main factor). If estimatedItemSize is zero it is always fine.
It is SDK that provides items that should be loaded in few cells, items have dynamic height which is received from server and can be updated several times.
Scenario when it happens (when was noticed) is 2 sections, in section 0 item is at index 4 of 14, section 1 is with only one cell with dynamic height item.
If specific item is at index 0 in section 0 or have 1 cell per section (tried 15 sections and set items in sections 5 and 15) all is good regardless of estimatedItemSize.
When new height is received if I call invalidateLayout or reloadItemAtIndexPaths it will “jump”. That same height is set in sizeForItemAtIndexPath.
In some combinations it happens and in some not and that is the most annoying part.
I tried setting estimated height to received height and it didn’t help (other cell may be smaller or larger). Also if I put items in one section at indexes 4 and 14 it “jumps”.
I managed to make it work by setting at specific moment estimatedItemSize to zero then put back to one that SDK user set and didn’t see any issues but I was wondering if there is any other solution for this and did anyone had issue like this. It would be nice to have solution to keep one estimatedItemSize if it is not the default one (zero).
Hi there,
I'm trying to migrate my app to using the UIDocumentViewController so I can use the new launch experience. My app exports a custom file type and uses UIDocument + UIDocumentBrowserViewController.
I tried creating a UIDocumentViewController and making it the root view of my app but it doesn't recognise my custom document type.
class Document_VC: UIDocumentViewController {
var storyCardsDocument: PlotCardDocument? {
self.document as? PlotCardDocument
}
override func viewDidLoad() {
super.viewDidLoad()
configureViewForCurrentDocument()
}
override func documentDidOpen() {
configureViewForCurrentDocument()
}
func configureViewForCurrentDocument() {
guard let document = storyCardsDocument,
!document.documentState.contains(.closed)
&& isViewLoaded else { return }
print("Document opened: \(document.fileURL)")
}
}
The app opens but when I navigate to a document made in a previous version of the app it is greyed out. I also don't see a 'New' button in the launcher view.
To try and see what I was doing wrong, I tested the markdown app sample code. When I make the UIDocumentViewController the root and try to open or create a new markdown document I get the following error:
no document class found. Define the correct UIDocument subclass with the key UIDocumentClass in the info.plist's CFBundleDocumentTypes dictionary.
is now broken. (but definitely worked when I originally wrote my Document-based app)
It's been a few years.
DocumentBrowserViewController's delegate implements the following func.
func documentBrowser(_ controller: UIDocumentBrowserViewController, didRequestDocumentCreationWithHandler importHandler: @escaping (URL?, UIDocumentBrowserViewController.ImportMode) -> Void) {
let newDocumentURL: URL? = Bundle.main.url(forResource: "blankFile", withExtension: "trtl2")
// Make sure the importHandler is always called, even if the user cancels the creation request.
if newDocumentURL != nil {
importHandler(newDocumentURL, .copy)
} else {
importHandler(nil, .none)
}
}
When I tap the + in the DocumentBrowserView, the above delegate func is called (my breakpoint gets hit and I can step through the code) newDocumentURL is getting defined successfully and
importHandler(newDocumentURL, .copy)
gets called, but returns the following error:
Optional(Error Domain=com.apple.DocumentManager Code=2 "No location available to save “blankFile.trtl2”." UserInfo={NSLocalizedDescription=No location available to save “blankFile.trtl2”., NSLocalizedRecoverySuggestion=Enable at least one location to be able to save documents.})
This feels like something new I need to set up in the plist, but so far haven't been able to discover what it is.
perhaps I need to update something in info.plist? perhaps one of:
CFBundleDocumentTypes
UTExportedTypeDeclarations
Any guidance appreciated.
thanks :-)