├── .gitignore ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── Makefile ├── Podfile ├── Podfile.lock ├── README.md ├── UIKitForMacPlayground.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings └── xcuserdata │ └── noahgilmore.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist ├── UIKitForMacPlayground.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ ├── IDEWorkspaceChecks.plist │ └── WorkspaceSettings.xcsettings ├── UIKitForMacPlayground ├── AppDelegate.swift ├── AppKitBridge.swift ├── AppKitBundleLoader.h ├── AppKitBundleLoader.m ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json ├── Base.lproj │ └── LaunchScreen.storyboard ├── ColorView.swift ├── ContextMenuViewController.swift ├── CursorsViewController.swift ├── DetailType.swift ├── DragAndDropViewController.swift ├── DragExampleView.swift ├── DropExampleView.swift ├── ExamplesViewController.swift ├── HashableIdGenerator.swift ├── HeaderView.swift ├── HoverExampleView.swift ├── HoverViewController.swift ├── Info.plist ├── ListViewController.swift ├── NotificationsViewController.swift ├── OpenFileViewController.swift ├── SceneDelegate.swift ├── SquareSceneDelegate.swift ├── SwiftUIView.swift ├── TouchBarViewController.swift ├── UIKitForMacPlayground-Bridging-Header.h ├── UIKitForMacPlayground.entitlements ├── UserActivities.swift ├── ViewController.swift └── WindowsViewController.swift └── UIKitForMacPlaygroundSupportingBundle ├── AppKitPrincipal.swift ├── Info.plist └── UIKitForMacPlaygroundSupportingBundle-Bridging-Header.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | vendor/ 5 | .bundle/ 6 | Pods/ 7 | xcuserdata/ 8 | 9 | ## Build generated 10 | build/ 11 | DerivedData/ 12 | 13 | ## Various settings 14 | *.pbxuser 15 | !default.pbxuser 16 | *.mode1v3 17 | !default.mode1v3 18 | *.mode2v3 19 | !default.mode2v3 20 | *.perspectivev3 21 | !default.perspectivev3 22 | xcuserdata/ 23 | 24 | ## Other 25 | *.moved-aside 26 | *.xccheckout 27 | *.xcscmblueprint 28 | 29 | ## Obj-C/Swift specific 30 | *.hmap 31 | *.ipa 32 | *.dSYM.zip 33 | *.dSYM 34 | 35 | ## Playgrounds 36 | timeline.xctimeline 37 | playground.xcworkspace 38 | 39 | # Swift Package Manager 40 | # 41 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 42 | # Packages/ 43 | # Package.pins 44 | # Package.resolved 45 | .build/ 46 | 47 | # CocoaPods 48 | # 49 | # We recommend against adding the Pods directory to your .gitignore. However 50 | # you should judge for yourself, the pros and cons are mentioned at: 51 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 52 | # 53 | # Pods/ 54 | 55 | # Carthage 56 | # 57 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 58 | # Carthage/Checkouts 59 | 60 | Carthage/Build 61 | 62 | # fastlane 63 | # 64 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 65 | # screenshots whenever they are needed. 66 | # For more information about the recommended setup visit: 67 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 68 | 69 | fastlane/report.xml 70 | fastlane/Preview.html 71 | fastlane/screenshots/**/*.png 72 | fastlane/test_output 73 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'cocoapods', '~> 1.7' 4 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | CFPropertyList (3.0.0) 5 | activesupport (4.2.11.1) 6 | i18n (~> 0.7) 7 | minitest (~> 5.1) 8 | thread_safe (~> 0.3, >= 0.3.4) 9 | tzinfo (~> 1.1) 10 | atomos (0.1.3) 11 | claide (1.0.2) 12 | cocoapods (1.7.3) 13 | activesupport (>= 4.0.2, < 5) 14 | claide (>= 1.0.2, < 2.0) 15 | cocoapods-core (= 1.7.3) 16 | cocoapods-deintegrate (>= 1.0.3, < 2.0) 17 | cocoapods-downloader (>= 1.2.2, < 2.0) 18 | cocoapods-plugins (>= 1.0.0, < 2.0) 19 | cocoapods-search (>= 1.0.0, < 2.0) 20 | cocoapods-stats (>= 1.0.0, < 2.0) 21 | cocoapods-trunk (>= 1.3.1, < 2.0) 22 | cocoapods-try (>= 1.1.0, < 2.0) 23 | colored2 (~> 3.1) 24 | escape (~> 0.0.4) 25 | fourflusher (>= 2.3.0, < 3.0) 26 | gh_inspector (~> 1.0) 27 | molinillo (~> 0.6.6) 28 | nap (~> 1.0) 29 | ruby-macho (~> 1.4) 30 | xcodeproj (>= 1.10.0, < 2.0) 31 | cocoapods-core (1.7.3) 32 | activesupport (>= 4.0.2, < 6) 33 | fuzzy_match (~> 2.0.4) 34 | nap (~> 1.0) 35 | cocoapods-deintegrate (1.0.4) 36 | cocoapods-downloader (1.2.2) 37 | cocoapods-plugins (1.0.0) 38 | nap 39 | cocoapods-search (1.0.0) 40 | cocoapods-stats (1.1.0) 41 | cocoapods-trunk (1.3.1) 42 | nap (>= 0.8, < 2.0) 43 | netrc (~> 0.11) 44 | cocoapods-try (1.1.0) 45 | colored2 (3.1.2) 46 | concurrent-ruby (1.1.5) 47 | escape (0.0.4) 48 | fourflusher (2.3.1) 49 | fuzzy_match (2.0.4) 50 | gh_inspector (1.1.3) 51 | i18n (0.9.5) 52 | concurrent-ruby (~> 1.0) 53 | minitest (5.11.3) 54 | molinillo (0.6.6) 55 | nanaimo (0.2.6) 56 | nap (1.1.0) 57 | netrc (0.11.0) 58 | ruby-macho (1.4.0) 59 | thread_safe (0.3.6) 60 | tzinfo (1.2.5) 61 | thread_safe (~> 0.1) 62 | xcodeproj (1.10.0) 63 | CFPropertyList (>= 2.3.3, < 4.0) 64 | atomos (~> 0.1.3) 65 | claide (>= 1.0.2, < 2.0) 66 | colored2 (~> 3.1) 67 | nanaimo (~> 0.2.6) 68 | 69 | PLATFORMS 70 | ruby 71 | 72 | DEPENDENCIES 73 | cocoapods (~> 1.7) 74 | 75 | BUNDLED WITH 76 | 1.15.1 77 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Noah Gilmore 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: bundle 2 | bundle: 3 | bundle install --path vendor/bundle 4 | 5 | .PHONY: pods 6 | pods: bundle 7 | bundle exec pod install 8 | 9 | .PHONY: pods-update 10 | pods-update: 11 | bundle exec pod install --repo-update 12 | 13 | .PHONY: clean 14 | clean: 15 | rm -rf Pods 16 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment the next line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | target 'UIKitForMacPlayground' do 5 | # Comment the next line if you're not using Swift and don't want to use dynamic frameworks 6 | use_frameworks! 7 | end 8 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODFILE CHECKSUM: e62e42da1da543cf83b59d8b7d84ce443aaa52dc 2 | 3 | COCOAPODS: 1.7.3 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Catalyst Playground 2 | An example project demonstrating Mac Catalyst features 3 | 4 | Screen Shot 2019-07-24 at 3 08 22 PM 5 | 6 | ## Running 7 | To run the project, clone and cd into the repo, then: 8 | 9 | ``` 10 | bundle install --path vendor/bundle 11 | bundle exec pod install 12 | xed . 13 | ``` 14 | 15 | Then click the run button! 16 | 17 | (Note that the project compiles on Mac and on iPad, but some features like the toolbar and appkit bundle bridging are not available on iPad) 18 | -------------------------------------------------------------------------------- /UIKitForMacPlayground.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 78646C4D22E87D2200B29FE7 /* (null) in Sources */ = {isa = PBXBuildFile; }; 11 | 78646C4E22E87D2200B29FE7 /* (null) in Sources */ = {isa = PBXBuildFile; }; 12 | A1147A2322F030C8003DC870 /* UIKitForMacPlaygroundSupportingBundle.bundle in Embed PlugIns */ = {isa = PBXBuildFile; fileRef = A1D887B122DCC874007C73ED /* UIKitForMacPlaygroundSupportingBundle.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 13 | A1147A2522F03713003DC870 /* NotificationsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1147A2422F03713003DC870 /* NotificationsViewController.swift */; }; 14 | A1147A2922F0386B003DC870 /* CursorsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1147A2722F0386B003DC870 /* CursorsViewController.swift */; }; 15 | A1147A2D22F08F6D003DC870 /* SwiftUIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1147A2C22F08F6C003DC870 /* SwiftUIView.swift */; }; 16 | A116117322D003B000251B87 /* WindowsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A116117222D003B000251B87 /* WindowsViewController.swift */; }; 17 | A116368B22C520AD0020072C /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A116368A22C520AD0020072C /* AppDelegate.swift */; }; 18 | A116368F22C520AD0020072C /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A116368E22C520AD0020072C /* ViewController.swift */; }; 19 | A116369422C520AF0020072C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A116369322C520AF0020072C /* Assets.xcassets */; }; 20 | A116369722C520AF0020072C /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A116369522C520AF0020072C /* LaunchScreen.storyboard */; }; 21 | A1A1A69322CB0D97003B81A7 /* ListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1A1A69222CB0D97003B81A7 /* ListViewController.swift */; }; 22 | A1A568FC22DFD4900045DF86 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A116368C22C520AD0020072C /* SceneDelegate.swift */; }; 23 | A1CDC1E222E98C7700B72002 /* OpenFileViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1CDC1E122E98C7700B72002 /* OpenFileViewController.swift */; }; 24 | A1D8877722DC3D3B007C73ED /* SquareSceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1D8877622DC3D3B007C73ED /* SquareSceneDelegate.swift */; }; 25 | A1D8879A22DCA178007C73ED /* HashableIdGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1D8879922DCA178007C73ED /* HashableIdGenerator.swift */; }; 26 | A1D8879C22DCA1AA007C73ED /* ExamplesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1D8879B22DCA1AA007C73ED /* ExamplesViewController.swift */; }; 27 | A1D8879E22DCA1F6007C73ED /* HeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1D8879D22DCA1F6007C73ED /* HeaderView.swift */; }; 28 | A1D887A022DCA20B007C73ED /* UserActivities.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1D8879F22DCA20B007C73ED /* UserActivities.swift */; }; 29 | A1D887A222DCA24E007C73ED /* HoverViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1D887A122DCA24E007C73ED /* HoverViewController.swift */; }; 30 | A1D887A422DCA275007C73ED /* HoverExampleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1D887A322DCA275007C73ED /* HoverExampleView.swift */; }; 31 | A1D887A622DCA299007C73ED /* ColorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1D887A522DCA299007C73ED /* ColorView.swift */; }; 32 | A1D887A822DCB108007C73ED /* DragAndDropViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1D887A722DCB108007C73ED /* DragAndDropViewController.swift */; }; 33 | A1D887AA22DCB167007C73ED /* DragExampleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1D887A922DCB167007C73ED /* DragExampleView.swift */; }; 34 | A1D887AC22DCB27F007C73ED /* DropExampleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1D887AB22DCB27F007C73ED /* DropExampleView.swift */; }; 35 | A1D887B922DCC9F3007C73ED /* AppKitPrincipal.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1D887B822DCC9F3007C73ED /* AppKitPrincipal.swift */; }; 36 | A1D887C522DD308C007C73ED /* AppKitBundleLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = A1D887C422DD308C007C73ED /* AppKitBundleLoader.m */; }; 37 | A1D887C722DE06EF007C73ED /* TouchBarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1D887C622DE06EF007C73ED /* TouchBarViewController.swift */; }; 38 | A1D887C922DE0D78007C73ED /* ContextMenuViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1D887C822DE0D78007C73ED /* ContextMenuViewController.swift */; }; 39 | A1D887CB22DE3670007C73ED /* DetailType.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1D887CA22DE3670007C73ED /* DetailType.swift */; }; 40 | A1D887CC22DE367E007C73ED /* DetailType.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1D887CA22DE3670007C73ED /* DetailType.swift */; }; 41 | B96C579B73CBD88DAF015B1D /* Pods_UIKitForMacPlayground.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 67B3C60BFC993E73C93CC8E0 /* Pods_UIKitForMacPlayground.framework */; }; 42 | /* End PBXBuildFile section */ 43 | 44 | /* Begin PBXContainerItemProxy section */ 45 | A1A568FE22DFD7020045DF86 /* PBXContainerItemProxy */ = { 46 | isa = PBXContainerItemProxy; 47 | containerPortal = A116367F22C520AD0020072C /* Project object */; 48 | proxyType = 1; 49 | remoteGlobalIDString = A1D887B022DCC874007C73ED; 50 | remoteInfo = UIKitForMacPlaygroundSupportingBundle; 51 | }; 52 | /* End PBXContainerItemProxy section */ 53 | 54 | /* Begin PBXCopyFilesBuildPhase section */ 55 | A1A5690022DFD7030045DF86 /* Embed PlugIns */ = { 56 | isa = PBXCopyFilesBuildPhase; 57 | buildActionMask = 12; 58 | dstPath = ""; 59 | dstSubfolderSpec = 13; 60 | files = ( 61 | A1147A2322F030C8003DC870 /* UIKitForMacPlaygroundSupportingBundle.bundle in Embed PlugIns */, 62 | ); 63 | name = "Embed PlugIns"; 64 | runOnlyForDeploymentPostprocessing = 0; 65 | }; 66 | /* End PBXCopyFilesBuildPhase section */ 67 | 68 | /* Begin PBXFileReference section */ 69 | 417486A30CE1D0BCFC3EB3F4 /* Pods-UIKitForMacPlayground.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-UIKitForMacPlayground.release.xcconfig"; path = "Target Support Files/Pods-UIKitForMacPlayground/Pods-UIKitForMacPlayground.release.xcconfig"; sourceTree = ""; }; 70 | 67B3C60BFC993E73C93CC8E0 /* Pods_UIKitForMacPlayground.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_UIKitForMacPlayground.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 71 | A1147A2422F03713003DC870 /* NotificationsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationsViewController.swift; sourceTree = ""; }; 72 | A1147A2722F0386B003DC870 /* CursorsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CursorsViewController.swift; sourceTree = ""; }; 73 | A1147A2C22F08F6C003DC870 /* SwiftUIView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftUIView.swift; sourceTree = ""; }; 74 | A116117222D003B000251B87 /* WindowsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowsViewController.swift; sourceTree = ""; }; 75 | A116368722C520AD0020072C /* UIKitForMacPlayground.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = UIKitForMacPlayground.app; sourceTree = BUILT_PRODUCTS_DIR; }; 76 | A116368A22C520AD0020072C /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 77 | A116368C22C520AD0020072C /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; 78 | A116368E22C520AD0020072C /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 79 | A116369322C520AF0020072C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 80 | A116369622C520AF0020072C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 81 | A116369822C520AF0020072C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 82 | A116369E22C520E00020072C /* UIKitForMacPlayground.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = UIKitForMacPlayground.entitlements; sourceTree = ""; }; 83 | A1A1A69222CB0D97003B81A7 /* ListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListViewController.swift; sourceTree = ""; }; 84 | A1CDC1E122E98C7700B72002 /* OpenFileViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenFileViewController.swift; sourceTree = ""; }; 85 | A1D8877622DC3D3B007C73ED /* SquareSceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SquareSceneDelegate.swift; sourceTree = ""; }; 86 | A1D8879922DCA178007C73ED /* HashableIdGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HashableIdGenerator.swift; sourceTree = ""; }; 87 | A1D8879B22DCA1AA007C73ED /* ExamplesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExamplesViewController.swift; sourceTree = ""; }; 88 | A1D8879D22DCA1F6007C73ED /* HeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeaderView.swift; sourceTree = ""; }; 89 | A1D8879F22DCA20B007C73ED /* UserActivities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserActivities.swift; sourceTree = ""; }; 90 | A1D887A122DCA24E007C73ED /* HoverViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HoverViewController.swift; sourceTree = ""; }; 91 | A1D887A322DCA275007C73ED /* HoverExampleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HoverExampleView.swift; sourceTree = ""; }; 92 | A1D887A522DCA299007C73ED /* ColorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorView.swift; sourceTree = ""; }; 93 | A1D887A722DCB108007C73ED /* DragAndDropViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DragAndDropViewController.swift; sourceTree = ""; }; 94 | A1D887A922DCB167007C73ED /* DragExampleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DragExampleView.swift; sourceTree = ""; }; 95 | A1D887AB22DCB27F007C73ED /* DropExampleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropExampleView.swift; sourceTree = ""; }; 96 | A1D887B122DCC874007C73ED /* UIKitForMacPlaygroundSupportingBundle.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UIKitForMacPlaygroundSupportingBundle.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; 97 | A1D887B322DCC874007C73ED /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 98 | A1D887B722DCC9F3007C73ED /* UIKitForMacPlaygroundSupportingBundle-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIKitForMacPlaygroundSupportingBundle-Bridging-Header.h"; sourceTree = ""; }; 99 | A1D887B822DCC9F3007C73ED /* AppKitPrincipal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppKitPrincipal.swift; sourceTree = ""; }; 100 | A1D887C222DD308C007C73ED /* UIKitForMacPlayground-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIKitForMacPlayground-Bridging-Header.h"; sourceTree = ""; }; 101 | A1D887C322DD308C007C73ED /* AppKitBundleLoader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppKitBundleLoader.h; sourceTree = ""; }; 102 | A1D887C422DD308C007C73ED /* AppKitBundleLoader.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppKitBundleLoader.m; sourceTree = ""; }; 103 | A1D887C622DE06EF007C73ED /* TouchBarViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TouchBarViewController.swift; sourceTree = ""; }; 104 | A1D887C822DE0D78007C73ED /* ContextMenuViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextMenuViewController.swift; sourceTree = ""; }; 105 | A1D887CA22DE3670007C73ED /* DetailType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailType.swift; sourceTree = ""; }; 106 | A793676ECB32AE0CCFB9C6EA /* Pods-UIKitForMacPlayground.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-UIKitForMacPlayground.debug.xcconfig"; path = "Target Support Files/Pods-UIKitForMacPlayground/Pods-UIKitForMacPlayground.debug.xcconfig"; sourceTree = ""; }; 107 | /* End PBXFileReference section */ 108 | 109 | /* Begin PBXFrameworksBuildPhase section */ 110 | A116368422C520AD0020072C /* Frameworks */ = { 111 | isa = PBXFrameworksBuildPhase; 112 | buildActionMask = 2147483647; 113 | files = ( 114 | B96C579B73CBD88DAF015B1D /* Pods_UIKitForMacPlayground.framework in Frameworks */, 115 | ); 116 | runOnlyForDeploymentPostprocessing = 0; 117 | }; 118 | A1D887AE22DCC874007C73ED /* Frameworks */ = { 119 | isa = PBXFrameworksBuildPhase; 120 | buildActionMask = 2147483647; 121 | files = ( 122 | ); 123 | runOnlyForDeploymentPostprocessing = 0; 124 | }; 125 | /* End PBXFrameworksBuildPhase section */ 126 | 127 | /* Begin PBXGroup section */ 128 | 12C4E70C679440A52759DFC1 /* Frameworks */ = { 129 | isa = PBXGroup; 130 | children = ( 131 | 67B3C60BFC993E73C93CC8E0 /* Pods_UIKitForMacPlayground.framework */, 132 | ); 133 | name = Frameworks; 134 | sourceTree = ""; 135 | }; 136 | 27FF9F9969858C90A708E2B4 /* Pods */ = { 137 | isa = PBXGroup; 138 | children = ( 139 | A793676ECB32AE0CCFB9C6EA /* Pods-UIKitForMacPlayground.debug.xcconfig */, 140 | 417486A30CE1D0BCFC3EB3F4 /* Pods-UIKitForMacPlayground.release.xcconfig */, 141 | ); 142 | path = Pods; 143 | sourceTree = ""; 144 | }; 145 | A116367E22C520AD0020072C = { 146 | isa = PBXGroup; 147 | children = ( 148 | A116368922C520AD0020072C /* UIKitForMacPlayground */, 149 | A1D887B222DCC874007C73ED /* UIKitForMacPlaygroundSupportingBundle */, 150 | A116368822C520AD0020072C /* Products */, 151 | 27FF9F9969858C90A708E2B4 /* Pods */, 152 | 12C4E70C679440A52759DFC1 /* Frameworks */, 153 | ); 154 | sourceTree = ""; 155 | }; 156 | A116368822C520AD0020072C /* Products */ = { 157 | isa = PBXGroup; 158 | children = ( 159 | A116368722C520AD0020072C /* UIKitForMacPlayground.app */, 160 | A1D887B122DCC874007C73ED /* UIKitForMacPlaygroundSupportingBundle.bundle */, 161 | ); 162 | name = Products; 163 | sourceTree = ""; 164 | }; 165 | A116368922C520AD0020072C /* UIKitForMacPlayground */ = { 166 | isa = PBXGroup; 167 | children = ( 168 | A1147A2C22F08F6C003DC870 /* SwiftUIView.swift */, 169 | A1147A2722F0386B003DC870 /* CursorsViewController.swift */, 170 | A116369E22C520E00020072C /* UIKitForMacPlayground.entitlements */, 171 | A116368A22C520AD0020072C /* AppDelegate.swift */, 172 | A116368C22C520AD0020072C /* SceneDelegate.swift */, 173 | A116368E22C520AD0020072C /* ViewController.swift */, 174 | A116369322C520AF0020072C /* Assets.xcassets */, 175 | A116369522C520AF0020072C /* LaunchScreen.storyboard */, 176 | A116369822C520AF0020072C /* Info.plist */, 177 | A1A1A69222CB0D97003B81A7 /* ListViewController.swift */, 178 | A116117222D003B000251B87 /* WindowsViewController.swift */, 179 | A1D8877622DC3D3B007C73ED /* SquareSceneDelegate.swift */, 180 | A1D8879922DCA178007C73ED /* HashableIdGenerator.swift */, 181 | A1D8879B22DCA1AA007C73ED /* ExamplesViewController.swift */, 182 | A1D8879D22DCA1F6007C73ED /* HeaderView.swift */, 183 | A1D8879F22DCA20B007C73ED /* UserActivities.swift */, 184 | A1D887A122DCA24E007C73ED /* HoverViewController.swift */, 185 | A1D887A322DCA275007C73ED /* HoverExampleView.swift */, 186 | A1D887A522DCA299007C73ED /* ColorView.swift */, 187 | A1D887A722DCB108007C73ED /* DragAndDropViewController.swift */, 188 | A1D887A922DCB167007C73ED /* DragExampleView.swift */, 189 | A1D887AB22DCB27F007C73ED /* DropExampleView.swift */, 190 | A1D887C322DD308C007C73ED /* AppKitBundleLoader.h */, 191 | A1D887C422DD308C007C73ED /* AppKitBundleLoader.m */, 192 | A1D887C222DD308C007C73ED /* UIKitForMacPlayground-Bridging-Header.h */, 193 | A1D887C622DE06EF007C73ED /* TouchBarViewController.swift */, 194 | A1D887C822DE0D78007C73ED /* ContextMenuViewController.swift */, 195 | A1D887CA22DE3670007C73ED /* DetailType.swift */, 196 | A1CDC1E122E98C7700B72002 /* OpenFileViewController.swift */, 197 | A1147A2422F03713003DC870 /* NotificationsViewController.swift */, 198 | ); 199 | path = UIKitForMacPlayground; 200 | sourceTree = ""; 201 | }; 202 | A1D887B222DCC874007C73ED /* UIKitForMacPlaygroundSupportingBundle */ = { 203 | isa = PBXGroup; 204 | children = ( 205 | A1D887B322DCC874007C73ED /* Info.plist */, 206 | A1D887B822DCC9F3007C73ED /* AppKitPrincipal.swift */, 207 | A1D887B722DCC9F3007C73ED /* UIKitForMacPlaygroundSupportingBundle-Bridging-Header.h */, 208 | ); 209 | path = UIKitForMacPlaygroundSupportingBundle; 210 | sourceTree = ""; 211 | }; 212 | /* End PBXGroup section */ 213 | 214 | /* Begin PBXNativeTarget section */ 215 | A116368622C520AD0020072C /* UIKitForMacPlayground */ = { 216 | isa = PBXNativeTarget; 217 | buildConfigurationList = A116369B22C520AF0020072C /* Build configuration list for PBXNativeTarget "UIKitForMacPlayground" */; 218 | buildPhases = ( 219 | 214D85965551D10ACF6D83E5 /* [CP] Check Pods Manifest.lock */, 220 | A116368322C520AD0020072C /* Sources */, 221 | A116368422C520AD0020072C /* Frameworks */, 222 | A116368522C520AD0020072C /* Resources */, 223 | A1A5690022DFD7030045DF86 /* Embed PlugIns */, 224 | ); 225 | buildRules = ( 226 | ); 227 | dependencies = ( 228 | A1A568FF22DFD7020045DF86 /* PBXTargetDependency */, 229 | ); 230 | name = UIKitForMacPlayground; 231 | productName = UIKitForMacPlayground; 232 | productReference = A116368722C520AD0020072C /* UIKitForMacPlayground.app */; 233 | productType = "com.apple.product-type.application"; 234 | }; 235 | A1D887B022DCC874007C73ED /* UIKitForMacPlaygroundSupportingBundle */ = { 236 | isa = PBXNativeTarget; 237 | buildConfigurationList = A1D887B422DCC874007C73ED /* Build configuration list for PBXNativeTarget "UIKitForMacPlaygroundSupportingBundle" */; 238 | buildPhases = ( 239 | A1D887AD22DCC874007C73ED /* Sources */, 240 | A1D887AE22DCC874007C73ED /* Frameworks */, 241 | A1D887AF22DCC874007C73ED /* Resources */, 242 | ); 243 | buildRules = ( 244 | ); 245 | dependencies = ( 246 | ); 247 | name = UIKitForMacPlaygroundSupportingBundle; 248 | productName = UIKitForMacPlaygroundSupportingBundle; 249 | productReference = A1D887B122DCC874007C73ED /* UIKitForMacPlaygroundSupportingBundle.bundle */; 250 | productType = "com.apple.product-type.bundle"; 251 | }; 252 | /* End PBXNativeTarget section */ 253 | 254 | /* Begin PBXProject section */ 255 | A116367F22C520AD0020072C /* Project object */ = { 256 | isa = PBXProject; 257 | attributes = { 258 | LastSwiftUpdateCheck = 1100; 259 | LastUpgradeCheck = 1100; 260 | ORGANIZATIONNAME = "Noah Gilmore"; 261 | TargetAttributes = { 262 | A116368622C520AD0020072C = { 263 | CreatedOnToolsVersion = 11.0; 264 | LastSwiftMigration = 1100; 265 | }; 266 | A1D887B022DCC874007C73ED = { 267 | CreatedOnToolsVersion = 11.0; 268 | LastSwiftMigration = 1100; 269 | }; 270 | }; 271 | }; 272 | buildConfigurationList = A116368222C520AD0020072C /* Build configuration list for PBXProject "UIKitForMacPlayground" */; 273 | compatibilityVersion = "Xcode 9.3"; 274 | developmentRegion = en; 275 | hasScannedForEncodings = 0; 276 | knownRegions = ( 277 | en, 278 | Base, 279 | ); 280 | mainGroup = A116367E22C520AD0020072C; 281 | productRefGroup = A116368822C520AD0020072C /* Products */; 282 | projectDirPath = ""; 283 | projectRoot = ""; 284 | targets = ( 285 | A116368622C520AD0020072C /* UIKitForMacPlayground */, 286 | A1D887B022DCC874007C73ED /* UIKitForMacPlaygroundSupportingBundle */, 287 | ); 288 | }; 289 | /* End PBXProject section */ 290 | 291 | /* Begin PBXResourcesBuildPhase section */ 292 | A116368522C520AD0020072C /* Resources */ = { 293 | isa = PBXResourcesBuildPhase; 294 | buildActionMask = 2147483647; 295 | files = ( 296 | A116369722C520AF0020072C /* LaunchScreen.storyboard in Resources */, 297 | A116369422C520AF0020072C /* Assets.xcassets in Resources */, 298 | ); 299 | runOnlyForDeploymentPostprocessing = 0; 300 | }; 301 | A1D887AF22DCC874007C73ED /* Resources */ = { 302 | isa = PBXResourcesBuildPhase; 303 | buildActionMask = 2147483647; 304 | files = ( 305 | ); 306 | runOnlyForDeploymentPostprocessing = 0; 307 | }; 308 | /* End PBXResourcesBuildPhase section */ 309 | 310 | /* Begin PBXShellScriptBuildPhase section */ 311 | 214D85965551D10ACF6D83E5 /* [CP] Check Pods Manifest.lock */ = { 312 | isa = PBXShellScriptBuildPhase; 313 | buildActionMask = 2147483647; 314 | files = ( 315 | ); 316 | inputFileListPaths = ( 317 | ); 318 | inputPaths = ( 319 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 320 | "${PODS_ROOT}/Manifest.lock", 321 | ); 322 | name = "[CP] Check Pods Manifest.lock"; 323 | outputFileListPaths = ( 324 | ); 325 | outputPaths = ( 326 | "$(DERIVED_FILE_DIR)/Pods-UIKitForMacPlayground-checkManifestLockResult.txt", 327 | ); 328 | runOnlyForDeploymentPostprocessing = 0; 329 | shellPath = /bin/sh; 330 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 331 | showEnvVarsInLog = 0; 332 | }; 333 | /* End PBXShellScriptBuildPhase section */ 334 | 335 | /* Begin PBXSourcesBuildPhase section */ 336 | A116368322C520AD0020072C /* Sources */ = { 337 | isa = PBXSourcesBuildPhase; 338 | buildActionMask = 2147483647; 339 | files = ( 340 | A1D887A422DCA275007C73ED /* HoverExampleView.swift in Sources */, 341 | A1D887A822DCB108007C73ED /* DragAndDropViewController.swift in Sources */, 342 | 78646C4E22E87D2200B29FE7 /* (null) in Sources */, 343 | A116368F22C520AD0020072C /* ViewController.swift in Sources */, 344 | A1D887C722DE06EF007C73ED /* TouchBarViewController.swift in Sources */, 345 | A1147A2522F03713003DC870 /* NotificationsViewController.swift in Sources */, 346 | A1D8879E22DCA1F6007C73ED /* HeaderView.swift in Sources */, 347 | A1D887A622DCA299007C73ED /* ColorView.swift in Sources */, 348 | A1147A2922F0386B003DC870 /* CursorsViewController.swift in Sources */, 349 | A116368B22C520AD0020072C /* AppDelegate.swift in Sources */, 350 | A1147A2D22F08F6D003DC870 /* SwiftUIView.swift in Sources */, 351 | A1D887A022DCA20B007C73ED /* UserActivities.swift in Sources */, 352 | A1A568FC22DFD4900045DF86 /* SceneDelegate.swift in Sources */, 353 | A1A1A69322CB0D97003B81A7 /* ListViewController.swift in Sources */, 354 | A1D887C522DD308C007C73ED /* AppKitBundleLoader.m in Sources */, 355 | A116117322D003B000251B87 /* WindowsViewController.swift in Sources */, 356 | A1D8877722DC3D3B007C73ED /* SquareSceneDelegate.swift in Sources */, 357 | A1D887A222DCA24E007C73ED /* HoverViewController.swift in Sources */, 358 | A1D8879C22DCA1AA007C73ED /* ExamplesViewController.swift in Sources */, 359 | A1D887C922DE0D78007C73ED /* ContextMenuViewController.swift in Sources */, 360 | A1D887AC22DCB27F007C73ED /* DropExampleView.swift in Sources */, 361 | A1D887CB22DE3670007C73ED /* DetailType.swift in Sources */, 362 | A1D887AA22DCB167007C73ED /* DragExampleView.swift in Sources */, 363 | 78646C4D22E87D2200B29FE7 /* (null) in Sources */, 364 | A1D8879A22DCA178007C73ED /* HashableIdGenerator.swift in Sources */, 365 | A1CDC1E222E98C7700B72002 /* OpenFileViewController.swift in Sources */, 366 | ); 367 | runOnlyForDeploymentPostprocessing = 0; 368 | }; 369 | A1D887AD22DCC874007C73ED /* Sources */ = { 370 | isa = PBXSourcesBuildPhase; 371 | buildActionMask = 2147483647; 372 | files = ( 373 | A1D887CC22DE367E007C73ED /* DetailType.swift in Sources */, 374 | A1D887B922DCC9F3007C73ED /* AppKitPrincipal.swift in Sources */, 375 | ); 376 | runOnlyForDeploymentPostprocessing = 0; 377 | }; 378 | /* End PBXSourcesBuildPhase section */ 379 | 380 | /* Begin PBXTargetDependency section */ 381 | A1A568FF22DFD7020045DF86 /* PBXTargetDependency */ = { 382 | isa = PBXTargetDependency; 383 | target = A1D887B022DCC874007C73ED /* UIKitForMacPlaygroundSupportingBundle */; 384 | targetProxy = A1A568FE22DFD7020045DF86 /* PBXContainerItemProxy */; 385 | }; 386 | /* End PBXTargetDependency section */ 387 | 388 | /* Begin PBXVariantGroup section */ 389 | A116369522C520AF0020072C /* LaunchScreen.storyboard */ = { 390 | isa = PBXVariantGroup; 391 | children = ( 392 | A116369622C520AF0020072C /* Base */, 393 | ); 394 | name = LaunchScreen.storyboard; 395 | sourceTree = ""; 396 | }; 397 | /* End PBXVariantGroup section */ 398 | 399 | /* Begin XCBuildConfiguration section */ 400 | A116369922C520AF0020072C /* Debug */ = { 401 | isa = XCBuildConfiguration; 402 | buildSettings = { 403 | ALWAYS_SEARCH_USER_PATHS = NO; 404 | CLANG_ANALYZER_NONNULL = YES; 405 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 406 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 407 | CLANG_CXX_LIBRARY = "libc++"; 408 | CLANG_ENABLE_MODULES = YES; 409 | CLANG_ENABLE_OBJC_ARC = YES; 410 | CLANG_ENABLE_OBJC_WEAK = YES; 411 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 412 | CLANG_WARN_BOOL_CONVERSION = YES; 413 | CLANG_WARN_COMMA = YES; 414 | CLANG_WARN_CONSTANT_CONVERSION = YES; 415 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 416 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 417 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 418 | CLANG_WARN_EMPTY_BODY = YES; 419 | CLANG_WARN_ENUM_CONVERSION = YES; 420 | CLANG_WARN_INFINITE_RECURSION = YES; 421 | CLANG_WARN_INT_CONVERSION = YES; 422 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 423 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 424 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 425 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 426 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 427 | CLANG_WARN_STRICT_PROTOTYPES = YES; 428 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 429 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 430 | CLANG_WARN_UNREACHABLE_CODE = YES; 431 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 432 | COPY_PHASE_STRIP = NO; 433 | DEBUG_INFORMATION_FORMAT = dwarf; 434 | ENABLE_STRICT_OBJC_MSGSEND = YES; 435 | ENABLE_TESTABILITY = YES; 436 | GCC_C_LANGUAGE_STANDARD = gnu11; 437 | GCC_DYNAMIC_NO_PIC = NO; 438 | GCC_NO_COMMON_BLOCKS = YES; 439 | GCC_OPTIMIZATION_LEVEL = 0; 440 | GCC_PREPROCESSOR_DEFINITIONS = ( 441 | "DEBUG=1", 442 | "$(inherited)", 443 | ); 444 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 445 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 446 | GCC_WARN_UNDECLARED_SELECTOR = YES; 447 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 448 | GCC_WARN_UNUSED_FUNCTION = YES; 449 | GCC_WARN_UNUSED_VARIABLE = YES; 450 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 451 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 452 | MTL_FAST_MATH = YES; 453 | ONLY_ACTIVE_ARCH = YES; 454 | SDKROOT = iphoneos; 455 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 456 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 457 | }; 458 | name = Debug; 459 | }; 460 | A116369A22C520AF0020072C /* Release */ = { 461 | isa = XCBuildConfiguration; 462 | buildSettings = { 463 | ALWAYS_SEARCH_USER_PATHS = NO; 464 | CLANG_ANALYZER_NONNULL = YES; 465 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 466 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 467 | CLANG_CXX_LIBRARY = "libc++"; 468 | CLANG_ENABLE_MODULES = YES; 469 | CLANG_ENABLE_OBJC_ARC = YES; 470 | CLANG_ENABLE_OBJC_WEAK = YES; 471 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 472 | CLANG_WARN_BOOL_CONVERSION = YES; 473 | CLANG_WARN_COMMA = YES; 474 | CLANG_WARN_CONSTANT_CONVERSION = YES; 475 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 476 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 477 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 478 | CLANG_WARN_EMPTY_BODY = YES; 479 | CLANG_WARN_ENUM_CONVERSION = YES; 480 | CLANG_WARN_INFINITE_RECURSION = YES; 481 | CLANG_WARN_INT_CONVERSION = YES; 482 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 483 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 484 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 485 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 486 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 487 | CLANG_WARN_STRICT_PROTOTYPES = YES; 488 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 489 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 490 | CLANG_WARN_UNREACHABLE_CODE = YES; 491 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 492 | COPY_PHASE_STRIP = NO; 493 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 494 | ENABLE_NS_ASSERTIONS = NO; 495 | ENABLE_STRICT_OBJC_MSGSEND = YES; 496 | GCC_C_LANGUAGE_STANDARD = gnu11; 497 | GCC_NO_COMMON_BLOCKS = YES; 498 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 499 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 500 | GCC_WARN_UNDECLARED_SELECTOR = YES; 501 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 502 | GCC_WARN_UNUSED_FUNCTION = YES; 503 | GCC_WARN_UNUSED_VARIABLE = YES; 504 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 505 | MTL_ENABLE_DEBUG_INFO = NO; 506 | MTL_FAST_MATH = YES; 507 | SDKROOT = iphoneos; 508 | SWIFT_COMPILATION_MODE = wholemodule; 509 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 510 | VALIDATE_PRODUCT = YES; 511 | }; 512 | name = Release; 513 | }; 514 | A116369C22C520AF0020072C /* Debug */ = { 515 | isa = XCBuildConfiguration; 516 | baseConfigurationReference = A793676ECB32AE0CCFB9C6EA /* Pods-UIKitForMacPlayground.debug.xcconfig */; 517 | buildSettings = { 518 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 519 | CLANG_ENABLE_MODULES = YES; 520 | CODE_SIGN_ENTITLEMENTS = UIKitForMacPlayground/UIKitForMacPlayground.entitlements; 521 | CODE_SIGN_IDENTITY = "Apple Development"; 522 | "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; 523 | CODE_SIGN_STYLE = Automatic; 524 | DERIVE_MACCATALYST_PRODUCT_BUNDLE_IDENTIFIER = YES; 525 | DERIVE_UIKITFORMAC_PRODUCT_BUNDLE_IDENTIFIER = YES; 526 | DEVELOPMENT_TEAM = 29P3YRFLV5; 527 | INFOPLIST_FILE = UIKitForMacPlayground/Info.plist; 528 | LD_RUNPATH_SEARCH_PATHS = ( 529 | "$(inherited)", 530 | "@executable_path/Frameworks", 531 | ); 532 | PRODUCT_BUNDLE_IDENTIFIER = com.noahgilmore.UIKitForMacPlayground; 533 | PRODUCT_NAME = "$(TARGET_NAME)"; 534 | PROVISIONING_PROFILE_SPECIFIER = ""; 535 | "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; 536 | SUPPORTS_MACCATALYST = YES; 537 | SUPPORTS_UIKITFORMAC = YES; 538 | SWIFT_OBJC_BRIDGING_HEADER = "UIKitForMacPlayground/UIKitForMacPlayground-Bridging-Header.h"; 539 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 540 | SWIFT_VERSION = 5.0; 541 | TARGETED_DEVICE_FAMILY = "1,2"; 542 | }; 543 | name = Debug; 544 | }; 545 | A116369D22C520AF0020072C /* Release */ = { 546 | isa = XCBuildConfiguration; 547 | baseConfigurationReference = 417486A30CE1D0BCFC3EB3F4 /* Pods-UIKitForMacPlayground.release.xcconfig */; 548 | buildSettings = { 549 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 550 | CLANG_ENABLE_MODULES = YES; 551 | CODE_SIGN_ENTITLEMENTS = UIKitForMacPlayground/UIKitForMacPlayground.entitlements; 552 | CODE_SIGN_STYLE = Automatic; 553 | DERIVE_MACCATALYST_PRODUCT_BUNDLE_IDENTIFIER = YES; 554 | DERIVE_UIKITFORMAC_PRODUCT_BUNDLE_IDENTIFIER = YES; 555 | DEVELOPMENT_TEAM = 29P3YRFLV5; 556 | INFOPLIST_FILE = UIKitForMacPlayground/Info.plist; 557 | LD_RUNPATH_SEARCH_PATHS = ( 558 | "$(inherited)", 559 | "@executable_path/Frameworks", 560 | ); 561 | PRODUCT_BUNDLE_IDENTIFIER = com.noahgilmore.UIKitForMacPlayground; 562 | PRODUCT_NAME = "$(TARGET_NAME)"; 563 | SUPPORTS_MACCATALYST = YES; 564 | SUPPORTS_UIKITFORMAC = YES; 565 | SWIFT_OBJC_BRIDGING_HEADER = "UIKitForMacPlayground/UIKitForMacPlayground-Bridging-Header.h"; 566 | SWIFT_VERSION = 5.0; 567 | TARGETED_DEVICE_FAMILY = "1,2"; 568 | }; 569 | name = Release; 570 | }; 571 | A1D887B522DCC874007C73ED /* Debug */ = { 572 | isa = XCBuildConfiguration; 573 | buildSettings = { 574 | CLANG_ENABLE_MODULES = YES; 575 | CODE_SIGN_STYLE = Automatic; 576 | COMBINE_HIDPI_IMAGES = YES; 577 | DERIVE_MACCATALYST_PRODUCT_BUNDLE_IDENTIFIER = NO; 578 | DEVELOPMENT_TEAM = 29P3YRFLV5; 579 | GCC_PREPROCESSOR_DEFINITIONS = ( 580 | "DEBUG=1", 581 | "IS_APPKIT_BUNDLE=1", 582 | ); 583 | INFOPLIST_FILE = UIKitForMacPlaygroundSupportingBundle/Info.plist; 584 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; 585 | LD_RUNPATH_SEARCH_PATHS = ( 586 | "$(inherited)", 587 | "@executable_path/../Frameworks", 588 | "@loader_path/../Frameworks", 589 | ); 590 | MACOSX_DEPLOYMENT_TARGET = 10.15; 591 | PRODUCT_BUNDLE_IDENTIFIER = com.noahgilmore.UIKitForMacPlaygroundSupportingBundle; 592 | PRODUCT_NAME = "$(TARGET_NAME)"; 593 | SDKROOT = macosx; 594 | SKIP_INSTALL = YES; 595 | SUPPORTS_MACCATALYST = NO; 596 | SWIFT_OBJC_BRIDGING_HEADER = "UIKitForMacPlaygroundSupportingBundle/UIKitForMacPlaygroundSupportingBundle-Bridging-Header.h"; 597 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 598 | SWIFT_VERSION = 5.0; 599 | WRAPPER_EXTENSION = bundle; 600 | }; 601 | name = Debug; 602 | }; 603 | A1D887B622DCC874007C73ED /* Release */ = { 604 | isa = XCBuildConfiguration; 605 | buildSettings = { 606 | CLANG_ENABLE_MODULES = YES; 607 | CODE_SIGN_STYLE = Automatic; 608 | COMBINE_HIDPI_IMAGES = YES; 609 | DERIVE_MACCATALYST_PRODUCT_BUNDLE_IDENTIFIER = NO; 610 | DEVELOPMENT_TEAM = Y5PE65HELJ; 611 | GCC_PREPROCESSOR_DEFINITIONS = "IS_APPKIT_BUNDLE=1"; 612 | INFOPLIST_FILE = UIKitForMacPlaygroundSupportingBundle/Info.plist; 613 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; 614 | LD_RUNPATH_SEARCH_PATHS = ( 615 | "$(inherited)", 616 | "@executable_path/../Frameworks", 617 | "@loader_path/../Frameworks", 618 | ); 619 | MACOSX_DEPLOYMENT_TARGET = 10.15; 620 | PRODUCT_BUNDLE_IDENTIFIER = com.noahgilmore.UIKitForMacPlaygroundSupportingBundle; 621 | PRODUCT_NAME = "$(TARGET_NAME)"; 622 | SDKROOT = macosx; 623 | SKIP_INSTALL = YES; 624 | SUPPORTS_MACCATALYST = NO; 625 | SWIFT_OBJC_BRIDGING_HEADER = "UIKitForMacPlaygroundSupportingBundle/UIKitForMacPlaygroundSupportingBundle-Bridging-Header.h"; 626 | SWIFT_VERSION = 5.0; 627 | WRAPPER_EXTENSION = bundle; 628 | }; 629 | name = Release; 630 | }; 631 | /* End XCBuildConfiguration section */ 632 | 633 | /* Begin XCConfigurationList section */ 634 | A116368222C520AD0020072C /* Build configuration list for PBXProject "UIKitForMacPlayground" */ = { 635 | isa = XCConfigurationList; 636 | buildConfigurations = ( 637 | A116369922C520AF0020072C /* Debug */, 638 | A116369A22C520AF0020072C /* Release */, 639 | ); 640 | defaultConfigurationIsVisible = 0; 641 | defaultConfigurationName = Release; 642 | }; 643 | A116369B22C520AF0020072C /* Build configuration list for PBXNativeTarget "UIKitForMacPlayground" */ = { 644 | isa = XCConfigurationList; 645 | buildConfigurations = ( 646 | A116369C22C520AF0020072C /* Debug */, 647 | A116369D22C520AF0020072C /* Release */, 648 | ); 649 | defaultConfigurationIsVisible = 0; 650 | defaultConfigurationName = Release; 651 | }; 652 | A1D887B422DCC874007C73ED /* Build configuration list for PBXNativeTarget "UIKitForMacPlaygroundSupportingBundle" */ = { 653 | isa = XCConfigurationList; 654 | buildConfigurations = ( 655 | A1D887B522DCC874007C73ED /* Debug */, 656 | A1D887B622DCC874007C73ED /* Release */, 657 | ); 658 | defaultConfigurationIsVisible = 0; 659 | defaultConfigurationName = Release; 660 | }; 661 | /* End XCConfigurationList section */ 662 | }; 663 | rootObject = A116367F22C520AD0020072C /* Project object */; 664 | } 665 | -------------------------------------------------------------------------------- /UIKitForMacPlayground.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /UIKitForMacPlayground.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /UIKitForMacPlayground.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /UIKitForMacPlayground.xcodeproj/xcuserdata/noahgilmore.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | UIKitForMacPlayground.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 1 11 | 12 | UIKitForMacPlaygroundSupportingBundle.xcscheme_^#shared#^_ 13 | 14 | orderHint 15 | 2 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /UIKitForMacPlayground.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /UIKitForMacPlayground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /UIKitForMacPlayground.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /UIKitForMacPlayground/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // UIKitForMacPlayground 4 | // 5 | // Created by Noah Gilmore on 6/27/19. 6 | // Copyright © 2019 Noah Gilmore. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import UserNotifications 11 | 12 | @UIApplicationMain 13 | class AppDelegate: UIResponder, UIApplicationDelegate { 14 | var bridge: AppKitObjcBridge? = nil 15 | 16 | static var shared: AppDelegate { 17 | return UIApplication.shared.delegate as! AppDelegate 18 | } 19 | 20 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 21 | // Override point for customization after application launch. 22 | let pluginPath = Bundle.main.builtInPlugInsPath!.appending("/UIKitForMacPlaygroundSupportingBundle.bundle") 23 | let bundle = Bundle(path: pluginPath)! 24 | let loader = AppKitBundleLoader() 25 | bridge = loader.load(bundle) 26 | bridge?.setUIKit(self) 27 | 28 | UNUserNotificationCenter.current().delegate = self 29 | 30 | return true 31 | } 32 | 33 | func applicationWillTerminate(_ application: UIApplication) { 34 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 35 | } 36 | 37 | // MARK: UISceneSession Lifecycle 38 | 39 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 40 | 41 | if let activity = options.userActivities.first { 42 | if activity.activityType == UserActivities.square.rawValue { 43 | let configuration = UISceneConfiguration(name: nil, sessionRole: connectingSceneSession.role) 44 | configuration.delegateClass = SquareSceneDelegate.self 45 | return configuration 46 | } 47 | } 48 | 49 | let configuration = UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 50 | configuration.delegateClass = SceneDelegate.self 51 | return configuration 52 | } 53 | 54 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { 55 | // Called when the user discards a scene session. 56 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 57 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 58 | } 59 | 60 | override func buildMenu(with builder: UIMenuBuilder) { 61 | super.buildMenu(with: builder) 62 | 63 | let command = UIKeyCommand( 64 | input: "J", 65 | modifierFlags: [.command], 66 | action: #selector(self.jumpSelected(_:)) 67 | ) 68 | command.title = "Jump to Windows" 69 | 70 | let noShortcutCommand = UICommand( 71 | title: "No shortcut", 72 | image: nil, 73 | action: #selector(self.noShortcutSelected), 74 | propertyList: nil 75 | ) 76 | 77 | builder.insertChild(UIMenu( 78 | title: "Jump", 79 | image: nil, 80 | identifier: UIMenu.Identifier(rawValue: "com.noahgilmore.uikitformacplaygroud.jump"), 81 | options: [], 82 | children: [command, noShortcutCommand] 83 | ), atEndOfMenu: .view) 84 | } 85 | 86 | @objc private func jumpSelected(_ sender: AppDelegate) { 87 | guard let delegate = UIApplication.shared.connectedScenes.compactMap({ 88 | ($0 as? UIWindowScene) 89 | }).filter({ $0.activationState == .foregroundActive }) 90 | .first?.delegate else { return } 91 | guard let sceneDelegate = delegate as? SceneDelegate else { return } 92 | sceneDelegate.handleDetailType(.windows) 93 | } 94 | 95 | @objc private func noShortcutSelected(_ sender: AppDelegate) { 96 | guard let delegate = UIApplication.shared.connectedScenes.compactMap({ 97 | ($0 as? UIWindowScene) 98 | }).filter({ $0.activationState == .foregroundActive }) 99 | .first?.delegate else { return } 100 | guard let sceneDelegate = delegate as? SceneDelegate else { return } 101 | sceneDelegate.handleDetailType(.keyboardShortcuts) 102 | } 103 | } 104 | 105 | extension AppDelegate: UIKitBridge { 106 | func didSelectDetailType(_ detailTypeString: String) { 107 | guard let detailType = DetailType(rawValue: detailTypeString) else { return } 108 | print(detailType) 109 | guard let scene = UIResponder.currentWindowScene() else { return } 110 | print(scene) 111 | guard let delegate = scene.delegate as? SceneDelegate else { return } 112 | print(delegate) 113 | delegate.handleDetailType(detailType) 114 | } 115 | } 116 | 117 | private weak var currentFirstResponder: UIResponder? 118 | extension UIResponder { 119 | var containingScene: UIWindowScene? { 120 | var currentResponder: UIResponder = self 121 | while let responder = currentResponder.next { 122 | if let windowScene = responder as? UIWindowScene { 123 | return windowScene 124 | } 125 | currentResponder = responder 126 | } 127 | return nil 128 | } 129 | 130 | @objc func findFirstResponder(sender: AnyObject) { 131 | currentFirstResponder = self 132 | } 133 | 134 | static func firstResponder() -> UIResponder? { 135 | currentFirstResponder = nil 136 | UIApplication.shared.sendAction(#selector(findFirstResponder(sender:)), to: nil, from: nil, for: nil) 137 | return currentFirstResponder 138 | } 139 | 140 | static func currentWindowScene() -> UIWindowScene? { 141 | return firstResponder()?.containingScene 142 | } 143 | } 144 | 145 | extension AppDelegate: UNUserNotificationCenterDelegate { 146 | func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { 147 | // Allows displaying rich notifications even while the app is in the foreground 148 | completionHandler([.alert, .badge, .sound]) 149 | } 150 | } 151 | 152 | -------------------------------------------------------------------------------- /UIKitForMacPlayground/AppKitBridge.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppKitBridge.swift 3 | // UIKitForMacPlayground 4 | // 5 | // Created by Noah Gilmore on 7/15/19. 6 | // Copyright © 2019 Noah Gilmore. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol AppKitBridge: NSObjectProtocol { 12 | func ping() 13 | } 14 | -------------------------------------------------------------------------------- /UIKitForMacPlayground/AppKitBundleLoader.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppKitBundleLoader.h 3 | // UIKitForMacPlayground 4 | // 5 | // Created by Noah Gilmore on 7/15/19. 6 | // Copyright © 2019 Noah Gilmore. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #if TARGET_OS_MACCATALYST || IS_APPKIT_BUNDLE 12 | #import 13 | #endif 14 | 15 | @protocol UIKitBridge; 16 | 17 | NS_ASSUME_NONNULL_BEGIN 18 | 19 | @protocol AppKitObjcBridge 20 | 21 | - (void)moveWindowRight; 22 | #if TARGET_OS_MACCATALYST || IS_APPKIT_BUNDLE 23 | - (NSToolbarItem *)customToolbarItemWithIdentifier:(NSString *)identifier callback:(void (^)(NSString *))callback NS_SWIFT_NAME(customToolbarItem(identifier:callback:)); 24 | #endif 25 | - (void)setUIKitBridge:(id)bridge; 26 | - (void)setPointerCursor; 27 | - (void)setDefaultCursor; 28 | - (void)sceneBecameActiveWithIdentifier:(NSString *)string NS_SWIFT_NAME(sceneBecameActive(identifier:)); 29 | 30 | @end 31 | 32 | @interface AppKitBundleLoader : NSObject 33 | 34 | - (id)loadBundle:(NSBundle *)bundle; 35 | 36 | @end 37 | 38 | NS_ASSUME_NONNULL_END 39 | -------------------------------------------------------------------------------- /UIKitForMacPlayground/AppKitBundleLoader.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppKitBundleLoader.m 3 | // UIKitForMacPlayground 4 | // 5 | // Created by Noah Gilmore on 7/15/19. 6 | // Copyright © 2019 Noah Gilmore. All rights reserved. 7 | // 8 | 9 | #import "AppKitBundleLoader.h" 10 | 11 | @implementation AppKitBundleLoader 12 | 13 | - (id)loadBundle:(NSBundle *)bundle { 14 | id bridge = [[[bundle principalClass] alloc] init]; 15 | return bridge; 16 | } 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /UIKitForMacPlayground/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /UIKitForMacPlayground/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /UIKitForMacPlayground/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 | -------------------------------------------------------------------------------- /UIKitForMacPlayground/ColorView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ColorView.swift 3 | // UIKitForMacPlayground 4 | // 5 | // Created by Noah Gilmore on 7/15/19. 6 | // Copyright © 2019 Noah Gilmore. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ColorView: UIView { 12 | var color: UIColor { 13 | didSet { 14 | self.backgroundColor = color 15 | } 16 | } 17 | 18 | init(color: UIColor, dimension: CGFloat? = nil) { 19 | self.color = color 20 | super.init(frame: .zero) 21 | 22 | self.backgroundColor = color 23 | 24 | if let dimension = dimension { 25 | self.widthAnchor.constraint(equalToConstant: dimension).isActive = true 26 | self.heightAnchor.constraint(equalToConstant: dimension).isActive = true 27 | } 28 | } 29 | 30 | required init?(coder aDecoder: NSCoder) { 31 | fatalError("init(coder:) has not been implemented") 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /UIKitForMacPlayground/ContextMenuViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContextMenuViewController.swift 3 | // UIKitForMacPlayground 4 | // 5 | // Created by Noah Gilmore on 7/16/19. 6 | // Copyright © 2019 Noah Gilmore. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | final class ContextMenuViewController: ExamplesViewController { 13 | let colorView = ColorView(color: .systemTeal, dimension: 100) 14 | 15 | override func viewDidLoad() { 16 | self.views = [ 17 | .paragraph(text: "This square has a context menu (right click to trigger)"), 18 | .view(view: colorView) 19 | ] 20 | super.viewDidLoad() 21 | 22 | colorView.addInteraction(UIContextMenuInteraction(delegate: self)) 23 | } 24 | } 25 | 26 | extension ContextMenuViewController: UIContextMenuInteractionDelegate { 27 | func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? { 28 | return UIContextMenuConfiguration(identifier: nil, previewProvider: nil, actionProvider: { suggestedActions in 29 | return UIMenu(title: "Menu", image: nil, identifier: nil, children: [ 30 | UIAction(title: "Change to random color", image: UIImage(systemName: "square.and.arrow.up"), identifier: UIAction.Identifier(rawValue: "open"), handler: { action in 31 | self.colorView.color = generateRandomColor() 32 | }), 33 | UIMenu(title: "Predefined colors", image: nil, identifier: nil, children: [ 34 | (string: "Red", color: UIColor.systemRed), 35 | (string: "Blue", color: UIColor.systemBlue), 36 | (string: "Green", color: UIColor.systemGreen) 37 | ].map { config in 38 | UIAction(title: config.string, image: nil, identifier: UIAction.Identifier(rawValue: "open-\(config.string)"), handler: { action in 39 | self.colorView.color = config.color 40 | }) 41 | }) 42 | ]) 43 | }) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /UIKitForMacPlayground/CursorsViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CursorsViewController.swift 3 | // UIKitForMacPlayground 4 | // 5 | // Created by Noah Gilmore on 7/17/19. 6 | // Copyright © 2019 Noah Gilmore. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | final class CursorPointerGestureRegocnizerTarget { 13 | @objc func handleHover(_ sender: UIHoverGestureRecognizer) { 14 | switch sender.state { 15 | case .began, .changed: 16 | AppDelegate.shared.bridge?.setPointerCursor() 17 | case .ended: 18 | AppDelegate.shared.bridge?.setDefaultCursor() 19 | default: 20 | break 21 | } 22 | } 23 | } 24 | 25 | final class CursorPointerGestureRecognizer: UIHoverGestureRecognizer { 26 | private let target: CursorPointerGestureRegocnizerTarget 27 | init() { 28 | let target = CursorPointerGestureRegocnizerTarget() 29 | self.target = target 30 | super.init(target: target, action: #selector(CursorPointerGestureRegocnizerTarget.handleHover(_:))) 31 | } 32 | } 33 | 34 | final class CursorsViewController: ExamplesViewController { 35 | let colorView = ColorView(color: .systemPink, dimension: 100) 36 | weak var bridge: AppKitObjcBridge? = nil 37 | 38 | override func viewDidLoad() { 39 | bridge = AppDelegate.shared.bridge 40 | if bridge != nil { 41 | self.views = [ 42 | .paragraph(text: "This square changes the cursor when hovering over it."), 43 | .paragraph(text: "Also, all the buttons in this app change the cursor to a pointer when they're hovered over."), 44 | .view(view: colorView) 45 | ] 46 | } else { 47 | self.views = [ 48 | .paragraph(text: "Cursors are not supported in non-AppKit runtime contexts."), 49 | .view(view: colorView) 50 | ] 51 | } 52 | super.viewDidLoad() 53 | 54 | colorView.addGestureRecognizer(UIHoverGestureRecognizer(target: self, action: #selector(handleHover(_:)))) 55 | } 56 | 57 | @objc private func handleHover(_ sender: UIHoverGestureRecognizer) { 58 | switch sender.state { 59 | case .began, .changed: 60 | AppDelegate.shared.bridge?.setPointerCursor() 61 | case .ended: 62 | AppDelegate.shared.bridge?.setDefaultCursor() 63 | default: 64 | break 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /UIKitForMacPlayground/DetailType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DetailType.swift 3 | // UIKitForMacPlayground 4 | // 5 | // Created by Noah Gilmore on 7/16/19. 6 | // Copyright © 2019 Noah Gilmore. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | enum DetailType: String, CaseIterable { 12 | case keyboardShortcuts = "Keyboard Shortcuts" 13 | case windows = "Windows" 14 | case hover = "Hover" 15 | case dragAndDrop = "Drag and drop" 16 | case touchBar = "Touch bar" 17 | case contextMenus = "Context menus" 18 | case cursors = "Cursors" 19 | case swiftUI = "SwiftUI" 20 | case openFile = "Opening files" 21 | case notifications = "Notifications" 22 | } 23 | 24 | @objc protocol UIKitBridge { 25 | func didSelectDetailType(_ detailTypeString: String) 26 | } 27 | 28 | extension Notification.Name { 29 | static let didChangeDetailType = Notification.Name("didChangeDetailType") 30 | } 31 | -------------------------------------------------------------------------------- /UIKitForMacPlayground/DragAndDropViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DragAndDropViewController.swift 3 | // UIKitForMacPlayground 4 | // 5 | // Created by Noah Gilmore on 7/15/19. 6 | // Copyright © 2019 Noah Gilmore. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | class DragAndDropViewController: ExamplesViewController { 13 | override func viewDidLoad() { 14 | self.views = [ 15 | .paragraph(text: "These squares are drag targets: drag them into the drop targets to set color (even across windows)."), 16 | .view(view: DragExampleView()), 17 | .paragraph(text: "These squares are drop targets: drag a drag target onto them to set their color (even across windows)"), 18 | .view(view: DropExampleView()), 19 | ] 20 | 21 | super.viewDidLoad() 22 | } 23 | } 24 | 25 | -------------------------------------------------------------------------------- /UIKitForMacPlayground/DragExampleView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DragExampleView.swift 3 | // UIKitForMacPlayground 4 | // 5 | // Created by Noah Gilmore on 7/15/19. 6 | // Copyright © 2019 Noah Gilmore. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | final class DragColorView: ColorView { 13 | init(color: UIColor) { 14 | super.init(color: color, dimension: 70) 15 | let interaction = UIDragInteraction(delegate: self) 16 | self.addInteraction(interaction) 17 | } 18 | 19 | required init?(coder aDecoder: NSCoder) { 20 | fatalError("init(coder:) has not been implemented") 21 | } 22 | } 23 | 24 | extension DragColorView: UIDragInteractionDelegate { 25 | func dragInteraction(_ interaction: UIDragInteraction, itemsForBeginning session: UIDragSession) -> [UIDragItem] { 26 | return [UIDragItem(itemProvider: NSItemProvider(object: self.color))] 27 | } 28 | } 29 | 30 | final class DragExampleView: UIView { 31 | private let stackView: UIStackView = { 32 | let view = UIStackView() 33 | view.axis = .horizontal 34 | view.spacing = 40 35 | return view 36 | }() 37 | 38 | init() { 39 | super.init(frame: .zero) 40 | 41 | self.addSubview(stackView) 42 | for color in [UIColor.red, UIColor.blue, UIColor.green] { 43 | let colorView = DragColorView(color: color) 44 | stackView.addArrangedSubview(colorView) 45 | } 46 | stackView.translatesAutoresizingMaskIntoConstraints = false 47 | stackView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true 48 | stackView.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true 49 | stackView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true 50 | stackView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true 51 | 52 | // let view = DragColorView(color: UIColor.purple) 53 | // self.addSubview(view) 54 | // view.translatesAutoresizingMaskIntoConstraints = false 55 | // view.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true 56 | // view.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true 57 | // view.topAnchor.constraint(equalTo: self.topAnchor).isActive = true 58 | // view.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true 59 | } 60 | 61 | required init?(coder aDecoder: NSCoder) { 62 | fatalError("init(coder:) has not been implemented") 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /UIKitForMacPlayground/DropExampleView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DropExampleView.swift 3 | // UIKitForMacPlayground 4 | // 5 | // Created by Noah Gilmore on 7/15/19. 6 | // Copyright © 2019 Noah Gilmore. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | final class DropColorView: ColorView { 13 | init(color: UIColor) { 14 | super.init(color: color, dimension: 100) 15 | let interaction = UIDropInteraction(delegate: self) 16 | self.addInteraction(interaction) 17 | } 18 | 19 | required init?(coder aDecoder: NSCoder) { 20 | fatalError("init(coder:) has not been implemented") 21 | } 22 | } 23 | 24 | extension DropColorView: UIDropInteractionDelegate { 25 | func dropInteraction(_ interaction: UIDropInteraction, canHandle session: UIDropSession) -> Bool { 26 | return session.canLoadObjects(ofClass: UIColor.self) 27 | } 28 | 29 | func dropInteraction(_ interaction: UIDropInteraction, performDrop session: UIDropSession) { 30 | session.loadObjects(ofClass: UIColor.self) { colors in 31 | let color = colors.first as! UIColor 32 | self.color = color 33 | } 34 | } 35 | 36 | func dropInteraction(_ interaction: UIDropInteraction, sessionDidUpdate session: UIDropSession) -> UIDropProposal { 37 | return UIDropProposal(operation: .copy) 38 | } 39 | } 40 | 41 | final class DropExampleView: UIView { 42 | private let stackView: UIStackView = { 43 | let view = UIStackView() 44 | view.axis = .horizontal 45 | view.spacing = 40 46 | return view 47 | }() 48 | 49 | init() { 50 | super.init(frame: .zero) 51 | 52 | self.addSubview(stackView) 53 | 54 | for color in [UIColor.red, UIColor.blue, UIColor.green] { 55 | let colorView = DropColorView(color: color) 56 | stackView.addArrangedSubview(colorView) 57 | } 58 | 59 | stackView.translatesAutoresizingMaskIntoConstraints = false 60 | stackView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true 61 | stackView.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true 62 | stackView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true 63 | stackView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true 64 | 65 | // let view = DropColorView(color: UIColor.systemPink) 66 | // self.addSubview(view) 67 | // view.translatesAutoresizingMaskIntoConstraints = false 68 | // view.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true 69 | // view.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true 70 | // view.topAnchor.constraint(equalTo: self.topAnchor).isActive = true 71 | // view.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true 72 | } 73 | 74 | required init?(coder aDecoder: NSCoder) { 75 | fatalError("init(coder:) has not been implemented") 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /UIKitForMacPlayground/ExamplesViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExamplesViewController.swift 3 | // UIKitForMacPlayground 4 | // 5 | // Created by Noah Gilmore on 7/15/19. 6 | // Copyright © 2019 Noah Gilmore. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | class AppKitWindowReportingViewController: UIViewController { 13 | var identifier: String? = nil 14 | 15 | override func viewDidAppear(_ animated: Bool) { 16 | super.viewDidAppear(animated) 17 | 18 | if let identifier = self.identifier { 19 | if self.view.window?.rootViewController == self { 20 | AppDelegate.shared.bridge?.sceneBecameActive(identifier: identifier) 21 | } 22 | } 23 | } 24 | } 25 | 26 | /// Subclasses extend this controller to provide standard components in a list (ViewType instances) 27 | class ExamplesViewController: AppKitWindowReportingViewController { 28 | typealias ButtonHandler = (ExamplesViewController) -> Void 29 | enum ViewType { 30 | case header(text: String) 31 | case paragraph(text: String) 32 | case button(text: String, color: UIColor, didTap: ButtonHandler) 33 | case view(view: UIView) 34 | } 35 | 36 | var views: [ViewType] = [] 37 | 38 | private let stackView: UIStackView = { 39 | let view = UIStackView() 40 | view.axis = .vertical 41 | view.spacing = 20 42 | view.alignment = .leading 43 | return view 44 | }() 45 | 46 | private var buttonUUIDHashValuesToActions: [Int: ButtonHandler] = [:] 47 | 48 | override func viewDidLoad() { 49 | super.viewDidLoad() 50 | 51 | self.view.backgroundColor = UIColor.systemBackground 52 | 53 | for view in views { 54 | switch view { 55 | case let .header(text): 56 | stackView.addArrangedSubview(HeaderView(text: text)) 57 | case let .paragraph(text): 58 | let label = UILabel() 59 | label.textColor = .label 60 | label.text = text 61 | label.numberOfLines = 0 62 | stackView.addArrangedSubview(label) 63 | case let .button(text, color, didTap): 64 | let button = UIButton() 65 | let uid = HashableIdGenerator.next() 66 | button.setTitle(text, for: .normal) 67 | button.tag = uid 68 | button.addTarget(self, action: #selector(didTapButton(_:)), for: .touchUpInside) 69 | button.setTitleColor(color, for: .normal) 70 | button.addGestureRecognizer(CursorPointerGestureRecognizer()) 71 | buttonUUIDHashValuesToActions[uid] = didTap 72 | stackView.addArrangedSubview(button) 73 | case let .view(view): 74 | stackView.addArrangedSubview(view) 75 | } 76 | } 77 | 78 | self.view.addSubview(stackView) 79 | stackView.translatesAutoresizingMaskIntoConstraints = false 80 | stackView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 50).isActive = true 81 | let trailingConstraint = stackView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -50) 82 | trailingConstraint.priority = .defaultHigh 83 | trailingConstraint.isActive = true 84 | stackView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor).isActive = true 85 | } 86 | 87 | @objc private func didTapButton(_ sender: UIButton) { 88 | let hashValue = sender.tag 89 | guard let handler = buttonUUIDHashValuesToActions[hashValue] else { 90 | return 91 | } 92 | handler(self) 93 | } 94 | 95 | override var canBecomeFirstResponder: Bool { 96 | return true 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /UIKitForMacPlayground/HashableIdGenerator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HashableIdGenerator.swift 3 | // UIKitForMacPlayground 4 | // 5 | // Created by Noah Gilmore on 7/15/19. 6 | // Copyright © 2019 Noah Gilmore. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// Generates a process-unique stream of ints. Used for buttonn identification in ExamplesViewController. 12 | enum HashableIdGenerator { 13 | private static var nextId = 0 14 | 15 | static func next() -> Int { 16 | nextId += 1 17 | return nextId 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /UIKitForMacPlayground/HeaderView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HeaderView.swift 3 | // UIKitForMacPlayground 4 | // 5 | // Created by Noah Gilmore on 7/15/19. 6 | // Copyright © 2019 Noah Gilmore. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | final class HeaderView: UIView { 13 | init(text: String) { 14 | super.init(frame: .zero) 15 | 16 | let label = UILabel() 17 | label.textColor = .label 18 | label.text = text 19 | label.font = UIFont.preferredFont(forTextStyle: .largeTitle) 20 | label.translatesAutoresizingMaskIntoConstraints = false 21 | addSubview(label) 22 | label.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true 23 | label.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true 24 | label.topAnchor.constraint(equalTo: self.topAnchor, constant: 30).isActive = true 25 | label.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true 26 | } 27 | 28 | required init?(coder aDecoder: NSCoder) { 29 | fatalError("init(coder:) has not been implemented") 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /UIKitForMacPlayground/HoverExampleView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HoverExampleView.swift 3 | // UIKitForMacPlayground 4 | // 5 | // Created by Noah Gilmore on 7/15/19. 6 | // Copyright © 2019 Noah Gilmore. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | final class HoverColorView: ColorView { 13 | let hoverColor: UIColor 14 | let originalColor: UIColor 15 | 16 | init(color: UIColor, hoverColor: UIColor) { 17 | self.hoverColor = hoverColor 18 | self.originalColor = color 19 | 20 | super.init(color: color, dimension: 70) 21 | let recognizer = UIHoverGestureRecognizer(target: self, action: #selector(handleHover(_:))) 22 | self.addGestureRecognizer(recognizer) 23 | } 24 | 25 | required init?(coder aDecoder: NSCoder) { 26 | fatalError("init(coder:) has not been implemented") 27 | } 28 | 29 | @objc private func handleHover(_ sender: UIHoverGestureRecognizer) { 30 | switch sender.state { 31 | case .began, .changed: 32 | self.color = self.hoverColor 33 | case .ended: 34 | self.color = self.originalColor 35 | default: 36 | break 37 | } 38 | } 39 | } 40 | 41 | final class HoverExampleView: UIView { 42 | private let stackView: UIStackView = { 43 | let view = UIStackView() 44 | view.axis = .horizontal 45 | view.spacing = 40 46 | return view 47 | }() 48 | 49 | init() { 50 | super.init(frame: .zero) 51 | 52 | self.addSubview(stackView) 53 | 54 | for (color, hoverColor) in [(UIColor.red, UIColor.blue), (UIColor.blue, UIColor.green), (UIColor.green, UIColor.red)] { 55 | let colorView = HoverColorView(color: color, hoverColor: hoverColor) 56 | stackView.addArrangedSubview(colorView) 57 | } 58 | 59 | stackView.translatesAutoresizingMaskIntoConstraints = false 60 | stackView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true 61 | stackView.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true 62 | stackView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true 63 | stackView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true 64 | } 65 | 66 | required init?(coder aDecoder: NSCoder) { 67 | fatalError("init(coder:) has not been implemented") 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /UIKitForMacPlayground/HoverViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HoverViewController.swift 3 | // UIKitForMacPlayground 4 | // 5 | // Created by Noah Gilmore on 7/15/19. 6 | // Copyright © 2019 Noah Gilmore. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | class HoverViewController: ExamplesViewController { 13 | override func viewDidLoad() { 14 | self.views = [ 15 | .paragraph(text: "The following squares change color when you hover over them."), 16 | .view(view: HoverExampleView()) 17 | ] 18 | 19 | super.viewDidLoad() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitForMacPlayground/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | CatalystPlayground 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | NSUserActivityTypes 26 | 27 | com.noahgilmore.uikitformacplayground.square 28 | 29 | UIApplicationSceneManifest 30 | 31 | UIApplicationSupportsMultipleScenes 32 | 33 | 34 | UILaunchStoryboardName 35 | LaunchScreen 36 | UIRequiredDeviceCapabilities 37 | 38 | armv7 39 | 40 | UISupportedInterfaceOrientations 41 | 42 | UIInterfaceOrientationPortrait 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | UISupportedInterfaceOrientations~ipad 47 | 48 | UIInterfaceOrientationPortrait 49 | UIInterfaceOrientationPortraitUpsideDown 50 | UIInterfaceOrientationLandscapeLeft 51 | UIInterfaceOrientationLandscapeRight 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /UIKitForMacPlayground/ListViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ListViewController.swift 3 | // UIKitForMacPlayground 4 | // 5 | // Created by Noah Gilmore on 7/1/19. 6 | // Copyright © 2019 Noah Gilmore. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | enum Section { 13 | case main 14 | } 15 | 16 | final class ListViewController: UITableViewController { 17 | let didSelectDetailType: (DetailType) -> Void 18 | private var dataSource: UITableViewDiffableDataSource! 19 | 20 | private static var reuseIdentifier = "cell" 21 | 22 | init(didSelectDetailType: @escaping (DetailType) -> Void) { 23 | self.didSelectDetailType = didSelectDetailType 24 | super.init(style: .plain) 25 | self.title = "Examples" 26 | 27 | tableView.register(UITableViewCell.self, forCellReuseIdentifier: Self.reuseIdentifier) 28 | 29 | dataSource = UITableViewDiffableDataSource(tableView: self.tableView) { tableView, indexPath, detailType in 30 | let cell = tableView.dequeueReusableCell(withIdentifier: Self.reuseIdentifier)! 31 | cell.textLabel?.text = detailType.rawValue 32 | return cell 33 | } 34 | 35 | var snapshot = NSDiffableDataSourceSnapshot() 36 | snapshot.appendSections([.main]) 37 | snapshot.appendItems(DetailType.allCases) 38 | dataSource.apply(snapshot) 39 | self.tableView.delegate = self 40 | } 41 | 42 | required init?(coder aDecoder: NSCoder) { 43 | fatalError("init(coder:) has not been implemented") 44 | } 45 | 46 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 47 | tableView.deselectRow(at: indexPath, animated: true) 48 | guard let detailType = dataSource.itemIdentifier(for: indexPath) else { return } 49 | self.didSelectDetailType(detailType) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /UIKitForMacPlayground/NotificationsViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NotificationsViewController.swift 3 | // UIKitForMacPlayground 4 | // 5 | // Created by Noah Gilmore on 7/30/19. 6 | // Copyright © 2019 Noah Gilmore. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import UserNotifications 12 | 13 | class NotificationsViewController: ExamplesViewController { 14 | override func viewDidLoad() { 15 | self.views = [ 16 | .paragraph(text: "This button shows a local notification."), 17 | .button(text: "Show notification", color: .systemBlue, didTap: { _ in 18 | self.requestAndTriggerNotification() 19 | } 20 | ) 21 | ] 22 | 23 | super.viewDidLoad() 24 | } 25 | 26 | func requestAndTriggerNotification() { 27 | let center = UNUserNotificationCenter.current() 28 | // Request permission to display alerts and play sounds. 29 | // Note: if the app is in the foreground, this will trigger the AppDelegate's 30 | // UNUserNotificationCenterDelegate method, which will call the completionHandler 31 | // to display the default notification UI. If the app is in the background, the 32 | // default notification UI will be displayed without calling the AppDelegate 33 | center.requestAuthorization(options: [.badge, .sound, .alert]) { (granted, error) in 34 | if granted { 35 | self.triggerNotification() 36 | } 37 | } 38 | } 39 | 40 | func triggerNotification() { 41 | let content = UNMutableNotificationContent() 42 | content.title = "This is a notification" 43 | content.body = "This is the notification body" 44 | let uuidString = UUID().uuidString 45 | let request = UNNotificationRequest(identifier: uuidString, 46 | content: content, trigger: nil) 47 | 48 | // Schedule the request with the system. 49 | let notificationCenter = UNUserNotificationCenter.current() 50 | notificationCenter.add(request) { (error) in 51 | if let error = error { 52 | print("User notification error: \(error)") 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /UIKitForMacPlayground/OpenFileViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OpenFileViewController.swift 3 | // UIKitForMacPlayground 4 | // 5 | // Created by Noah Gilmore on 7/25/19. 6 | // Copyright © 2019 Noah Gilmore. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import MobileCoreServices 12 | 13 | class OpenFileViewController: ExamplesViewController { 14 | private lazy var imageView: UIImageView = { 15 | let view = UIImageView() 16 | view.contentMode = .scaleAspectFit 17 | view.widthAnchor.constraint(lessThanOrEqualToConstant: 300).isActive = true 18 | return view 19 | }() 20 | 21 | override func viewDidLoad() { 22 | self.views = [ 23 | .paragraph(text: "This button launches a file open panel for images. The chosen image will be displayed in an image view."), 24 | .button(text: "Open file", color: .systemBlue, didTap: { vc in 25 | let controller = UIDocumentPickerViewController(documentTypes: [(kUTTypeImage as String)], in: .import) 26 | controller.delegate = self 27 | vc.present(controller, animated: true, completion: nil) 28 | }), 29 | .view(view: imageView) 30 | ] 31 | 32 | super.viewDidLoad() 33 | } 34 | } 35 | 36 | extension OpenFileViewController: UIDocumentPickerDelegate { 37 | func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) { 38 | guard let url = urls.first else { return } 39 | guard let image = UIImage(contentsOfFile: url.path) else { return } 40 | imageView.image = image 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /UIKitForMacPlayground/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // UIKitForMacPlayground 4 | // 5 | // Created by Noah Gilmore on 6/27/19. 6 | // Copyright © 2019 Noah Gilmore. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | #if targetEnvironment(macCatalyst) 11 | import AppKit 12 | #endif 13 | import SwiftUI 14 | 15 | // Hacky stuff as per https://stackoverflow.com/questions/27243158/hiding-the-master-view-controller-with-uisplitviewcontroller-in-ios8 16 | extension UISplitViewController { 17 | func toggleMasterView() { 18 | let barButtonItem = self.displayModeButtonItem 19 | UIApplication.shared.sendAction(barButtonItem.action!, to: barButtonItem.target, from: nil, for: nil) 20 | } 21 | } 22 | 23 | class ReportingSplitViewController: UISplitViewController { 24 | override func viewDidAppear(_ animated: Bool) { 25 | super.viewDidAppear(animated) 26 | AppDelegate.shared.bridge?.sceneBecameActive(identifier: "normal") 27 | } 28 | } 29 | 30 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 31 | var window: UIWindow? 32 | 33 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 34 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 35 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 36 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 37 | guard let scene = (scene as? UIWindowScene) else { return } 38 | 39 | let splitViewController = ReportingSplitViewController() 40 | let listViewController = ListViewController(didSelectDetailType: { type in 41 | let viewController = self.viewController(forDetailType: type) 42 | viewController.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem 43 | viewController.navigationItem.leftItemsSupplementBackButton = true 44 | viewController.navigationItem.largeTitleDisplayMode = .never 45 | let detailNavController = UINavigationController(rootViewController: viewController) 46 | splitViewController.showDetailViewController(detailNavController, sender: self) 47 | if viewController.canBecomeFirstResponder { 48 | viewController.becomeFirstResponder() 49 | } 50 | guard let identifier = self.window?.windowScene?.session.persistentIdentifier else { return } 51 | NotificationCenter.default.post(name: .didChangeDetailType, object: nil, userInfo: ["detailType": type.rawValue, "identifier": identifier]) 52 | }) 53 | let navController = UINavigationController(rootViewController: listViewController) 54 | navController.navigationBar.prefersLargeTitles = true 55 | let viewController = ViewController() 56 | viewController.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem 57 | viewController.navigationItem.leftItemsSupplementBackButton = true 58 | viewController.navigationItem.largeTitleDisplayMode = .never 59 | let detailNavController = UINavigationController(rootViewController: viewController) 60 | splitViewController.viewControllers = [navController, detailNavController] 61 | splitViewController.delegate = self 62 | splitViewController.primaryBackgroundStyle = .sidebar 63 | 64 | window = UIWindow(windowScene: scene) 65 | window?.rootViewController = splitViewController 66 | window?.makeKeyAndVisible() 67 | 68 | #if targetEnvironment(macCatalyst) 69 | if let titlebar = scene.titlebar { 70 | let toolbar = NSToolbar(identifier: "toolbar") 71 | toolbar.delegate = self 72 | 73 | titlebar.toolbar = toolbar 74 | titlebar.titleVisibility = .hidden 75 | } 76 | #endif 77 | } 78 | 79 | func handleDetailType(_ detailType: DetailType) { 80 | guard let splitViewController = window?.rootViewController as? UISplitViewController else { return } 81 | guard let navigationController = splitViewController.viewControllers.first as? UINavigationController else { return } 82 | guard let listViewController = navigationController.topViewController as? ListViewController else { return } 83 | listViewController.didSelectDetailType(detailType) 84 | } 85 | 86 | func viewController(forDetailType type: DetailType) -> UIViewController { 87 | switch type { 88 | case .windows: 89 | return WindowsViewController() 90 | case .hover: 91 | return HoverViewController() 92 | case .dragAndDrop: 93 | return DragAndDropViewController() 94 | case .touchBar: 95 | return TouchBarViewController() 96 | case .contextMenus: 97 | return ContextMenuViewController() 98 | case .cursors: 99 | return CursorsViewController() 100 | case .swiftUI: 101 | return UIHostingController(rootView: SwiftUIView()) 102 | case .openFile: 103 | return OpenFileViewController() 104 | case .notifications: 105 | return NotificationsViewController() 106 | default: 107 | return ViewController() 108 | } 109 | } 110 | 111 | func sceneDidDisconnect(_ scene: UIScene) { 112 | // Called as the scene is being released by the system. 113 | // This occurs shortly after the scene enters the background, or when its session is discarded. 114 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 115 | // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead). 116 | } 117 | 118 | func sceneDidBecomeActive(_ scene: UIScene) { 119 | // Called when the scene has moved from an inactive state to an active state. 120 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 121 | } 122 | 123 | func sceneWillResignActive(_ scene: UIScene) { 124 | // Called when the scene will move from an active state to an inactive state. 125 | // This may occur due to temporary interruptions (ex. an incoming phone call). 126 | } 127 | 128 | func sceneWillEnterForeground(_ scene: UIScene) { 129 | // Called as the scene transitions from the background to the foreground. 130 | // Use this method to undo the changes made on entering the background. 131 | } 132 | 133 | func sceneDidEnterBackground(_ scene: UIScene) { 134 | // Called as the scene transitions from the foreground to the background. 135 | // Use this method to save data, release shared resources, and store enough scene-specific state information 136 | // to restore the scene back to its current state. 137 | } 138 | 139 | func windowScene(_ windowScene: UIWindowScene, didUpdate previousCoordinateSpace: UICoordinateSpace, interfaceOrientation previousInterfaceOrientation: UIInterfaceOrientation, traitCollection previousTraitCollection: UITraitCollection) { 140 | print("Old: \(previousCoordinateSpace.bounds), new: \(windowScene.coordinateSpace.bounds)") 141 | } 142 | } 143 | 144 | extension SceneDelegate: UISplitViewControllerDelegate { 145 | func splitViewController(_ splitViewController: UISplitViewController, collapseSecondary secondaryViewController: UIViewController, onto primaryViewController: UIViewController) -> Bool { 146 | return true 147 | } 148 | } 149 | 150 | #if targetEnvironment(macCatalyst) 151 | extension SceneDelegate: NSToolbarDelegate { 152 | var identifiers: [NSToolbarItem.Identifier] { 153 | return [NSToolbarItem.Identifier(rawValue: "test"), .flexibleSpace, NSToolbarItem.Identifier(rawValue: "other")] 154 | } 155 | 156 | func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] { 157 | return self.identifiers 158 | } 159 | 160 | func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] { 161 | return self.identifiers 162 | } 163 | 164 | func toolbar(_ toolbar: NSToolbar, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier, willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? { 165 | guard let identifier = self.window?.windowScene?.session.persistentIdentifier else { return nil } 166 | switch itemIdentifier.rawValue { 167 | case "test": 168 | let item = NSToolbarItem(itemIdentifier: itemIdentifier, barButtonItem: UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(didTapAddButton))) 169 | item.title = "New window" 170 | return item 171 | case "other": 172 | let item = AppDelegate.shared.bridge!.customToolbarItem(identifier: identifier) { string in 173 | guard let detailType = DetailType(rawValue: string) else { return } 174 | self.handleDetailType(detailType) 175 | } 176 | return item 177 | default: 178 | return nil 179 | } 180 | } 181 | 182 | @objc private func didTapAddButton(_ sender: NSToolbarItem) { 183 | UIApplication.shared.requestSceneSessionActivation(nil, userActivity: nil, options: nil, errorHandler: nil) 184 | } 185 | 186 | @objc private func didTapOtherButton(_ sender: NSToolbarItem) { 187 | UIApplication.shared.requestSceneSessionActivation(nil, userActivity: nil, options: nil, errorHandler: nil) 188 | } 189 | } 190 | #endif 191 | -------------------------------------------------------------------------------- /UIKitForMacPlayground/SquareSceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SquareSceneDelegate.swift 3 | // UIKitForMacPlayground 4 | // 5 | // Created by Noah Gilmore on 7/14/19. 6 | // Copyright © 2019 Noah Gilmore. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | class SquareSceneDelegate: UIResponder, UIWindowSceneDelegate { 13 | var window: UIWindow? 14 | 15 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 16 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 17 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 18 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 19 | guard let scene = (scene as? UIWindowScene) else { return } 20 | 21 | window = UIWindow(windowScene: scene) 22 | let vc = ViewController() 23 | if let vc = vc as? AppKitWindowReportingViewController { 24 | vc.identifier = "square" 25 | } 26 | window?.rootViewController = vc 27 | window?.makeKeyAndVisible() 28 | } 29 | 30 | func sceneDidDisconnect(_ scene: UIScene) { 31 | // Called as the scene is being released by the system. 32 | // This occurs shortly after the scene enters the background, or when its session is discarded. 33 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 34 | // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead). 35 | } 36 | 37 | func sceneDidBecomeActive(_ scene: UIScene) { 38 | // Called when the scene has moved from an inactive state to an active state. 39 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 40 | } 41 | 42 | func sceneWillResignActive(_ scene: UIScene) { 43 | // Called when the scene will move from an active state to an inactive state. 44 | // This may occur due to temporary interruptions (ex. an incoming phone call). 45 | } 46 | 47 | func sceneWillEnterForeground(_ scene: UIScene) { 48 | // Called as the scene transitions from the background to the foreground. 49 | // Use this method to undo the changes made on entering the background. 50 | } 51 | 52 | func sceneDidEnterBackground(_ scene: UIScene) { 53 | // Called as the scene transitions from the foreground to the background. 54 | // Use this method to save data, release shared resources, and store enough scene-specific state information 55 | // to restore the scene back to its current state. 56 | } 57 | 58 | func windowScene(_ windowScene: UIWindowScene, didUpdate previousCoordinateSpace: UICoordinateSpace, interfaceOrientation previousInterfaceOrientation: UIInterfaceOrientation, traitCollection previousTraitCollection: UITraitCollection) { 59 | print("Old: \(previousCoordinateSpace), new: \(windowScene.coordinateSpace)") 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /UIKitForMacPlayground/SwiftUIView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftUIView.swift 3 | // UIKitForMacPlayground 4 | // 5 | // Created by Noah Gilmore on 7/17/19. 6 | // Copyright © 2019 Noah Gilmore. All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | 11 | struct SwiftUIView: View { 12 | var body: some View { 13 | Text("This view is defined in SwiftUI.") 14 | .frame(width: 300, height: 300) 15 | .background(Color.pink) 16 | } 17 | } 18 | 19 | #if DEBUG 20 | struct SwiftUIView_Previews: PreviewProvider { 21 | static var previews: some View { 22 | SwiftUIView() 23 | } 24 | } 25 | #endif 26 | -------------------------------------------------------------------------------- /UIKitForMacPlayground/TouchBarViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TouchBarViewController.swift 3 | // UIKitForMacPlayground 4 | // 5 | // Created by Noah Gilmore on 7/16/19. 6 | // Copyright © 2019 Noah Gilmore. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | #if targetEnvironment(macCatalyst) 12 | import AppKit 13 | #endif 14 | 15 | final class TouchBarViewController: ExamplesViewController { 16 | let colorView = ColorView(color: .red, dimension: 100) 17 | 18 | override func viewDidLoad() { 19 | self.views = [ 20 | .paragraph(text: "This view controller provides touch bar items. Use them to modify the color of this square."), 21 | .view(view: colorView) 22 | ] 23 | super.viewDidLoad() 24 | } 25 | 26 | #if targetEnvironment(macCatalyst) 27 | override func makeTouchBar() -> NSTouchBar? { 28 | let bar = NSTouchBar() 29 | let buttonIdentifier = NSTouchBarItem.Identifier(rawValue: "clickme") 30 | let colorIdentifier = NSTouchBarItem.Identifier(rawValue: "color") 31 | bar.defaultItemIdentifiers = [ 32 | buttonIdentifier, 33 | colorIdentifier 34 | ] 35 | let colorPicker = NSColorPickerTouchBarItem.strokeColorPicker(withIdentifier: colorIdentifier) 36 | colorPicker.target = self 37 | colorPicker.action = #selector(didPickColor) 38 | bar.templateItems = [ 39 | NSButtonTouchBarItem( 40 | identifier: buttonIdentifier, 41 | title: "Random color", 42 | target: self, 43 | action: #selector(didTapRandomColor) 44 | ), 45 | colorPicker 46 | ] 47 | return bar 48 | } 49 | 50 | @objc private func didTapRandomColor(_ sender: NSTouchBarItem) { 51 | self.colorView.color = generateRandomColor() 52 | } 53 | 54 | @objc private func didPickColor(_ sender: NSColorPickerTouchBarItem) { 55 | self.colorView.color = sender.color 56 | } 57 | #endif 58 | } 59 | -------------------------------------------------------------------------------- /UIKitForMacPlayground/UIKitForMacPlayground-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | #import "AppKitBundleLoader.h" 6 | #import 7 | -------------------------------------------------------------------------------- /UIKitForMacPlayground/UIKitForMacPlayground.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.files.user-selected.read-write 8 | 9 | com.apple.security.network.client 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /UIKitForMacPlayground/UserActivities.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserActivities.swift 3 | // UIKitForMacPlayground 4 | // 5 | // Created by Noah Gilmore on 7/15/19. 6 | // Copyright © 2019 Noah Gilmore. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// NSUserActivity types supported by this app 12 | enum UserActivities: String { 13 | case square = "com.noahgilmore.uikitformacplayground.square" 14 | } 15 | -------------------------------------------------------------------------------- /UIKitForMacPlayground/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // UIKitForMacPlayground 4 | // 5 | // Created by Noah Gilmore on 6/27/19. 6 | // Copyright © 2019 Noah Gilmore. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | #if targetEnvironment(macCatalyst) 12 | import AppKit 13 | #endif 14 | 15 | func generateRandomColor() -> UIColor { 16 | let hue : CGFloat = CGFloat(arc4random() % 256) / 256 // use 256 to get full range from 0.0 to 1.0 17 | let saturation : CGFloat = CGFloat(arc4random() % 128) / 256 + 0.5 // from 0.5 to 1.0 to stay away from white 18 | let brightness : CGFloat = CGFloat(arc4random() % 128) / 256 + 0.5 // from 0.5 to 1.0 to stay away from black 19 | 20 | return UIColor(hue: hue, saturation: saturation, brightness: brightness, alpha: 1) 21 | } 22 | 23 | class ViewController: ExamplesViewController { 24 | override func viewDidLoad() { 25 | self.views = [ 26 | .paragraph(text: "Press Cmd-R to change the color of this view") 27 | ] 28 | super.viewDidLoad() 29 | // Do any additional setup after loading the view. 30 | self.view.backgroundColor = generateRandomColor() 31 | } 32 | 33 | override func viewDidAppear(_ animated: Bool) { 34 | super.viewDidAppear(animated) 35 | UIMenuSystem.main.setNeedsRebuild() 36 | } 37 | 38 | override var keyCommands: [UIKeyCommand]? { 39 | return [ 40 | UIKeyCommand( 41 | input: "R", 42 | modifierFlags: [.command], 43 | action: #selector(ViewController.testSelected(_:)) 44 | ) 45 | ] 46 | } 47 | 48 | override var canBecomeFirstResponder: Bool { 49 | return true 50 | } 51 | 52 | @objc private func testSelected(_ sender: AppDelegate) { 53 | self.view.backgroundColor = generateRandomColor() 54 | } 55 | 56 | override func buildMenu(with builder: UIMenuBuilder) { 57 | super.buildMenu(with: builder) 58 | 59 | let command = UIKeyCommand( 60 | input: "Y", 61 | modifierFlags: [.command], 62 | action: #selector(self.testSelected(_:)) 63 | ) 64 | command.title = "Change colors" 65 | 66 | let noShortcutCommand = UICommand( 67 | title: "No shortcut", 68 | image: nil, 69 | action: #selector(self.testSelected), 70 | propertyList: nil 71 | ) 72 | 73 | builder.insertChild(UIMenu( 74 | title: "Jump", 75 | image: nil, 76 | identifier: UIMenu.Identifier(rawValue: "com.noahgilmore.uikitformacplaygroud.changecolor"), 77 | options: [], 78 | children: [command, noShortcutCommand] 79 | ), atEndOfMenu: .file) 80 | } 81 | 82 | #if targetEnvironment(macCatalyst) 83 | override func makeTouchBar() -> NSTouchBar? { 84 | let bar = NSTouchBar() 85 | let identifier = NSTouchBarItem.Identifier(rawValue: "clickme") 86 | bar.defaultItemIdentifiers = [identifier] 87 | bar.templateItems = [ 88 | NSButtonTouchBarItem( 89 | identifier: identifier, 90 | title: "😥", 91 | target: self, 92 | action: #selector(didTapClickMe(_:)) 93 | ) 94 | ] 95 | return bar 96 | } 97 | 98 | @objc private func didTapClickMe(_ sender: NSTouchBarItem) { 99 | print("Click me!!!") 100 | } 101 | #endif 102 | } 103 | -------------------------------------------------------------------------------- /UIKitForMacPlayground/WindowsViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WindowsViewCotroller.swift 3 | // UIKitForMacPlayground 4 | // 5 | // Created by Noah Gilmore on 7/5/19. 6 | // Copyright © 2019 Noah Gilmore. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | class WindowsViewController: ExamplesViewController { 13 | override func viewDidLoad() { 14 | self.views = [ 15 | .paragraph(text: "You can jump to this page at any time using Command J or selecting Jump To -> Windows from the menu bar."), 16 | .header(text: "Creation/deletion"), 17 | .button(text: "Launch new window (Cmd-N equivalent)", color: .systemBlue, didTap: { _ in 18 | UIApplication.shared.requestSceneSessionActivation(nil, userActivity: nil, options: nil, errorHandler: nil) 19 | }), 20 | .button(text: "Close current window (Cmd-W equivalent)", color: .systemRed, didTap: { _ in 21 | guard let containingScene = self.view.window?.windowScene else { 22 | return 23 | } 24 | UIApplication.shared.requestSceneSessionDestruction( 25 | containingScene.session, 26 | options: nil, 27 | errorHandler: nil 28 | ) 29 | }), 30 | .header(text: "Different window types"), 31 | .button(text: "Launch a new single-color, 200x200, non-resizable window", color: .systemBlue, didTap: { _ in 32 | let activity = NSUserActivity(activityType: UserActivities.square.rawValue) 33 | UIApplication.shared.requestSceneSessionActivation(nil, userActivity: activity, options: nil, errorHandler: nil) 34 | }) 35 | ] 36 | 37 | if let bridge = AppDelegate.shared.bridge { 38 | self.views.append(contentsOf: [ 39 | .header(text: "Window management"), 40 | .button(text: "Move this window 50pt to the right", color: .systemBlue, didTap: { _ in 41 | bridge.moveWindowRight() 42 | }) 43 | ]) 44 | } 45 | 46 | super.viewDidLoad() 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /UIKitForMacPlaygroundSupportingBundle/AppKitPrincipal.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppKitPrincipal.swift 3 | // UIKitForMacPlaygroundSupportingBundle 4 | // 5 | // Created by Noah Gilmore on 7/15/19. 6 | // Copyright © 2019 Noah Gilmore. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import AppKit 11 | 12 | class AppKitPrincipal: NSObject, AppKitObjcBridge { 13 | private var bridge: UIKitBridge! = nil 14 | private let windowManager = WindowManager() 15 | 16 | func moveWindowRight() { 17 | let firstWindow = NSApplication.shared.windows.first! 18 | let currentFrame = firstWindow.frame 19 | let newFrame = currentFrame.offsetBy(dx: 50, dy: 0) 20 | firstWindow.setFrame(newFrame, display: false, animate: true) 21 | } 22 | 23 | func customToolbarItem(identifier: String, callback: @escaping (String) -> Void) -> NSToolbarItem { 24 | let item = DetailTypeToolbarItem(itemIdentifier: NSToolbarItem.Identifier(rawValue: identifier), handler: callback) 25 | return item 26 | } 27 | 28 | func setUIKit(_ bridge: UIKitBridge) { 29 | self.bridge = bridge 30 | } 31 | 32 | @objc private func didSelectFromPopup(_ sender: NSPopUpButton) { 33 | self.bridge.didSelectDetailType(sender.titleOfSelectedItem ?? "") 34 | } 35 | 36 | func setDefaultCursor() { 37 | NSCursor.arrow.set() 38 | } 39 | 40 | func setPointerCursor() { 41 | NSCursor.pointingHand.set() 42 | } 43 | 44 | func sceneBecameActive(identifier: String) { 45 | self.windowManager.sceneBecameActive(identifier: identifier) 46 | } 47 | } 48 | 49 | class DetailTypeToolbarItem: NSToolbarItem { 50 | private let handler: (String) -> Void 51 | 52 | init(itemIdentifier: NSToolbarItem.Identifier, handler: @escaping (String) -> Void) { 53 | self.handler = handler 54 | super.init(itemIdentifier: itemIdentifier) 55 | let button = NSPopUpButton() 56 | for type in DetailType.allCases { 57 | button.addItem(withTitle: type.rawValue) 58 | } 59 | button.target = self 60 | button.action = #selector(didSelectFromPopup) 61 | self.view = button 62 | 63 | NotificationCenter.default.addObserver(forName: Notification.Name.didChangeDetailType, object: nil, queue: nil, using: { notification in 64 | guard let detailTypeString = notification.userInfo?["detailType"] as? String else { 65 | return 66 | } 67 | guard let identifier = notification.userInfo?["identifier"] as? String else { 68 | return 69 | } 70 | guard identifier == self.itemIdentifier.rawValue else { return } 71 | guard let detailType = DetailType(rawValue: detailTypeString) else { return } 72 | self.setSelected(detailType) 73 | }) 74 | } 75 | 76 | func setSelected(_ detailType: DetailType) { 77 | guard let button = self.view as? NSPopUpButton else { 78 | return 79 | } 80 | button.selectItem(withTitle: detailType.rawValue) 81 | } 82 | 83 | @objc private func didSelectFromPopup(_ sender: NSPopUpButton) { 84 | guard let selectedTitle = sender.titleOfSelectedItem else { return } 85 | self.handler(selectedTitle) 86 | } 87 | } 88 | 89 | class RedView: NSView { 90 | override func draw(_ dirtyRect: NSRect) { 91 | super.draw(dirtyRect) 92 | NSColor.systemRed.setFill() 93 | dirtyRect.fill() 94 | } 95 | } 96 | 97 | /// TODO: This is a hacky way to track and get references to windows between UIKit and AppKit. There's probably a much better way. 98 | class WindowManager { 99 | private var windows: [Int: NSWindow] = [:] 100 | 101 | func sceneBecameActive(identifier: String) { 102 | var newWindows: [Int: NSWindow] = [:] 103 | for window in NSApplication.shared.windows { 104 | if !windows.keys.contains(window.windowNumber) { 105 | print("BECOMING ACTIVE: \(window.windowNumber), \(identifier)") 106 | self.handleWindowAppearance(identifier: identifier, window: window) 107 | } 108 | newWindows[window.windowNumber] = window 109 | } 110 | windows = newWindows 111 | } 112 | 113 | func handleWindowAppearance(identifier: String, window: NSWindow) { 114 | if identifier == "square" { 115 | window.setContentSize(NSSize(width: 200, height: 200)) 116 | window.styleMask.remove(.resizable) 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /UIKitForMacPlaygroundSupportingBundle/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | NSHumanReadableCopyright 22 | Copyright © 2019 Noah Gilmore. All rights reserved. 23 | NSPrincipalClass 24 | AppKitPrincipal 25 | 26 | 27 | -------------------------------------------------------------------------------- /UIKitForMacPlaygroundSupportingBundle/UIKitForMacPlaygroundSupportingBundle-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | #import "AppKitBundleLoader.h" 6 | 7 | --------------------------------------------------------------------------------