├── CommandBar
├── Assets.xcassets
│ ├── Contents.json
│ ├── AppIcon.appiconset
│ │ ├── icon20.png
│ │ ├── icon-1024.png
│ │ ├── icon-120.png
│ │ ├── icon-152.png
│ │ ├── icon-167.png
│ │ ├── icon-180.png
│ │ ├── icon-29.png
│ │ ├── icon-40.png
│ │ ├── icon-58.png
│ │ ├── icon-60.png
│ │ ├── icon-76.png
│ │ ├── icon-80.png
│ │ ├── icon-87.png
│ │ └── Contents.json
│ └── swiftdoc.imageset
│ │ ├── swiftdoc.png
│ │ └── Contents.json
├── Source
│ ├── Number+Scaling.swift
│ ├── Application
│ │ ├── CMBSceneDelegate+NSToolbar.swift
│ │ ├── CMBAppDelegate.swift
│ │ └── CMBSceneDelegate.swift
│ └── Main
│ │ ├── CMBMainViewController.swift
│ │ ├── CMBCommandBarViewController.swift
│ │ ├── CMBCommandBarResultsCell.swift
│ │ └── CMBCommandBarResultsViewController.swift
├── Info.plist
└── Base.lproj
│ └── LaunchScreen.storyboard
├── CommandBar.xcodeproj
├── project.xcworkspace
│ └── contents.xcworkspacedata
└── project.pbxproj
└── README.md
/CommandBar/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/CommandBar/Assets.xcassets/AppIcon.appiconset/icon20.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/steventroughtonsmith/CatalystCommandBar/HEAD/CommandBar/Assets.xcassets/AppIcon.appiconset/icon20.png
--------------------------------------------------------------------------------
/CommandBar/Assets.xcassets/AppIcon.appiconset/icon-1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/steventroughtonsmith/CatalystCommandBar/HEAD/CommandBar/Assets.xcassets/AppIcon.appiconset/icon-1024.png
--------------------------------------------------------------------------------
/CommandBar/Assets.xcassets/AppIcon.appiconset/icon-120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/steventroughtonsmith/CatalystCommandBar/HEAD/CommandBar/Assets.xcassets/AppIcon.appiconset/icon-120.png
--------------------------------------------------------------------------------
/CommandBar/Assets.xcassets/AppIcon.appiconset/icon-152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/steventroughtonsmith/CatalystCommandBar/HEAD/CommandBar/Assets.xcassets/AppIcon.appiconset/icon-152.png
--------------------------------------------------------------------------------
/CommandBar/Assets.xcassets/AppIcon.appiconset/icon-167.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/steventroughtonsmith/CatalystCommandBar/HEAD/CommandBar/Assets.xcassets/AppIcon.appiconset/icon-167.png
--------------------------------------------------------------------------------
/CommandBar/Assets.xcassets/AppIcon.appiconset/icon-180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/steventroughtonsmith/CatalystCommandBar/HEAD/CommandBar/Assets.xcassets/AppIcon.appiconset/icon-180.png
--------------------------------------------------------------------------------
/CommandBar/Assets.xcassets/AppIcon.appiconset/icon-29.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/steventroughtonsmith/CatalystCommandBar/HEAD/CommandBar/Assets.xcassets/AppIcon.appiconset/icon-29.png
--------------------------------------------------------------------------------
/CommandBar/Assets.xcassets/AppIcon.appiconset/icon-40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/steventroughtonsmith/CatalystCommandBar/HEAD/CommandBar/Assets.xcassets/AppIcon.appiconset/icon-40.png
--------------------------------------------------------------------------------
/CommandBar/Assets.xcassets/AppIcon.appiconset/icon-58.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/steventroughtonsmith/CatalystCommandBar/HEAD/CommandBar/Assets.xcassets/AppIcon.appiconset/icon-58.png
--------------------------------------------------------------------------------
/CommandBar/Assets.xcassets/AppIcon.appiconset/icon-60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/steventroughtonsmith/CatalystCommandBar/HEAD/CommandBar/Assets.xcassets/AppIcon.appiconset/icon-60.png
--------------------------------------------------------------------------------
/CommandBar/Assets.xcassets/AppIcon.appiconset/icon-76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/steventroughtonsmith/CatalystCommandBar/HEAD/CommandBar/Assets.xcassets/AppIcon.appiconset/icon-76.png
--------------------------------------------------------------------------------
/CommandBar/Assets.xcassets/AppIcon.appiconset/icon-80.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/steventroughtonsmith/CatalystCommandBar/HEAD/CommandBar/Assets.xcassets/AppIcon.appiconset/icon-80.png
--------------------------------------------------------------------------------
/CommandBar/Assets.xcassets/AppIcon.appiconset/icon-87.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/steventroughtonsmith/CatalystCommandBar/HEAD/CommandBar/Assets.xcassets/AppIcon.appiconset/icon-87.png
--------------------------------------------------------------------------------
/CommandBar/Assets.xcassets/swiftdoc.imageset/swiftdoc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/steventroughtonsmith/CatalystCommandBar/HEAD/CommandBar/Assets.xcassets/swiftdoc.imageset/swiftdoc.png
--------------------------------------------------------------------------------
/CommandBar.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/CommandBar/Assets.xcassets/swiftdoc.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "swiftdoc.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CommandBar
2 |
3 | This is an illustrative example of a Spotlight-style 'command bar', like the 'Open Quickly' panel in Xcode, implemented using UIKit/Mac Catalyst.
4 |
5 | Using the keyboard, you can navigate the results list, select an item from the list, and dismiss the popup, without losing focus in the search field.
6 |
7 | ### Screenshots
8 |
9 | 
10 |
--------------------------------------------------------------------------------
/CommandBar/Source/Number+Scaling.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CGFloat+Scaling.swift
3 | // Voxcaster
4 | //
5 | // Created by Steven Troughton-Smith on 29/07/2020.
6 | //
7 |
8 | import UIKit
9 |
10 | public let supportsMacIdiom = !(UIDevice.current.userInterfaceIdiom == .pad)
11 |
12 | @inlinable func UIFloat(_ value: CGFloat) -> CGFloat
13 | {
14 | #if targetEnvironment(macCatalyst)
15 | return (value == 0.5) ? 0.5 : round(value * (supportsMacIdiom ? 0.77 : 1.0))
16 | #else
17 | return value
18 | #endif
19 | }
20 |
--------------------------------------------------------------------------------
/CommandBar/Source/Application/CMBSceneDelegate+NSToolbar.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CMBSceneDelegate+NSToolbar.swift
3 | // CommandBar
4 | //
5 | // Created by Steven Troughton-Smith on 29/12/2021.
6 | //
7 | //
8 |
9 | import UIKit
10 |
11 | #if targetEnvironment(macCatalyst)
12 | import AppKit
13 |
14 | extension CMBSceneDelegate: NSToolbarDelegate {
15 |
16 | func toolbarItems() -> [NSToolbarItem.Identifier] {
17 | return [.toggleSidebar]
18 | }
19 |
20 | func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
21 | return toolbarItems()
22 | }
23 |
24 | func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
25 | return toolbarItems()
26 | }
27 |
28 | func toolbar(_ toolbar: NSToolbar, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier, willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? {
29 | return NSToolbarItem(itemIdentifier: itemIdentifier)
30 | }
31 | }
32 | #endif
33 |
--------------------------------------------------------------------------------
/CommandBar/Source/Application/CMBAppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CMBAppDelegate.swift
3 | // CommandBar
4 | //
5 | // Created by Steven Troughton-Smith on 29/12/2021.
6 | //
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class CMBAppDelegate: UIResponder, UIApplicationDelegate {
13 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
14 | return true
15 | }
16 |
17 | // MARK: - Menus
18 |
19 | override func buildMenu(with builder: UIMenuBuilder) {
20 |
21 | if builder.system == UIMenuSystem.context {
22 | return
23 | }
24 |
25 | super.buildMenu(with: builder)
26 |
27 | let command = UIKeyCommand(input: "o", modifierFlags: [.command, .shift], action: #selector(CMBMainViewController.showCommandBar(_:)))
28 |
29 | command.title = NSLocalizedString("Open Quickly…", comment: "")
30 |
31 | let menu = UIMenu(title: "", image: nil, identifier: UIMenu.Identifier("MENU_FILE_OPENQUICKLY"), options: .displayInline, children: [command])
32 | builder.insertSibling(menu, afterMenu: .newScene)
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/CommandBar/Source/Application/CMBSceneDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CMBSceneDelegate.swift
3 | // CommandBar
4 | //
5 | // Created by Steven Troughton-Smith on 29/12/2021.
6 | //
7 | //
8 |
9 | import UIKit
10 |
11 | class CMBSceneDelegate: UIResponder, UIWindowSceneDelegate {
12 | var window: UIWindow?
13 |
14 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
15 | guard let windowScene = scene as? UIWindowScene else {
16 | fatalError("Expected scene of type UIWindowScene but got an unexpected type")
17 | }
18 | window = UIWindow(windowScene: windowScene)
19 |
20 | if let window = window {
21 | window.rootViewController = CMBMainViewController()
22 |
23 | #if targetEnvironment(macCatalyst)
24 |
25 | let toolbar = NSToolbar(identifier: NSToolbar.Identifier("CMBSceneDelegate.Toolbar"))
26 | toolbar.delegate = self
27 | toolbar.displayMode = .iconOnly
28 | toolbar.allowsUserCustomization = false
29 |
30 | windowScene.titlebar?.toolbar = toolbar
31 | windowScene.titlebar?.toolbarStyle = .unified
32 |
33 | #endif
34 |
35 | window.makeKeyAndVisible()
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/CommandBar/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ITSAppUsesNonExemptEncryption
6 |
7 | LSRequiresIPhoneOS
8 |
9 | UIApplicationSceneManifest
10 |
11 | UIApplicationSupportsMultipleScenes
12 |
13 | UISceneConfigurations
14 |
15 | UIWindowSceneSessionRoleApplication
16 |
17 |
18 | UISceneConfigurationName
19 | Default Configuration
20 | UISceneDelegateClassName
21 | $(PRODUCT_MODULE_NAME).CMBSceneDelegate
22 |
23 |
24 |
25 |
26 | UIApplicationSupportsIndirectInputEvents
27 |
28 | UILaunchStoryboardName
29 | LaunchScreen
30 | UIRequiredDeviceCapabilities
31 |
32 | armv7
33 |
34 | UISupportedInterfaceOrientations
35 |
36 | UIInterfaceOrientationPortrait
37 | UIInterfaceOrientationLandscapeLeft
38 | UIInterfaceOrientationLandscapeRight
39 |
40 | UISupportedInterfaceOrientations~ipad
41 |
42 | UIInterfaceOrientationPortrait
43 | UIInterfaceOrientationPortraitUpsideDown
44 | UIInterfaceOrientationLandscapeLeft
45 | UIInterfaceOrientationLandscapeRight
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/CommandBar/Source/Main/CMBMainViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CMBMainViewController.swift
3 | // CommandBar
4 | //
5 | // Created by Steven Troughton-Smith on 29/12/2021.
6 | //
7 | //
8 |
9 | import UIKit
10 |
11 | extension NSObject {
12 | @objc func CMB_shouldHideAnchor() -> Bool {
13 | return true
14 | }
15 | }
16 |
17 | final class CMBMainViewController: UIViewController, UIPopoverPresentationControllerDelegate {
18 |
19 | init() {
20 | super.init(nibName: nil, bundle: nil)
21 | title = "CommandBar"
22 | view.backgroundColor = .systemBackground
23 |
24 | /*
25 | macOS needs a straightforward swizzle to hide the popover arrow.
26 |
27 | N.B.: if you have other popovers in your app, you might want to find a way to specify
28 | that only certain NSPopover instances hide the arrow. Outside of scope for this
29 | example!
30 | */
31 |
32 | do {
33 | let m1 = class_getInstanceMethod(NSClassFromString("NSPopover"), NSSelectorFromString("shouldHideAnchor"))
34 | let m2 = class_getInstanceMethod(NSClassFromString("NSPopover"), NSSelectorFromString("CMB_shouldHideAnchor"))
35 |
36 | if let m1 = m1, let m2 = m2 {
37 | method_exchangeImplementations(m1, m2)
38 | }
39 | }
40 |
41 | }
42 |
43 | required init?(coder aDecoder: NSCoder) {
44 | fatalError("init(coder:) has not been implemented")
45 | }
46 |
47 | // MARK: -
48 |
49 | func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
50 | return .none
51 | }
52 |
53 | #if !targetEnvironment(macCatalyst)
54 | override var keyCommands:[UIKeyCommand] {
55 | return [UIKeyCommand(input: "o", modifierFlags: [.command, .shift], action: #selector(showCommandBar(_:)))]
56 | }
57 | #endif
58 |
59 | @objc func showCommandBar(_ sender:Any?) {
60 | let cmb = CMBCommandBarViewController()
61 | cmb.modalPresentationStyle = .popover
62 | cmb.preferredContentSize = CGSize(width: UIFloat(400), height: UIFloat(80))
63 |
64 | if let pc = cmb.popoverPresentationController {
65 | pc.delegate = self
66 | pc.sourceView = view
67 | let safeRect = view.bounds.inset(by:view.safeAreaInsets)
68 |
69 | #if targetEnvironment(macCatalyst)
70 | pc.permittedArrowDirections = [.up]
71 | #else
72 | pc.permittedArrowDirections = [.init(rawValue:0)] // iOS lets you hide the popover arrow like so
73 | #endif
74 | pc.sourceRect = CGRect(x: safeRect.midX, y: safeRect.midY / 2, width: 1, height: 1)
75 | }
76 | present(cmb, animated: true, completion: {
77 | cmb.searchField.becomeFirstResponder()
78 | })
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/CommandBar/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images": [
3 | {
4 | "size": "20x20",
5 | "idiom": "iphone",
6 | "filename": "icon-40.png",
7 | "scale": "2x"
8 | },
9 | {
10 | "size": "20x20",
11 | "idiom": "iphone",
12 | "filename": "icon-60.png",
13 | "scale": "3x"
14 | },
15 | {
16 | "size": "29x29",
17 | "idiom": "iphone",
18 | "filename": "icon-58.png",
19 | "scale": "2x"
20 | },
21 | {
22 | "size": "29x29",
23 | "idiom": "iphone",
24 | "filename": "icon-87.png",
25 | "scale": "3x"
26 | },
27 | {
28 | "size": "40x40",
29 | "idiom": "iphone",
30 | "filename": "icon-80.png",
31 | "scale": "2x"
32 | },
33 | {
34 | "size": "40x40",
35 | "idiom": "iphone",
36 | "filename": "icon-120.png",
37 | "scale": "3x"
38 | },
39 | {
40 | "size": "60x60",
41 | "idiom": "iphone",
42 | "filename": "icon-120.png",
43 | "scale": "2x"
44 | },
45 | {
46 | "size": "60x60",
47 | "idiom": "iphone",
48 | "filename": "icon-180.png",
49 | "scale": "3x"
50 | },
51 | {
52 | "size": "20x20",
53 | "idiom": "ipad",
54 | "filename": "icon20.png",
55 | "scale": "1x"
56 | },
57 | {
58 | "size": "20x20",
59 | "idiom": "ipad",
60 | "filename": "icon-40.png",
61 | "scale": "2x"
62 | },
63 | {
64 | "size": "29x29",
65 | "idiom": "ipad",
66 | "filename": "icon-29.png",
67 | "scale": "1x"
68 | },
69 | {
70 | "size": "29x29",
71 | "idiom": "ipad",
72 | "filename": "icon-58.png",
73 | "scale": "2x"
74 | },
75 | {
76 | "size": "40x40",
77 | "idiom": "ipad",
78 | "filename": "icon-40.png",
79 | "scale": "1x"
80 | },
81 | {
82 | "size": "40x40",
83 | "idiom": "ipad",
84 | "filename": "icon-80.png",
85 | "scale": "2x"
86 | },
87 | {
88 | "size": "76x76",
89 | "idiom": "ipad",
90 | "filename": "icon-76.png",
91 | "scale": "1x"
92 | },
93 | {
94 | "size": "76x76",
95 | "idiom": "ipad",
96 | "filename": "icon-152.png",
97 | "scale": "2x"
98 | },
99 | {
100 | "size": "83.5x83.5",
101 | "idiom": "ipad",
102 | "filename": "icon-167.png",
103 | "scale": "2x"
104 | },
105 | {
106 | "size": "1024x1024",
107 | "idiom": "ios-marketing",
108 | "filename": "icon-1024.png",
109 | "scale": "1x"
110 | }
111 | ],
112 | "info": {
113 | "version": 1,
114 | "author": "xcode"
115 | }
116 | }
--------------------------------------------------------------------------------
/CommandBar/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/CommandBar/Source/Main/CMBCommandBarViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CMBCommandBarViewController.swift
3 | // CommandBar
4 | //
5 | // Created by Steven Troughton-Smith on 29/12/2021.
6 | //
7 |
8 | import UIKit
9 | import CoreGraphics
10 | import Foundation
11 |
12 | extension NSNotification.Name {
13 | static let upArrowPressedNotification = NSNotification.Name("CMBSearchTextFieldUp")
14 | static let downArrowPressedNotification = NSNotification.Name("CMBSearchTextFieldDown")
15 | static let returnPressedNotification = NSNotification.Name("CMBSearchTextFieldReturn")
16 | }
17 |
18 | class CMBCommandBarViewController: UIViewController, UITextFieldDelegate {
19 |
20 | let searchField = UISearchTextField()
21 | let resultsViewController = CMBCommandBarResultsViewController()
22 |
23 | init() {
24 | super.init(nibName:nil, bundle:nil)
25 |
26 | searchField.font = UIFont.systemFont(ofSize: UIFloat(26))
27 | searchField.focusEffect = nil
28 | searchField.delegate = self
29 | searchField.placeholder = NSLocalizedString("Open Quickly", comment: "")
30 | searchField.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged)
31 | searchField.borderStyle = .none
32 |
33 | view.addSubview(searchField)
34 |
35 | addChild(resultsViewController)
36 | view.addSubview(resultsViewController.view)
37 | }
38 |
39 | required init?(coder: NSCoder) {
40 | fatalError("init(coder:) has not been implemented")
41 | }
42 |
43 | // MARK: -
44 |
45 | override func viewDidLayoutSubviews() {
46 |
47 | let searchHeight = UIFloat(80)
48 |
49 | let safeFrame = view.bounds.inset(by:view.safeAreaInsets)
50 |
51 | let searchRect = CGRect(x: safeFrame.minX, y: safeFrame.minY, width: safeFrame.width, height: searchHeight)
52 | searchField.frame = searchRect
53 |
54 | resultsViewController.view.frame = CGRect(x: safeFrame.minX, y: safeFrame.minY + searchHeight, width: safeFrame.width, height: (preferredExpandedContentSize.height-safeFrame.minY)-searchHeight)
55 | }
56 |
57 | // MARK: - Keyboard Navigation
58 |
59 | override var keyCommands:[UIKeyCommand] {
60 | get {
61 | let upItem = UIKeyCommand(input: UIKeyCommand.inputUpArrow, modifierFlags: [], action: #selector(goUp(_:)))
62 | let downItem = UIKeyCommand(input: UIKeyCommand.inputDownArrow, modifierFlags: [], action: #selector(goDown(_:)))
63 | let escapeItem = UIKeyCommand(input: UIKeyCommand.inputEscape, modifierFlags: [], action: #selector(dismiss(_:)))
64 | let returnItem = UIKeyCommand(input: "\r", modifierFlags: [], action: #selector(actuateSelection(_:)))
65 |
66 | upItem.wantsPriorityOverSystemBehavior = true
67 | downItem.wantsPriorityOverSystemBehavior = true
68 | returnItem.wantsPriorityOverSystemBehavior = true
69 |
70 | return [upItem, downItem, escapeItem, returnItem]
71 | }
72 | }
73 |
74 | @objc func actuateSelection(_ sender:Any?) {
75 | NotificationCenter.default.post(name: .returnPressedNotification, object: nil)
76 | }
77 |
78 | @objc func goUp(_ sender:Any?) {
79 | NotificationCenter.default.post(name: .upArrowPressedNotification, object: nil)
80 | }
81 |
82 | @objc func goDown(_ sender:Any?) {
83 | NotificationCenter.default.post(name: .downArrowPressedNotification, object: nil)
84 | }
85 |
86 | @objc func textFieldDidChange(_ textField:UITextField) {
87 | if textField.text == "" {
88 | contract()
89 | }
90 | else {
91 | expand()
92 | }
93 | }
94 |
95 | @objc func dismiss(_ sender:Any?) {
96 | searchField.resignFirstResponder()
97 | presentingViewController?.dismiss(animated:true)
98 | }
99 |
100 | // MARK: -
101 |
102 | var preferredExpandedContentSize = CGSize(width: UIFloat(400), height: UIFloat(400))
103 | var preferredContractedContentSize = CGSize(width: UIFloat(400), height: UIFloat(80))
104 |
105 | // MARK: -
106 |
107 |
108 | func contract() {
109 |
110 | preferredContentSize = preferredContractedContentSize
111 | }
112 |
113 | func expand() {
114 |
115 | preferredContentSize = preferredExpandedContentSize
116 | }
117 |
118 |
119 | }
120 |
--------------------------------------------------------------------------------
/CommandBar/Source/Main/CMBCommandBarResultsCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CMBCommandBarResultsCell.swift
3 | // CommandBar
4 | //
5 | // Created by Steven Troughton-Smith on 29/12/2021.
6 | //
7 |
8 | import UIKit
9 |
10 | class CMBCommandBarResultsCell: UICollectionViewCell {
11 |
12 | let imageView = UIImageView()
13 | let textLabel = UILabel()
14 | let subtitleLabel = UILabel()
15 |
16 | override var isSelected: Bool {
17 | didSet {
18 | recolor()
19 | }
20 | }
21 |
22 | // MARK: -
23 |
24 | override init(frame:CGRect) {
25 | super.init(frame: .zero)
26 |
27 | textLabel.font = UIFont.systemFont(ofSize: UIFloat(16))
28 | subtitleLabel.font = UIFont.systemFont(ofSize: UIFloat(14))
29 |
30 | contentView.addSubview(textLabel)
31 | contentView.addSubview(subtitleLabel)
32 |
33 | imageView.contentMode = .scaleAspectFit
34 | contentView.addSubview(imageView)
35 |
36 | let selection = UIView()
37 | selection.backgroundColor = .systemBlue
38 | selection.layer.cornerRadius = UIFloat(8)
39 | selection.layer.cornerCurve = .continuous
40 |
41 | selectedBackgroundView = selection
42 |
43 | if let selectedBackgroundView = selectedBackgroundView {
44 | selectedBackgroundView.alpha = 0
45 | insertSubview(selectedBackgroundView, at: 0)
46 |
47 | selectedBackgroundView.layer.cornerRadius = UIFloat(8)
48 | selectedBackgroundView.layer.cornerCurve = .continuous
49 | selectedBackgroundView.layer.masksToBounds = true
50 | }
51 |
52 | contentView.layer.cornerRadius = UIFloat(8)
53 | contentView.layer.cornerCurve = .continuous
54 | contentView.layer.masksToBounds = true
55 |
56 | if #available(macCatalyst 15.0, iOS 15.0, *) {
57 | focusEffect = nil
58 | }
59 |
60 | recolor()
61 | }
62 |
63 | required init?(coder: NSCoder) {
64 | fatalError("init(coder:) has not been implemented")
65 | }
66 |
67 | // MARK: -
68 |
69 | override func layoutSubviews() {
70 | super.layoutSubviews()
71 |
72 | let imageWidth = UIFloat(70)
73 |
74 | let xMargins = UIFloat(13)
75 | let yMargins = UIFloat(0)
76 | let padding = UIFloat(8)
77 | let textPadding = UIFloat(8)
78 |
79 | let safeContentArea = bounds.insetBy(dx: xMargins, dy: yMargins)
80 |
81 | contentView.frame = safeContentArea
82 | selectedBackgroundView?.frame = safeContentArea
83 |
84 | let division = contentView.bounds.divided(atDistance: imageWidth, from: .minXEdge)
85 |
86 | var imageViewRect = division.slice.insetBy(dx: padding, dy: 0)
87 | imageViewRect.size.height = imageViewRect.size.width*3/4
88 | imageViewRect.origin.y = (safeContentArea.height-imageViewRect.size.height)/2
89 |
90 | imageView.frame = imageViewRect
91 |
92 | let textRect = division.remainder.insetBy(dx: 0, dy: textPadding)
93 | let labelsDivider = textRect.divided(atDistance: textRect.height/2, from: .minYEdge)
94 |
95 | textLabel.frame = labelsDivider.slice
96 |
97 | let subtitleFrame = labelsDivider.remainder
98 |
99 | subtitleLabel.frame = subtitleFrame
100 | }
101 |
102 | // MARK: -
103 |
104 | func recolor() {
105 | if isSelected == true {
106 |
107 | textLabel.textColor = .white
108 | subtitleLabel.textColor = .white
109 | imageView.tintColor = .white
110 |
111 | if let selectedBackgroundView = selectedBackgroundView {
112 | selectedBackgroundView.alpha = 1
113 | selectedBackgroundView.backgroundColor = .systemBlue
114 | }
115 | }
116 | else {
117 | textLabel.textColor = .label
118 | subtitleLabel.textColor = .secondaryLabel
119 | imageView.tintColor = .systemBlue
120 |
121 | if let selectedBackgroundView = selectedBackgroundView {
122 | selectedBackgroundView.alpha = 0
123 | selectedBackgroundView.backgroundColor = .systemBlue
124 | }
125 | }
126 | }
127 |
128 | override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
129 | super.traitCollectionDidChange(previousTraitCollection)
130 |
131 | recolor()
132 | }
133 |
134 | // MARK: -
135 |
136 | override func didUpdateFocus(in context: UIFocusUpdateContext, with coordinator: UIFocusAnimationCoordinator) {
137 |
138 | super.didUpdateFocus(in: context, with: coordinator)
139 |
140 | if context.nextFocusedItem === self {
141 | isHighlighted = true
142 | } else if context.previouslyFocusedItem === self {
143 | isHighlighted = false
144 | }
145 | }
146 |
147 | }
148 |
--------------------------------------------------------------------------------
/CommandBar/Source/Main/CMBCommandBarResultsViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CMBCommandBarResultsViewController.swift
3 | // CommandBar
4 | //
5 | // Created by Steven Troughton-Smith on 29/12/2021.
6 | //
7 |
8 | import UIKit
9 | import Foundation
10 |
11 | class CMBCommandBarResultsViewController: UICollectionViewController {
12 |
13 | enum CMBCommandBarSection {
14 | case main
15 | }
16 |
17 | struct CMBCommandBarItem: Hashable {
18 | var title: String = ""
19 |
20 | func hash(into hasher: inout Hasher) {
21 | hasher.combine(identifier)
22 | }
23 | static func == (lhs: CMBCommandBarItem, rhs: CMBCommandBarItem) -> Bool {
24 | return lhs.identifier == rhs.identifier
25 | }
26 | private let identifier = UUID()
27 | }
28 |
29 | var dataSource: UICollectionViewDiffableDataSource! = nil
30 |
31 | init() {
32 |
33 | let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1),
34 | heightDimension: .absolute(UIFloat(60)))
35 | let item = NSCollectionLayoutItem(layoutSize: itemSize)
36 |
37 | let group = NSCollectionLayoutGroup.vertical(layoutSize: itemSize,
38 | subitems: [item])
39 |
40 | let section = NSCollectionLayoutSection(group: group)
41 | let layout = UICollectionViewCompositionalLayout(section: section)
42 |
43 | super.init(collectionViewLayout: layout)
44 |
45 | collectionView.backgroundColor = .clear
46 | collectionView.isOpaque = false
47 | collectionView.selectionFollowsFocus = true
48 |
49 | configureDataSource()
50 |
51 | NotificationCenter.default.addObserver(forName: .upArrowPressedNotification, object: nil, queue: nil) { _ in
52 | self.goUp(nil)
53 | }
54 |
55 | NotificationCenter.default.addObserver(forName: .downArrowPressedNotification, object: nil, queue: nil) { _ in
56 | self.goDown(nil)
57 | }
58 |
59 | NotificationCenter.default.addObserver(forName: .returnPressedNotification, object: nil, queue: nil) { _ in
60 | self.actuateSelection(nil)
61 | }
62 | }
63 |
64 | required init?(coder: NSCoder) {
65 | fatalError("init(coder:) has not been implemented")
66 | }
67 |
68 | // MARK: Data Source -
69 |
70 | func configureDataSource() {
71 |
72 | let cellRegistration = UICollectionView.CellRegistration { cell, indexPath, menuItem in
73 |
74 | cell.imageView.image = UIImage(named:"swiftdoc")
75 | cell.textLabel.text = menuItem.title
76 | cell.subtitleLabel.text = "/path/to/thing"
77 | }
78 |
79 | dataSource = UICollectionViewDiffableDataSource(collectionView: collectionView) {
80 | (collectionView: UICollectionView, indexPath: IndexPath, item: CMBCommandBarItem) -> UICollectionViewCell? in
81 |
82 | return collectionView.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item: item)
83 | }
84 |
85 | collectionView.dataSource = dataSource
86 |
87 | refresh()
88 | }
89 |
90 | private lazy var menuItems: [CMBCommandBarItem] = {
91 | return [
92 | CMBCommandBarItem(title: "MyApp.swift"),
93 | CMBCommandBarItem(title: "MyScene.swift"),
94 | CMBCommandBarItem(title: "MyView.swift"),
95 | ]
96 | }()
97 |
98 | func initialSnapshot(forItems:[CMBCommandBarItem]) -> NSDiffableDataSourceSectionSnapshot {
99 | var snapshot = NSDiffableDataSourceSectionSnapshot()
100 |
101 | snapshot.append(forItems, to: nil)
102 | snapshot.expand(forItems)
103 |
104 | return snapshot
105 | }
106 |
107 | func refresh() {
108 | guard let dataSource = collectionView.dataSource as? UICollectionViewDiffableDataSource else { return }
109 |
110 | dataSource.apply(initialSnapshot(forItems: menuItems), to: .main, animatingDifferences: false)
111 | collectionView.selectItem(at: IndexPath(item: 0, section: 0), animated: false, scrollPosition: [])
112 | }
113 |
114 | // MARK: - Input Navigation
115 |
116 | @objc func actuateSelection(_ sender:Any?) {
117 | guard let item = collectionView.indexPathsForSelectedItems?.first else { return }
118 |
119 | NSLog("Actuated item \(item)")
120 | }
121 |
122 | @objc func goUp(_ sender:Any?) {
123 | var current = IndexPath(item:0, section:0)
124 |
125 | if let item = collectionView.indexPathsForSelectedItems?.first {
126 | current = item
127 | }
128 |
129 | if current.item - 1 >= 0 {
130 | collectionView.selectItem(at: IndexPath(item: current.item - 1, section: 0), animated: false, scrollPosition: [])
131 | }
132 | }
133 |
134 | @objc func goDown(_ sender:Any?) {
135 | let max = collectionView.numberOfItems(inSection: 0)
136 | var current = IndexPath(item:0, section:0)
137 |
138 | if let item = collectionView.indexPathsForSelectedItems?.first {
139 | current = item
140 | }
141 |
142 | if current.item + 1 < max {
143 | collectionView.selectItem(at: IndexPath(item: current.item + 1, section: 0), animated: false, scrollPosition: [])
144 | }
145 | }
146 |
147 | // MARK: -
148 |
149 | deinit {
150 | NotificationCenter.default.removeObserver(self, name: .upArrowPressedNotification, object:nil)
151 | NotificationCenter.default.removeObserver(self, name: .downArrowPressedNotification, object:nil)
152 | NotificationCenter.default.removeObserver(self, name: .returnPressedNotification, object:nil)
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/CommandBar.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 55;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | B0175206277BF8CA005D4134 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B0175205277BF8CA005D4134 /* Assets.xcassets */; };
11 | B017520A277BF8CA005D4134 /* CMBAppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0175209277BF8CA005D4134 /* CMBAppDelegate.swift */; };
12 | B017520C277BF8CA005D4134 /* CMBSceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B017520B277BF8CA005D4134 /* CMBSceneDelegate.swift */; };
13 | B017520E277BF8CA005D4134 /* CMBSceneDelegate+NSToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = B017520D277BF8CA005D4134 /* CMBSceneDelegate+NSToolbar.swift */; };
14 | B0175211277BF8CA005D4134 /* CMBMainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0175210277BF8CA005D4134 /* CMBMainViewController.swift */; };
15 | B0175214277BF8CA005D4134 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0175212277BF8CA005D4134 /* LaunchScreen.storyboard */; };
16 | B017521C277BF8E6005D4134 /* CMBCommandBarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B017521B277BF8E6005D4134 /* CMBCommandBarViewController.swift */; };
17 | B017521E277BF994005D4134 /* Number+Scaling.swift in Sources */ = {isa = PBXBuildFile; fileRef = B017521D277BF994005D4134 /* Number+Scaling.swift */; };
18 | B0175221277C03BC005D4134 /* CMBCommandBarResultsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0175220277C03BC005D4134 /* CMBCommandBarResultsViewController.swift */; };
19 | B0175224277C1119005D4134 /* CMBCommandBarResultsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0175223277C1119005D4134 /* CMBCommandBarResultsCell.swift */; };
20 | /* End PBXBuildFile section */
21 |
22 | /* Begin PBXFileReference section */
23 | B0175202277BF8CA005D4134 /* CommandBar.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CommandBar.app; sourceTree = BUILT_PRODUCTS_DIR; };
24 | B0175205277BF8CA005D4134 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
25 | B0175209277BF8CA005D4134 /* CMBAppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CMBAppDelegate.swift; sourceTree = ""; };
26 | B017520B277BF8CA005D4134 /* CMBSceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CMBSceneDelegate.swift; sourceTree = ""; };
27 | B017520D277BF8CA005D4134 /* CMBSceneDelegate+NSToolbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CMBSceneDelegate+NSToolbar.swift"; sourceTree = ""; };
28 | B0175210277BF8CA005D4134 /* CMBMainViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CMBMainViewController.swift; sourceTree = ""; };
29 | B0175213277BF8CA005D4134 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
30 | B0175215277BF8CA005D4134 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
31 | B017521B277BF8E6005D4134 /* CMBCommandBarViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CMBCommandBarViewController.swift; sourceTree = ""; };
32 | B017521D277BF994005D4134 /* Number+Scaling.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Number+Scaling.swift"; sourceTree = ""; };
33 | B0175220277C03BC005D4134 /* CMBCommandBarResultsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CMBCommandBarResultsViewController.swift; sourceTree = ""; };
34 | B0175223277C1119005D4134 /* CMBCommandBarResultsCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CMBCommandBarResultsCell.swift; sourceTree = ""; };
35 | B0175225277C2230005D4134 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; };
36 | /* End PBXFileReference section */
37 |
38 | /* Begin PBXFrameworksBuildPhase section */
39 | B01751FF277BF8CA005D4134 /* Frameworks */ = {
40 | isa = PBXFrameworksBuildPhase;
41 | buildActionMask = 2147483647;
42 | files = (
43 | );
44 | runOnlyForDeploymentPostprocessing = 0;
45 | };
46 | /* End PBXFrameworksBuildPhase section */
47 |
48 | /* Begin PBXGroup section */
49 | B01751F9277BF8CA005D4134 = {
50 | isa = PBXGroup;
51 | children = (
52 | B0175225277C2230005D4134 /* README.md */,
53 | B0175204277BF8CA005D4134 /* CommandBar */,
54 | B0175203277BF8CA005D4134 /* Products */,
55 | );
56 | sourceTree = "";
57 | };
58 | B0175203277BF8CA005D4134 /* Products */ = {
59 | isa = PBXGroup;
60 | children = (
61 | B0175202277BF8CA005D4134 /* CommandBar.app */,
62 | );
63 | name = Products;
64 | sourceTree = "";
65 | };
66 | B0175204277BF8CA005D4134 /* CommandBar */ = {
67 | isa = PBXGroup;
68 | children = (
69 | B0175205277BF8CA005D4134 /* Assets.xcassets */,
70 | B0175212277BF8CA005D4134 /* LaunchScreen.storyboard */,
71 | B0175215277BF8CA005D4134 /* Info.plist */,
72 | B0175207277BF8CA005D4134 /* Source */,
73 | );
74 | path = CommandBar;
75 | sourceTree = "";
76 | };
77 | B0175207277BF8CA005D4134 /* Source */ = {
78 | isa = PBXGroup;
79 | children = (
80 | B017521D277BF994005D4134 /* Number+Scaling.swift */,
81 | B0175208277BF8CA005D4134 /* Application */,
82 | B017520F277BF8CA005D4134 /* Main */,
83 | );
84 | path = Source;
85 | sourceTree = "";
86 | };
87 | B0175208277BF8CA005D4134 /* Application */ = {
88 | isa = PBXGroup;
89 | children = (
90 | B0175209277BF8CA005D4134 /* CMBAppDelegate.swift */,
91 | B017520B277BF8CA005D4134 /* CMBSceneDelegate.swift */,
92 | B017520D277BF8CA005D4134 /* CMBSceneDelegate+NSToolbar.swift */,
93 | );
94 | path = Application;
95 | sourceTree = "";
96 | };
97 | B017520F277BF8CA005D4134 /* Main */ = {
98 | isa = PBXGroup;
99 | children = (
100 | B0175210277BF8CA005D4134 /* CMBMainViewController.swift */,
101 | B017521B277BF8E6005D4134 /* CMBCommandBarViewController.swift */,
102 | B0175220277C03BC005D4134 /* CMBCommandBarResultsViewController.swift */,
103 | B0175223277C1119005D4134 /* CMBCommandBarResultsCell.swift */,
104 | );
105 | path = Main;
106 | sourceTree = "";
107 | };
108 | /* End PBXGroup section */
109 |
110 | /* Begin PBXNativeTarget section */
111 | B0175201277BF8CA005D4134 /* CommandBar */ = {
112 | isa = PBXNativeTarget;
113 | buildConfigurationList = B0175218277BF8CA005D4134 /* Build configuration list for PBXNativeTarget "CommandBar" */;
114 | buildPhases = (
115 | B01751FE277BF8CA005D4134 /* Sources */,
116 | B01751FF277BF8CA005D4134 /* Frameworks */,
117 | B0175200277BF8CA005D4134 /* Resources */,
118 | );
119 | buildRules = (
120 | );
121 | dependencies = (
122 | );
123 | name = CommandBar;
124 | productName = CommandBar;
125 | productReference = B0175202277BF8CA005D4134 /* CommandBar.app */;
126 | productType = "com.apple.product-type.application";
127 | };
128 | /* End PBXNativeTarget section */
129 |
130 | /* Begin PBXProject section */
131 | B01751FA277BF8CA005D4134 /* Project object */ = {
132 | isa = PBXProject;
133 | attributes = {
134 | BuildIndependentTargetsInParallel = 1;
135 | CLASSPREFIX = CMB;
136 | LastSwiftUpdateCheck = 1320;
137 | LastUpgradeCheck = 1320;
138 | TargetAttributes = {
139 | B0175201277BF8CA005D4134 = {
140 | CreatedOnToolsVersion = 13.2;
141 | };
142 | };
143 | };
144 | buildConfigurationList = B01751FD277BF8CA005D4134 /* Build configuration list for PBXProject "CommandBar" */;
145 | compatibilityVersion = "Xcode 13.0";
146 | developmentRegion = en;
147 | hasScannedForEncodings = 0;
148 | knownRegions = (
149 | en,
150 | Base,
151 | );
152 | mainGroup = B01751F9277BF8CA005D4134;
153 | productRefGroup = B0175203277BF8CA005D4134 /* Products */;
154 | projectDirPath = "";
155 | projectRoot = "";
156 | targets = (
157 | B0175201277BF8CA005D4134 /* CommandBar */,
158 | );
159 | };
160 | /* End PBXProject section */
161 |
162 | /* Begin PBXResourcesBuildPhase section */
163 | B0175200277BF8CA005D4134 /* Resources */ = {
164 | isa = PBXResourcesBuildPhase;
165 | buildActionMask = 2147483647;
166 | files = (
167 | B0175206277BF8CA005D4134 /* Assets.xcassets in Resources */,
168 | B0175214277BF8CA005D4134 /* LaunchScreen.storyboard in Resources */,
169 | );
170 | runOnlyForDeploymentPostprocessing = 0;
171 | };
172 | /* End PBXResourcesBuildPhase section */
173 |
174 | /* Begin PBXSourcesBuildPhase section */
175 | B01751FE277BF8CA005D4134 /* Sources */ = {
176 | isa = PBXSourcesBuildPhase;
177 | buildActionMask = 2147483647;
178 | files = (
179 | B0175221277C03BC005D4134 /* CMBCommandBarResultsViewController.swift in Sources */,
180 | B0175211277BF8CA005D4134 /* CMBMainViewController.swift in Sources */,
181 | B017521C277BF8E6005D4134 /* CMBCommandBarViewController.swift in Sources */,
182 | B017520C277BF8CA005D4134 /* CMBSceneDelegate.swift in Sources */,
183 | B017520E277BF8CA005D4134 /* CMBSceneDelegate+NSToolbar.swift in Sources */,
184 | B0175224277C1119005D4134 /* CMBCommandBarResultsCell.swift in Sources */,
185 | B017521E277BF994005D4134 /* Number+Scaling.swift in Sources */,
186 | B017520A277BF8CA005D4134 /* CMBAppDelegate.swift in Sources */,
187 | );
188 | runOnlyForDeploymentPostprocessing = 0;
189 | };
190 | /* End PBXSourcesBuildPhase section */
191 |
192 | /* Begin PBXVariantGroup section */
193 | B0175212277BF8CA005D4134 /* LaunchScreen.storyboard */ = {
194 | isa = PBXVariantGroup;
195 | children = (
196 | B0175213277BF8CA005D4134 /* Base */,
197 | );
198 | name = LaunchScreen.storyboard;
199 | sourceTree = "";
200 | };
201 | /* End PBXVariantGroup section */
202 |
203 | /* Begin XCBuildConfiguration section */
204 | B0175216277BF8CA005D4134 /* Debug */ = {
205 | isa = XCBuildConfiguration;
206 | buildSettings = {
207 | ALWAYS_SEARCH_USER_PATHS = NO;
208 | CLANG_ANALYZER_NONNULL = YES;
209 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
210 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
211 | CLANG_CXX_LIBRARY = "libc++";
212 | CLANG_ENABLE_MODULES = YES;
213 | CLANG_ENABLE_OBJC_ARC = YES;
214 | CLANG_ENABLE_OBJC_WEAK = YES;
215 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
216 | CLANG_WARN_BOOL_CONVERSION = YES;
217 | CLANG_WARN_COMMA = YES;
218 | CLANG_WARN_CONSTANT_CONVERSION = YES;
219 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
220 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
221 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
222 | CLANG_WARN_EMPTY_BODY = YES;
223 | CLANG_WARN_ENUM_CONVERSION = YES;
224 | CLANG_WARN_INFINITE_RECURSION = YES;
225 | CLANG_WARN_INT_CONVERSION = YES;
226 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
227 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
228 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
229 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
230 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
231 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
232 | CLANG_WARN_STRICT_PROTOTYPES = YES;
233 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
234 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
235 | CLANG_WARN_UNREACHABLE_CODE = YES;
236 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
237 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
238 | COPY_PHASE_STRIP = NO;
239 | DEBUG_INFORMATION_FORMAT = dwarf;
240 | ENABLE_STRICT_OBJC_MSGSEND = YES;
241 | ENABLE_TESTABILITY = YES;
242 | GCC_C_LANGUAGE_STANDARD = gnu11;
243 | GCC_DYNAMIC_NO_PIC = NO;
244 | GCC_NO_COMMON_BLOCKS = YES;
245 | GCC_OPTIMIZATION_LEVEL = 0;
246 | GCC_PREPROCESSOR_DEFINITIONS = (
247 | "DEBUG=1",
248 | "$(inherited)",
249 | );
250 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
251 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
252 | GCC_WARN_UNDECLARED_SELECTOR = YES;
253 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
254 | GCC_WARN_UNUSED_FUNCTION = YES;
255 | GCC_WARN_UNUSED_VARIABLE = YES;
256 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
257 | MTL_FAST_MATH = YES;
258 | ONLY_ACTIVE_ARCH = YES;
259 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
260 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
261 | SWIFT_VERSION = 5.0;
262 | };
263 | name = Debug;
264 | };
265 | B0175217277BF8CA005D4134 /* Release */ = {
266 | isa = XCBuildConfiguration;
267 | buildSettings = {
268 | ALWAYS_SEARCH_USER_PATHS = NO;
269 | CLANG_ANALYZER_NONNULL = YES;
270 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
271 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
272 | CLANG_CXX_LIBRARY = "libc++";
273 | CLANG_ENABLE_MODULES = YES;
274 | CLANG_ENABLE_OBJC_ARC = YES;
275 | CLANG_ENABLE_OBJC_WEAK = YES;
276 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
277 | CLANG_WARN_BOOL_CONVERSION = YES;
278 | CLANG_WARN_COMMA = YES;
279 | CLANG_WARN_CONSTANT_CONVERSION = YES;
280 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
281 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
282 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
283 | CLANG_WARN_EMPTY_BODY = YES;
284 | CLANG_WARN_ENUM_CONVERSION = YES;
285 | CLANG_WARN_INFINITE_RECURSION = YES;
286 | CLANG_WARN_INT_CONVERSION = YES;
287 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
288 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
289 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
290 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
291 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
292 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
293 | CLANG_WARN_STRICT_PROTOTYPES = YES;
294 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
295 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
296 | CLANG_WARN_UNREACHABLE_CODE = YES;
297 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
298 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
299 | COPY_PHASE_STRIP = NO;
300 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
301 | ENABLE_NS_ASSERTIONS = NO;
302 | ENABLE_STRICT_OBJC_MSGSEND = YES;
303 | GCC_C_LANGUAGE_STANDARD = gnu11;
304 | GCC_NO_COMMON_BLOCKS = YES;
305 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
306 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
307 | GCC_WARN_UNDECLARED_SELECTOR = YES;
308 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
309 | GCC_WARN_UNUSED_FUNCTION = YES;
310 | GCC_WARN_UNUSED_VARIABLE = YES;
311 | MTL_ENABLE_DEBUG_INFO = NO;
312 | MTL_FAST_MATH = YES;
313 | SWIFT_COMPILATION_MODE = wholemodule;
314 | SWIFT_OPTIMIZATION_LEVEL = "-O";
315 | SWIFT_VERSION = 5.0;
316 | };
317 | name = Release;
318 | };
319 | B0175219277BF8CA005D4134 /* Debug */ = {
320 | isa = XCBuildConfiguration;
321 | buildSettings = {
322 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
323 | CLANG_STATIC_ANALYZER_MODE = deep;
324 | CODE_SIGN_IDENTITY = "iPhone Developer";
325 | CODE_SIGN_STYLE = Automatic;
326 | CURRENT_PROJECT_VERSION = 1;
327 | DEVELOPMENT_TEAM = 2ZDN69KUUV;
328 | GCC_TREAT_WARNINGS_AS_ERRORS = deep;
329 | GCC_WARN_PEDANTIC = deep;
330 | GENERATE_INFOPLIST_FILE = YES;
331 | INFOPLIST_FILE = "$(PRODUCT_NAME)/Info.plist";
332 | IPHONEOS_DEPLOYMENT_TARGET = 15.0;
333 | "IPHONEOS_DEPLOYMENT_TARGET[sdk=macosx*]" = 15.0;
334 | LD_RUNPATH_SEARCH_PATHS = (
335 | "$(inherited)",
336 | "@executable_path/Frameworks",
337 | );
338 | MARKETING_VERSION = 1.0;
339 | PRODUCT_BUNDLE_IDENTIFIER = com.highcaffeinecontent.CommandBar;
340 | PRODUCT_NAME = CommandBar;
341 | RUN_CLANG_STATIC_ANALYZER = YES;
342 | SDKROOT = iphoneos;
343 | SUPPORTS_MACCATALYST = YES;
344 | SWIFT_EMIT_LOC_STRINGS = YES;
345 | SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
346 | SWIFT_VERSION = 5.0;
347 | TARGETED_DEVICE_FAMILY = "1,2,6";
348 | };
349 | name = Debug;
350 | };
351 | B017521A277BF8CA005D4134 /* Release */ = {
352 | isa = XCBuildConfiguration;
353 | buildSettings = {
354 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
355 | CLANG_STATIC_ANALYZER_MODE = deep;
356 | CODE_SIGN_IDENTITY = "iPhone Developer";
357 | CODE_SIGN_STYLE = Automatic;
358 | CURRENT_PROJECT_VERSION = 1;
359 | DEVELOPMENT_TEAM = 2ZDN69KUUV;
360 | GCC_TREAT_WARNINGS_AS_ERRORS = deep;
361 | GCC_WARN_PEDANTIC = deep;
362 | GENERATE_INFOPLIST_FILE = YES;
363 | INFOPLIST_FILE = "$(PRODUCT_NAME)/Info.plist";
364 | IPHONEOS_DEPLOYMENT_TARGET = 15.0;
365 | "IPHONEOS_DEPLOYMENT_TARGET[sdk=macosx*]" = 15.0;
366 | LD_RUNPATH_SEARCH_PATHS = (
367 | "$(inherited)",
368 | "@executable_path/Frameworks",
369 | );
370 | MARKETING_VERSION = 1.0;
371 | PRODUCT_BUNDLE_IDENTIFIER = com.highcaffeinecontent.CommandBar;
372 | PRODUCT_NAME = CommandBar;
373 | RUN_CLANG_STATIC_ANALYZER = YES;
374 | SDKROOT = iphoneos;
375 | SUPPORTS_MACCATALYST = YES;
376 | SWIFT_EMIT_LOC_STRINGS = YES;
377 | SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
378 | SWIFT_VERSION = 5.0;
379 | TARGETED_DEVICE_FAMILY = "1,2,6";
380 | VALIDATE_PRODUCT = YES;
381 | };
382 | name = Release;
383 | };
384 | /* End XCBuildConfiguration section */
385 |
386 | /* Begin XCConfigurationList section */
387 | B01751FD277BF8CA005D4134 /* Build configuration list for PBXProject "CommandBar" */ = {
388 | isa = XCConfigurationList;
389 | buildConfigurations = (
390 | B0175216277BF8CA005D4134 /* Debug */,
391 | B0175217277BF8CA005D4134 /* Release */,
392 | );
393 | defaultConfigurationIsVisible = 0;
394 | defaultConfigurationName = Release;
395 | };
396 | B0175218277BF8CA005D4134 /* Build configuration list for PBXNativeTarget "CommandBar" */ = {
397 | isa = XCConfigurationList;
398 | buildConfigurations = (
399 | B0175219277BF8CA005D4134 /* Debug */,
400 | B017521A277BF8CA005D4134 /* Release */,
401 | );
402 | defaultConfigurationIsVisible = 0;
403 | defaultConfigurationName = Release;
404 | };
405 | /* End XCConfigurationList section */
406 | };
407 | rootObject = B01751FA277BF8CA005D4134 /* Project object */;
408 | }
409 |
--------------------------------------------------------------------------------