2021 EssentialsSwiftUI & UI Frameworks
WWDC21 · 24 min · Essentials / SwiftUI & UI Frameworks
Qualities of a great Mac Catalyst app
Discover best practices, tools, and techniques to help craft the best possible Mac Catalyst app. We’ll take you through key considerations when you bring your iPad app to macOS, explore detailed code examples for refining your interface and experience, and show you how to distribute your Mac app to everyone. To get the most out of this session, we recommend a basic familiarity with Mac Catalyst. Watch “What’s new in Mac Catalyst” from WWDC21 to get an overview of the latest features for bringing your iPad app to Mac. And for more on improving your macOS experience, watch “Optimize the interface of your Mac Catalyst app” from WWDC20.
Watch at developer.apple.com ↗Code shown on screen · 14 snippets
System button
let button = UIButton(type: .system)
button.setTitle("Button", for: .normal) Pull-down button
button.menu = UIMenu(...)
button.showsMenuAsPrimaryAction = true Pop-up button
button.menu = UIMenu(...)
button.showsMenuAsPrimaryAction = true
button.changesSelectionAsPrimaryAction = true Checkbox
let checkbox = UISwitch()
if checkbox.style == .checkbox {
checkbox.title = "Checkbox"
} Delegating actions
final class MyView: UIView {
override func target(forAction action: Selector, withSender sender: Any?) -> Any? {
if action == #selector(Model.setAsFavorite(_:)) {
return myModel
} else {
return super.target(forAction: action, withSender: sender)
}
}
} Requesting a new scene
let viewDetailActivityType = "viewDetail"
let itemIDKey = "itemID"
final class MyView: UIView {
@objc func viewDoubleClicked(_ sender: Any?) {
let userActivity = NSUserActivity(activityType: viewDetailActivityType)
userActivity.userInfo = [itemIDKey: selectedItem.itemID]
UIApplication.shared.requestSceneSessionActivation(nil,
userActivity: userActivity,
options: nil,
errorHandler: { error in //...
})
}
//...
} Responding to a new scene request
let viewDetailActivityType = "viewDetail"
final class AppDelegate: UIApplicationDelegate {
func application(_ application: UIApplication,
configurationForConnecting session: UISceneSession,
options: UIScene.ConnectionOptions) -> UISceneConfiguration {
if let activity = options.userActivities.first {
if activity.activityType == viewDetailActivityType {
return UISceneConfiguration(name: "DetailViewer", sessionRole:session.role)
}
}
return UISceneConfiguration(name: "Default Configuration",
sessionRole: session.role)
}
//...
} Setting item ID on new scene's root view controller
let itemIDKey = "itemID"
final class SceneDelegate: UIWindowSceneDelegate {
func scene(_ scene: UIScene, willConnectTo session: UISceneSession,
options: UIScene.ConnectionOptions) {
if let userActivity = connectionOptions.userActivities.first {
if let itemId = userActivity.userInfo?[itemIDKey] as? ItemIDType {
// Set item ID on new view controller
}
}
//...
}
//... Saving state for later restoration
final class SceneDelegate: UIWindowSceneDelegate {
func stateRestorationActivity(for scene: UIScene) -> NSUserActivity? {
//...
}
} State restoration
final class AppDelegate: UIApplicationDelegate {
func application(_ application: UIApplication,
configurationForConnecting session: UISceneSession,
options: UIScene.ConnectionOptions) -> UISceneConfiguration {
//...
}
} Handle both new scene requests and state restoration
let itemIDKey = "itemID"
final class SceneDelegate: UIWindowSceneDelegate {
func scene(_ scene: UIScene, willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions) {
if let userActivity = connectionOptions.userActivities.first ??
session.stateRestorationActivity {
if let itemId = userActivity.userInfo?[itemIDKey] as? ItemIDType {
// Set item ID on new view controller
}
}
}
} Provide sharing configuration for the scene
final class RootViewController: UIViewController {
override var activityItemsConfiguration: UIActivityItemsConfigurationReading? {
get { UIActivityItemsConfiguration(objects: [image]) }
//...
}
} Support sharing through context menu
final class MyView: UIView {
override var activityItemsConfiguration: UIActivityItemsConfigurationReading? {
get { UIActivityItemsConfiguration(objects: images) }
//...
}
func viewDidLoad() {
let contextMenuInteraction = UIContextMenuInteraction(delegate: self)
addInteraction(contextMenuInteraction)
}
} Supporting continuity camera
final class MyView: UIView {
override var pasteConfiguration: UIPasteConfiguration? {
get { UIPasteConfiguration(forAcceptingClass: UIImage.self) }
//...
}
func willMove(toWindow: UIWindow) {
addInteraction(contextMenuInteraction)
}
override func paste(itemProviders: [NSItemProvider]) {
for itemProvider in itemProviders {
if itemProvider.canLoadObject(ofClass: UIImage.self) {
if let image = try? await itemProvider.loadObject(ofClass:UIImage.self) {
insertImage(image)
}
//... Resources
Related sessions
-
27 min -
23 min -
36 min -
27 min -
20 min -
20 min -
38 min