Reprint is prohibited without authorization

Original text: juejin. Cn/post / 684490…

WWDC 19 has been over a month, and this article should have been written earlier, but some of the code beta1-beta4 API changes once a beta, and some of the previous beta initialization methods are private methods that start with __. So I waited until the Beta4 API was basically stable before writing this article.

PDF (figure)

If you’ve upgraded to iOS 13 you’ll notice that when you take a screenshot in Safari, there’s a “full page” feature that converts the current HTML to a PDF and saves it in “Files.” So you might be thinking, this new feature is nothing, I don’t make a browser App. We can actually turn a scrollView into an image, and then an image into a PDF, so we can make this scrollView into a long image, so let’s see what it looks like.

That’s pretty good, so let’s see how it works.

We must first in the controller to realize UIScreenshotServiceDelegate agent, because iOS 13 project structure changed, here are two types of set up the agent.

// iOS 13 project structure
let scene = UIApplication.shared.connectedScenes.first? .delegateas? SceneDelegatescene? .window? .windowScene? .screenshotService? .delegate =self

// Pre-ios 13 project structure
UIApplication.shared.keyWindow? .windowScene? .screenshotService? .delegate =self
Copy the code

UIScreenshotServiceDelegate agent is only one way, let us to implement it

func screenshotService(_ screenshotService: UIScreenshotService, generatePDFRepresentationWithCompletion completionHandler: @escaping (Data? , Int, CGRect) -> Void) {
    completionHandler(getScreenshotData(tableView), 0.CGRect.zero)
}
Copy the code

Taking a look at the callback, the first parameter is the PDF’s data, the second parameter is the INDEX of the PDF page, and the third parameter is the coordinates in the PDF relative to the current page. GetScreenshotData is a method I wrote myself, the logic of the method is scrollView → image → PDF → data, because there is not much code, and can be found on the Internet, I will not post it.

Scrollview.frame = CGRect(Origin:.zero, size: scrollView.contentSize)

Note: A tableView will cause all cells to be loaded. If the current controller is an infinite list, do not use this feature.

Gestures

Two-finger sliding gestures

In iOS 13, the tableView and collectionView have been added with the function of two-finger sliding editing. This function is used in text messages and memos. Now let’s take a look at the effect.

This function experience is also very cool, if your App has the corresponding scene, it is recommended to add this function, let’s see how to achieve this effect.

First set the tableView. AllowsMultipleSelectionDuringEditing = true to allow multiple, then implement method of two agents.

/// Whether to allow multiple selection
optional func tableView(_ tableView: UITableView, shouldBeginMultipleSelectionInteractionAtIndexPath indexPath: IndexPath) -> Bool

/// You can make some UI changes here, such as modifying the text of the buttons on the navigation bar
optional func tableView(_ tableView: UITableView, didBeginMultipleSelectionInteractionAtIndexPath indexPath: IndexPath) 
Copy the code

When the user selects the finally, want to do some operations, we can use the tableView. IndexPathsForSelectedRows get the user selected rows.

Edit the gestures

  • Copy: Three fingers kneading
  • Cut: Two three-finger kneads
  • Paste: three fingers loosen
  • Undo: Swipe left with three fingers (or double click with three fingers)
  • Redo: Three-point swipe right
  • Shortcut menu: Three-finger click

IOS 13 added some gesture of text editing, these gestures to the system default will provide, if we want to disable these gestures, needs to be rewritten editingInteractionConfiguration attributes, the code is as follows.

override var editingInteractionConfiguration: UIEditingInteractionConfiguration {
    return .none
}
Copy the code

Presentations

Present in iOS 13 now looks like this.

This brings a new way of interacting. Drop down to dismiss controller. Actually, this is a great feature and greatly improves the experience, but for us developers, it brings up some pits.

First, UIModalPresentationStyle adds an automatic attribute, which is the default in iOS 13. The system is going to choose whether it’s pageSheet or fullScreen based on the controller that comes out, so for example, when we come out with UIImagePickerController and the camera is fullScreen, the controller that we write ourselves is pageSheet. If we want to launch fullScreen controllers is also very simple, before the present set of vc. ModalPresentationStyle =. FullScreen.

So let’s talk about what the pits of pageSheet are, and let’s look at the order in which the fullscreens are called.

pageSheet

When controller A presents controller B, viewWillDisappear and viewDidDisappear of controller A are not called, when controller B is dismissed, ViewWillAppear and viewDidAppear of controller A are not called either. That is to say, if you have some logic is on of the four methods, or your business logic to change a place, or set of vc. ModalPresentationStyle =. FullScreen.

In addition, UIViewController adds a property isModalInPresentation, which defaults to false. When this property is false, the user can pull down the dismiss controller, and when it is true, the user cannot pull down the Dismiss controller. This property can be used with controllers with editing capabilities, so let’s take a look at the official Demo

We can see that unedited content can be pulled down in dismiss, edited content can not be pulled down in dismiss, and an alert pops up to remind the user whether to save the edited content. The detailed code you can go to the Demo to see, here is a brief talk.

First, determine whether the user input, if there is input, change isModalInPresentation to true. Then implement UIAdaptivePresentationControllerDelegate agent presentationControllerDidAttemptToDismiss: method. This method is called when isModalInPresentation = true and the user tries to pull down the dismiss controller. Finally, an alert will pop up in this method to remind the user whether to save the edited content.

Search

The UISearchViewController structure in iOS 13 is as follows.

UISearchBar
UISearchBar
UISearchTextField

let field = searchController.searchBar.searchTextField
field.textColor = UIColor.label
field.font = UIFont.systemFont(ofSize: 20)
Copy the code

Secondly, Token function is added. Token can be copied, pasted and dragged. Token also has the following features:

  1. Always precede plain text;
  2. Can be selected and deleted;
  3. Can be selected with plain text;

Token
Token

The first way is to create a Token directly
let field = searchController.searchBar.searchTextField
field.insertToken(UISearchToken(icon: nil, text: "Token"), at: 0)

// The second way is to select a piece of text and turn it into a Token, as shown in the figure below
let field = searchController.searchBar.searchTextField
guard letselectedTextRange = field.selectedTextRange, ! selectedTextRange.isEmptyelse { return }
guard let selectedText = field.text(in: selectedTextRange) else { return } // "beach"
let token = UISearchToken(icon: nil, text: selectedText)
field.replaceTextualPortion(of: selectedTextRange, with: token, at: field.tokens.count)
Copy the code

textualRange

Finally introduce showsSearchResultsController attribute, this property controller can control whether to display search results.

Menus

Remember when I said at the beginning of this article that some apis change once a beta… That’s right UIMenu every beta is written differently (take a date pill) let’s see how it works first.

Let’s analyze the structure in the GIF, as shown here

We can see that UIMenu can nest UIAction and we can nest UIMenu again. Let’s see how this is done. First create a UIContextMenuInteraction object and add it to the corresponding View.

let menuInteraction = UIContextMenuInteraction(delegate: self)
menuView.addInteraction(menuInteraction)
Copy the code

UIMenu second UIContextMenuInteractionDelegate agent, configuration.

let menuInteraction = UIContextMenuInteraction(delegate: self)
menuView.addInteraction(menuInteraction)
Copy the code

UIMenu second UIContextMenuInteractionDelegate agent, configuration.

func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? {
    return UIContextMenuConfiguration(identifier: nil, previewProvider: { () -> UIViewController? in
        // The controller to display
        return ViewController2()
    }) { (list) -> UIMenu? in
        let editMenu = UIMenu(title: "Edit...", image: nil, identifier: nil, options: [], children: [
            UIAction(title: "Copy", image: nil, identifier: nil, discoverabilityTitle: nil, attributes: [], state: .off, handler: { (_) in
                print("Copy")}),UIAction(title: "Duplicate", image: nil, identifier: nil, discoverabilityTitle: nil, attributes: [], state: .off, handler: { (_) in
                print("Duplicate")})])return UIMenu(title: "", image: nil, identifier: nil, options: [], children: [
            UIAction(title: "Share", image: nil, identifier: nil, discoverabilityTitle: nil, attributes: [], state: .off, handler: { (_) in
                print("Share")
            }),
            editMenu,
            UIAction(title: "Delete", image: nil, identifier: nil, discoverabilityTitle: nil, attributes: [.destructive], state: .off, handler: { (_) in
                print("Delete")})])}}Copy the code

There are a lot of codes, but it is not difficult. We will analyze them step by step. In addition, I deleted the codes related to pictures, which have little meaning and will affect the reading experience.

First of all, this method requires that we return a UIContextMenuConfiguration object, the object initialization method has three arguments, the first is the identifier, the second is a closure, request return to display controller, the third is a closure, A UIMenu object is required to be returned.

UIMenu

Now let’s take a look at how UIMenu is created. First we create the editMenu, which is the second bar in the GIF, and when we click on it it will bring up two UIActions, and then let’s see how we create UIMenu.

init(title: String, 
     image: UIImage? = nil, 
     identifier: UIMenu.Identifier? = nil, 
     options: UIMenu.Options = [], 
     children: [UIMenuElement] = [])
Copy the code

Uimenu. options is declared as follows.

public struct Options : OptionSet {
    public init(rawValue: UInt)
    /// Show children inline in parent, instead of hierarchically
    public static var displayInline: UIMenu.Options { get }
    /// Indicates whether the menu should be rendered with a destructive appearance in its parent
    public static var destructive: UIMenu.Options { get}}Copy the code

The options parameter is used for the second layer menu. We can see that the Delete in the GIF is red because it is UIAction and has corresponding properties that can be set, so if I want to Edit… To make it red, you’re going to set options = destructive. Now displayInline, the effect is to put the second menu in the first layer and display it as follows.

Options is an OptionSet, meaning that two properties can be set simultaneously. Only the displayInline effect, made into OptionSet should be for future expansion, is not useful at present.

UIAction

Now let’s look at the initialization method of UIAction.

init(title: String, 
     image: UIImage? = nil, 
     identifier: UIAction.Identifier? = nil, 
     discoverabilityTitle: String? = nil, 
     attributes: UIMenuElement.Attributes = [], 
     state: UIMenuElement.State = .off, 
     handler: @escaping UIActionHandler)
Copy the code

The first three parameters are not mentioned, and the fourth parameter discoverabilityTitle is not used. If you know, please leave a message in the comment section.

The fifth parameter, Attributes, takes a look at declarations and renderings.

public struct Attributes : OptionSet {
    public init(rawValue: UInt)
    public static var disabled: UIMenuElement.Attributes { get }
    public static var destructive: UIMenuElement.Attributes { get }
    public static var hidden: UIMenuElement.Attributes { get}}Copy the code

attributes
OptionSet

The sixth parameter, state, also looks at the declaration and rendering.

public enum State : Int {
    case off
    case on
    case mixed
}
Copy the code

state
attributes
on
mixed
UIAction
state = .on

The seventh argument is a closure that will be called back when the user clicks on it, handling the logic accordingly.

Finally we put UIAction and editMenu together in a new UIMenu to achieve the effect in the GIF.


These are some of the new features of iOS 13. Please point out any errors.

WWDC link Modernizing Your UI for iOS 13

If you want to know how iOS 13 fits into night mode, you can read this article.