Problem remains in iOS 18.0 beta 2 (22A5297f). No change.
I’m unable to mask all appearances of the NLE by keeping the document non-nil. it still appears in transition when opening a new document by assigning the document property.
My understanding from discussion with the UIKit team is that this is a known issue that they are working on. I had hoped it would be fixed in the second beta or at least before the public beta.
Post
Replies
Boosts
Views
Activity
Here's what works for me:
[builder removeMenuForIdentifier:UIMenuReplace]; // includes iOS 18 `Writing Tools`
Implementing the writingToolsBehavior property, either via @synthesize or hand-coded methods to always return UIWritingToolsBehaviorNone from getter and ignore setter calls, has no effect.
FB13420491 is claimed to be fixed in macOS 15 WWDC developer beta 1 (24A5264n).
In my testing, things seem to work OK now on this macOS build. It would be good to hear what others are seeing for their use cases.
Please see the UITextInputTraits property, writingToolsBehavior. You can ... implement it on your custom UITextInput-conforming view
OK, I was able to try this and, unfortunately, it does not achieve the desired result: the Writing Tools menu item is still present in the custom view's Edit context menu (though it does nothing when selected).
I have a custom UIView subclass conforming to UITextInput, which uses UIEditMenuInteraction to provide an Edit context menu. The relevant code looks like this:
@implementation ConsoleView
{
UIEditMenuInteraction * _editMenuInteraction;
BOOL _editMenuIsVisible;
...
UIWritingToolsBehavior _writingToolsBehavior API_AVAILABLE(ios(18.0), macos(15.0));
}
...
@synthesize writingToolsBehavior = _writingToolsBehavior;
...
- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self) {
...
// disable iOS 18+ Writing Tools menu
if ( @available(iOS 18, macOS 15, *) ) {
_writingToolsBehavior = UIWritingToolsBehaviorNone;
}
...
I confirmed using the debugger that both the initialization code and the property accessor are executed, so that the property value is UIWritingToolsBehaviorNone. Yet the menu item remains.
Any other ideas?
I am able to use the -removeMenuForIdentifier: method, as mentioned above, to remove all other unwanted menu items from the edit context menu. It seems curious that the Writing Tools item should be handled differently.
One more thought on this:
It seems like it would be nice to have the ability to configure a UIDocumentBrowserViewController to behave like a UIDocumentPickerViewController, as far as having the option for it be presented modally, with a cancel button.
Perhaps I'm misunderstanding the use cases, but these classes seem very similar, with the exception of UIDocumentPickerViewController having the limitation of not being able to create new documents (which is not actually a desirable behavior in my use case, though I can live with it). Since UIDocumentBrowserViewController is newer, I have the impression that it's mostly meant to replace UIDocumentPickerViewController for many use cases.
Since we seem to be stuck with UIDocumentBrowserViewController in the new UIDocumentViewController (unless we implement it ourselves), it seems like having such capability would allow for greater adoption in apps that don't fit the mold of Pages and Swift Playground.
Thanks for looking into this!
So, just to check my understanding, it sounds like this is a bug in the developer beta, which should be fixed before GM. So I'll need the workaround shown above for now, but should be able to remove it post-beta.
The issue you are running into here is due to the fact that the browser view controller is only the bottom half of the new launch experience. So dismissing that view controller will not get you back into the document, it would only hide the bottom sheet that shows the browser.
That makes sense, though it doesn't mesh with what I actually observe on the screen when I do this—what I see is the full UI of my app is exposed momentarily (minus keyboard, but I believe it's just hidden), and then it is hidden again by the browser & new launch experience (NLE?). And when I look at the UI layers in Xcode's view debugger, it shows all of my UI layers (UINavigationBar and custom view displaying document contents) are actually in front of the UIDocumentViewController view that displays the browser & NLE "poster" (my custom UI is a full-screen subview of the UIDocumentViewController's view in the storyboard).
You should be able to hide the back button and replace it with a custom documents button that shows a document picker view controller. This is what the document view controller did in iOS 17.
Yes, I figured this was possible, but I had hoped to leverage the existing machinery from iOS 17 (which I assumed still existed in the framework for compatibility reasons) rather than having to add code to my app to re-implement this for iOS 18 (and risk getting it wrong).
Ultimately, I presume it might be better to find a way to adopt the new UI rather than trying to implement something like this, except as a stopgap. I would like to minimize custom code and rely on system behaviors where possible.
This is still possible with the two things I mentioned above. Make sure you have a document set (this will hide the launch experience)
Makes sense. At this point I think my normal app launch sequence is actually fine and does not show the NLE view. Outside of the "file open" flow, I believe the NLE only occurs momentarily during a transition where my app is opened via a URL from an app extension. I assume that I might need to provide a dummy document to cover the transition during this time when the document is presumably nil, currently.
Thanks for your insights on this!
Thanks, I’ll take a look.
UPDATE: If there were only a supported way to dismiss the new document browser without choosing a file, I think I could implement something close to the desired behavior on iOS 18.
I tried adding this to my -viewDidLoad override:
if ( @available(iOS 18, macOS 15, *) ) {
_documentBrowserViewController = self.launchOptions.browserViewController;
// see https://stackoverflow.com/a/76047554/21963209
self.launchOptions.browserViewController.additionalLeadingNavigationBarButtonItems = @[
[[UIBarButtonItem alloc] initWithTitle:@"Cancel" style:UIBarButtonItemStylePlain target:self action:@selector(didTapDocumentBrowserCancel:)]
];
}
with this action method:
- (IBAction)didTapDocumentBrowserCancel:(id)sender {
[_documentBrowserViewController dismissViewControllerAnimated:YES completion:nil];
}
and this almost does what I want, except that a moment after the browser view is dismissed, this message is emitted to the console and the browser is presented again:
A visible UIDocumentBrowserViewController was asked to dismiss unexpectedly. Avoid calling -[UIViewController dismissViewControllerAnimated:completion:] when this browser view controller is used within a UIDocumentViewControllerLaunchOptions context. Browser view controller: <UIDocumentBrowserViewController: 0x106b78f00>
Why must Apple stand in my way at every turn?
I don't think I'll have time to create a sample app to demonstrate the problem, but I have requested a couple UIKit consultations (still listed as Pending) to discuss this and other issues via WebEx.
I was recently able to work around the problem as follows:
Configured the UINavigationController in the Main Storyboard to use a custom UINavigationBar subclass, which contains the following methods (and nothing else):
- (UINavigationController *)myNavigationController {
UIResponder * next = super.nextResponder;
// perhaps needlessly complex; I expect next == UINavigationController at this point
while ( next && ![next isKindOfClass:UINavigationController.class] ) {
next = next.nextResponder;
}
return (UINavigationController *)next;
}
- (UIResponder *)nextResponder {
UIResponder * responder = [self myNavigationController].topViewController ?: [super nextResponder];
return responder;
}
This change got my UIDocumentViewController back into the responder chain, but I found that there was another problem:
I had an override of -canPerformAction:withSender: in my UIDocumentViewController subclass in order to work around the problem described in https://developer.apple.com/forums/thread/724027> (FB13258087):
// FIXME: working around FB13258087 <https://developer.apple.com/forums/thread/724027>
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
BOOL canDoIt = [super canPerformAction:action withSender:sender];
...
}
During debug I found that under iOS 18, canDoIt was evaluating to NO for actions implemented by my UIDocumentViewController subclass, such as duplicate: and print:. It seems that the superclass method was not providing the default behavior as described in the API documentation ("This default implementation of this method returns YES if the responder class implements the requested action and calls the next responder if it doesn’t.").
I changed the implementation of my method override to directly implement the default behavior:
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
BOOL canDoIt = [self respondsToSelector:action] ?: [self.nextResponder canPerformAction:action withSender:sender]; // *** calling super was returning NO in iOS 18 for implemented methods like duplicate: and print:
With these two changes, I'm seeing the expected behavior (same as in iOS 17.5) as far as navigation bar menu items are concerned (I have other iOS 18-related regressions to investigate, separate from this).
Here we are again:
FB13825638 (iOS 18 REGRESSION: Mail no longer accepts a self-signed certificate from my mail server (AGAIN))
Quinn's should be the accepted answer (sorry, this should've been a comment on that—I clicked the wrong button and don't see how to fix it).
@eskimo Quinn, Any chance you could push to get the x-apple.systempreferences URL scheme properly documented? It seems like something that user-friendly apps need to be able to do, and people are doing it anyway, so it would be great to have it documented explicitly as stable API somewhere outside of a header file (which only gives two specific examples for Full Disk Access, which is not the location desired here). I understand that things tend to move around in System Settings, but it seems like some needs to commit to supporting a scheme or other API that will allow apps to direct people to where they need to go.
Note: For my current use case, I think I can get by using the combining forms of long solidus (U+0338) and reverse solidus (U+20E5), but I'm still interested in the answer to the original question.
I'd love to know this as well. It seems that this should be simple, but it is surprisingly difficult. My understanding from the App Extension Programming Guide is that an app extension is not allowed to launch its containing app, or access the shared UIApplication instance, but every example that I've seen involves a hack to access UIApplication.sharedApplication (without actually calling that method), and then invoking openURL: on it to launch the containing app via an app-specific URL.
I just hit this same regression myself after updating to Xcode 15.1 (from Xcode 15) today.
It's pretty sad to see such an obvious regression in a minor release. Apple claims Objective-C is still fully supported (I asked about this at WWDC), so there's really no excuse for such an obvious testing miss.
FWIW, selecting Extension also does not work anymore, though I don't know if I ever tried that one before.
Thanks for filing feedback. I need to file a report myself when I get a chance.