├── .gitignore ├── LICENSE ├── PLMenuBar.podspec ├── PLMenuBar.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── PLMenuBar ├── Info.plist ├── PLBackdropView │ ├── PLBackdropView.h │ ├── PLBackdropView.m │ ├── PLBackdropViewSettings.h │ ├── PLBackdropViewSettings.m │ ├── PLBackdropViewSettingsATVMenuDark.h │ ├── PLBackdropViewSettingsATVMenuDark.m │ ├── PLBackdropViewSettingsATVMenuLight.h │ └── PLBackdropViewSettingsATVMenuLight.m ├── PLMenuBar.h ├── PLMenuBarView.swift ├── PLMenuDetailComboRowView.swift ├── PLMenuDetailComboSectionView.swift ├── PLMenuDetailComboView.swift ├── PLMenuDetailDescView.swift ├── PLMenuDetailView.swift └── Resources │ └── button-check.png ├── PLMenuBarTests ├── Info.plist └── PLMenuBarTests.swift ├── PLMenuDemo ├── AppDelegate.swift ├── Assets.xcassets │ ├── App Icon & Top Shelf Image.brandassets │ │ ├── App Icon - Large.imagestack │ │ │ ├── Back.imagestacklayer │ │ │ │ ├── Content.imageset │ │ │ │ │ └── Contents.json │ │ │ │ └── Contents.json │ │ │ ├── Contents.json │ │ │ ├── Front.imagestacklayer │ │ │ │ ├── Content.imageset │ │ │ │ │ └── Contents.json │ │ │ │ └── Contents.json │ │ │ └── Middle.imagestacklayer │ │ │ │ ├── Content.imageset │ │ │ │ └── Contents.json │ │ │ │ └── Contents.json │ │ ├── App Icon - Small.imagestack │ │ │ ├── Back.imagestacklayer │ │ │ │ ├── Content.imageset │ │ │ │ │ └── Contents.json │ │ │ │ └── Contents.json │ │ │ ├── Contents.json │ │ │ ├── Front.imagestacklayer │ │ │ │ ├── Content.imageset │ │ │ │ │ └── Contents.json │ │ │ │ └── Contents.json │ │ │ └── Middle.imagestacklayer │ │ │ │ ├── Content.imageset │ │ │ │ └── Contents.json │ │ │ │ └── Contents.json │ │ ├── Contents.json │ │ └── Top Shelf Image.imageset │ │ │ └── Contents.json │ ├── Contents.json │ └── LaunchImage.launchimage │ │ └── Contents.json ├── Base.lproj │ └── Main.storyboard ├── Info.plist └── ViewController.swift └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata 19 | 20 | ## Other 21 | *.xccheckout 22 | *.moved-aside 23 | *.xcuserstate 24 | *.xcscmblueprint 25 | 26 | ## Obj-C/Swift specific 27 | *.hmap 28 | *.ipa 29 | 30 | ## Playgrounds 31 | timeline.xctimeline 32 | playground.xcworkspace 33 | 34 | # Swift Package Manager 35 | # 36 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 37 | # Packages/ 38 | .build/ 39 | 40 | # CocoaPods 41 | # 42 | # We recommend against adding the Pods directory to your .gitignore. However 43 | # you should judge for yourself, the pros and cons are mentioned at: 44 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 45 | # 46 | # Pods/ 47 | 48 | # Carthage 49 | # 50 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 51 | # Carthage/Checkouts 52 | 53 | Carthage/Build 54 | 55 | # fastlane 56 | # 57 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 58 | # screenshots whenever they are needed. 59 | # For more information about the recommended setup visit: 60 | # https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md 61 | 62 | fastlane/report.xml 63 | fastlane/screenshots 64 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 PatrickSCLIn 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 | -------------------------------------------------------------------------------- /PLMenuBar.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | 3 | s.name = "PLMenuBar" 4 | s.version = "0.0.1" 5 | s.summary = "PLMenuBar is a customized menubar for tvOS, simple and easy to use." 6 | s.homepage = "https://github.com/PatrickSCLin/PLMenuBar" 7 | s.license = { :type => 'MIT', :file => 'LICENSE' } 8 | s.author = { "Patrick Lin" => "patricksclin@gmail.com" } 9 | s.platform = :tvos, "9.0" 10 | s.source = { :git => "https://github.com/PatrickSCLin/PLMenuBar.git", :tag => "0.0.1" } 11 | s.source_files = Dir['PLMenuBar/*'] 12 | s.resources = "PLMenuBar/Resources/*.png" 13 | s.requires_arc = true 14 | 15 | end 16 | -------------------------------------------------------------------------------- /PLMenuBar.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | CD4785281CBE628E00DCA1D3 /* PLMenuDetailComboView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD4785271CBE628E00DCA1D3 /* PLMenuDetailComboView.swift */; }; 11 | CD47852A1CBE62C900DCA1D3 /* PLMenuDetailDescView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD4785291CBE62C900DCA1D3 /* PLMenuDetailDescView.swift */; }; 12 | CD47852C1CBE655900DCA1D3 /* PLMenuDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD47852B1CBE655900DCA1D3 /* PLMenuDetailView.swift */; }; 13 | CD72364F1DCA29A900AD3466 /* PLBackdropViewSettingsATVMenuDark.h in Headers */ = {isa = PBXBuildFile; fileRef = CD72364D1DCA29A900AD3466 /* PLBackdropViewSettingsATVMenuDark.h */; settings = {ATTRIBUTES = (Public, ); }; }; 14 | CD7236501DCA29A900AD3466 /* PLBackdropViewSettingsATVMenuDark.m in Sources */ = {isa = PBXBuildFile; fileRef = CD72364E1DCA29A900AD3466 /* PLBackdropViewSettingsATVMenuDark.m */; }; 15 | CD7C4FCA1CBDF5B600D84809 /* PLMenuBar.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD8C3CE61CBD39FB000D394F /* PLMenuBar.framework */; }; 16 | CD7C4FCB1CBDF5B600D84809 /* PLMenuBar.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CD8C3CE61CBD39FB000D394F /* PLMenuBar.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 17 | CD7C50511CBE1F7B00D84809 /* PLBackdropView.h in Headers */ = {isa = PBXBuildFile; fileRef = CD7C504B1CBE1F7B00D84809 /* PLBackdropView.h */; settings = {ATTRIBUTES = (Public, ); }; }; 18 | CD7C50521CBE1F7B00D84809 /* PLBackdropView.m in Sources */ = {isa = PBXBuildFile; fileRef = CD7C504C1CBE1F7B00D84809 /* PLBackdropView.m */; }; 19 | CD7C50531CBE1F7B00D84809 /* PLBackdropViewSettings.h in Headers */ = {isa = PBXBuildFile; fileRef = CD7C504D1CBE1F7B00D84809 /* PLBackdropViewSettings.h */; settings = {ATTRIBUTES = (Public, ); }; }; 20 | CD7C50541CBE1F7B00D84809 /* PLBackdropViewSettings.m in Sources */ = {isa = PBXBuildFile; fileRef = CD7C504E1CBE1F7B00D84809 /* PLBackdropViewSettings.m */; }; 21 | CD7C50551CBE1F7B00D84809 /* PLBackdropViewSettingsATVMenuLight.h in Headers */ = {isa = PBXBuildFile; fileRef = CD7C504F1CBE1F7B00D84809 /* PLBackdropViewSettingsATVMenuLight.h */; settings = {ATTRIBUTES = (Public, ); }; }; 22 | CD7C50561CBE1F7B00D84809 /* PLBackdropViewSettingsATVMenuLight.m in Sources */ = {isa = PBXBuildFile; fileRef = CD7C50501CBE1F7B00D84809 /* PLBackdropViewSettingsATVMenuLight.m */; }; 23 | CD8C3CEA1CBD39FB000D394F /* PLMenuBar.h in Headers */ = {isa = PBXBuildFile; fileRef = CD8C3CE91CBD39FB000D394F /* PLMenuBar.h */; settings = {ATTRIBUTES = (Public, ); }; }; 24 | CD8C3CF11CBD39FB000D394F /* PLMenuBar.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD8C3CE61CBD39FB000D394F /* PLMenuBar.framework */; }; 25 | CD8C3CF61CBD39FB000D394F /* PLMenuBarTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD8C3CF51CBD39FB000D394F /* PLMenuBarTests.swift */; }; 26 | CD8C3D071CBD3B24000D394F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD8C3D061CBD3B24000D394F /* AppDelegate.swift */; }; 27 | CD8C3D091CBD3B24000D394F /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD8C3D081CBD3B24000D394F /* ViewController.swift */; }; 28 | CD8C3D0C1CBD3B24000D394F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CD8C3D0A1CBD3B24000D394F /* Main.storyboard */; }; 29 | CD8C3D0E1CBD3B24000D394F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CD8C3D0D1CBD3B24000D394F /* Assets.xcassets */; }; 30 | CD8C3D141CBD3B95000D394F /* PLMenuBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD8C3D131CBD3B95000D394F /* PLMenuBarView.swift */; }; 31 | CDA34F931CBE9927003AD6F6 /* PLMenuDetailComboSectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDA34F921CBE9927003AD6F6 /* PLMenuDetailComboSectionView.swift */; }; 32 | CDA34F951CBEADAF003AD6F6 /* PLMenuDetailComboRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDA34F941CBEADAF003AD6F6 /* PLMenuDetailComboRowView.swift */; }; 33 | CDA34F981CBEB2E0003AD6F6 /* button-check.png in Resources */ = {isa = PBXBuildFile; fileRef = CDA34F971CBEB2E0003AD6F6 /* button-check.png */; }; 34 | /* End PBXBuildFile section */ 35 | 36 | /* Begin PBXContainerItemProxy section */ 37 | CD8C3CF21CBD39FB000D394F /* PBXContainerItemProxy */ = { 38 | isa = PBXContainerItemProxy; 39 | containerPortal = CD8C3CDD1CBD39FB000D394F /* Project object */; 40 | proxyType = 1; 41 | remoteGlobalIDString = CD8C3CE51CBD39FB000D394F; 42 | remoteInfo = PLMenuBar; 43 | }; 44 | CD8C3D151CBD3C8D000D394F /* PBXContainerItemProxy */ = { 45 | isa = PBXContainerItemProxy; 46 | containerPortal = CD8C3CDD1CBD39FB000D394F /* Project object */; 47 | proxyType = 1; 48 | remoteGlobalIDString = CD8C3CE51CBD39FB000D394F; 49 | remoteInfo = PLMenuBar; 50 | }; 51 | /* End PBXContainerItemProxy section */ 52 | 53 | /* Begin PBXCopyFilesBuildPhase section */ 54 | CD7C4FCC1CBDF5B600D84809 /* Embed Frameworks */ = { 55 | isa = PBXCopyFilesBuildPhase; 56 | buildActionMask = 2147483647; 57 | dstPath = ""; 58 | dstSubfolderSpec = 10; 59 | files = ( 60 | CD7C4FCB1CBDF5B600D84809 /* PLMenuBar.framework in Embed Frameworks */, 61 | ); 62 | name = "Embed Frameworks"; 63 | runOnlyForDeploymentPostprocessing = 0; 64 | }; 65 | /* End PBXCopyFilesBuildPhase section */ 66 | 67 | /* Begin PBXFileReference section */ 68 | CD4785271CBE628E00DCA1D3 /* PLMenuDetailComboView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PLMenuDetailComboView.swift; sourceTree = ""; }; 69 | CD4785291CBE62C900DCA1D3 /* PLMenuDetailDescView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PLMenuDetailDescView.swift; sourceTree = ""; }; 70 | CD47852B1CBE655900DCA1D3 /* PLMenuDetailView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PLMenuDetailView.swift; sourceTree = ""; }; 71 | CD72364D1DCA29A900AD3466 /* PLBackdropViewSettingsATVMenuDark.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PLBackdropViewSettingsATVMenuDark.h; sourceTree = ""; }; 72 | CD72364E1DCA29A900AD3466 /* PLBackdropViewSettingsATVMenuDark.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PLBackdropViewSettingsATVMenuDark.m; sourceTree = ""; }; 73 | CD7C504B1CBE1F7B00D84809 /* PLBackdropView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PLBackdropView.h; sourceTree = ""; }; 74 | CD7C504C1CBE1F7B00D84809 /* PLBackdropView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PLBackdropView.m; sourceTree = ""; }; 75 | CD7C504D1CBE1F7B00D84809 /* PLBackdropViewSettings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PLBackdropViewSettings.h; sourceTree = ""; }; 76 | CD7C504E1CBE1F7B00D84809 /* PLBackdropViewSettings.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PLBackdropViewSettings.m; sourceTree = ""; }; 77 | CD7C504F1CBE1F7B00D84809 /* PLBackdropViewSettingsATVMenuLight.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PLBackdropViewSettingsATVMenuLight.h; sourceTree = ""; }; 78 | CD7C50501CBE1F7B00D84809 /* PLBackdropViewSettingsATVMenuLight.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PLBackdropViewSettingsATVMenuLight.m; sourceTree = ""; }; 79 | CD8C3CE61CBD39FB000D394F /* PLMenuBar.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PLMenuBar.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 80 | CD8C3CE91CBD39FB000D394F /* PLMenuBar.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PLMenuBar.h; sourceTree = ""; }; 81 | CD8C3CEB1CBD39FB000D394F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 82 | CD8C3CF01CBD39FB000D394F /* PLMenuBarTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PLMenuBarTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 83 | CD8C3CF51CBD39FB000D394F /* PLMenuBarTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PLMenuBarTests.swift; sourceTree = ""; }; 84 | CD8C3CF71CBD39FB000D394F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 85 | CD8C3D041CBD3B24000D394F /* PLMenuDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PLMenuDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 86 | CD8C3D061CBD3B24000D394F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 87 | CD8C3D081CBD3B24000D394F /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 88 | CD8C3D0B1CBD3B24000D394F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 89 | CD8C3D0D1CBD3B24000D394F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 90 | CD8C3D0F1CBD3B24000D394F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 91 | CD8C3D131CBD3B95000D394F /* PLMenuBarView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PLMenuBarView.swift; sourceTree = ""; }; 92 | CDA34F921CBE9927003AD6F6 /* PLMenuDetailComboSectionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PLMenuDetailComboSectionView.swift; sourceTree = ""; }; 93 | CDA34F941CBEADAF003AD6F6 /* PLMenuDetailComboRowView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PLMenuDetailComboRowView.swift; sourceTree = ""; }; 94 | CDA34F971CBEB2E0003AD6F6 /* button-check.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "button-check.png"; sourceTree = ""; }; 95 | /* End PBXFileReference section */ 96 | 97 | /* Begin PBXFrameworksBuildPhase section */ 98 | CD8C3CE21CBD39FB000D394F /* Frameworks */ = { 99 | isa = PBXFrameworksBuildPhase; 100 | buildActionMask = 2147483647; 101 | files = ( 102 | ); 103 | runOnlyForDeploymentPostprocessing = 0; 104 | }; 105 | CD8C3CED1CBD39FB000D394F /* Frameworks */ = { 106 | isa = PBXFrameworksBuildPhase; 107 | buildActionMask = 2147483647; 108 | files = ( 109 | CD8C3CF11CBD39FB000D394F /* PLMenuBar.framework in Frameworks */, 110 | ); 111 | runOnlyForDeploymentPostprocessing = 0; 112 | }; 113 | CD8C3D011CBD3B24000D394F /* Frameworks */ = { 114 | isa = PBXFrameworksBuildPhase; 115 | buildActionMask = 2147483647; 116 | files = ( 117 | CD7C4FCA1CBDF5B600D84809 /* PLMenuBar.framework in Frameworks */, 118 | ); 119 | runOnlyForDeploymentPostprocessing = 0; 120 | }; 121 | /* End PBXFrameworksBuildPhase section */ 122 | 123 | /* Begin PBXGroup section */ 124 | CD7C504A1CBE1F7B00D84809 /* PLBackdropView */ = { 125 | isa = PBXGroup; 126 | children = ( 127 | CD7C504B1CBE1F7B00D84809 /* PLBackdropView.h */, 128 | CD7C504C1CBE1F7B00D84809 /* PLBackdropView.m */, 129 | CD7C504D1CBE1F7B00D84809 /* PLBackdropViewSettings.h */, 130 | CD7C504E1CBE1F7B00D84809 /* PLBackdropViewSettings.m */, 131 | CD7C504F1CBE1F7B00D84809 /* PLBackdropViewSettingsATVMenuLight.h */, 132 | CD7C50501CBE1F7B00D84809 /* PLBackdropViewSettingsATVMenuLight.m */, 133 | CD72364D1DCA29A900AD3466 /* PLBackdropViewSettingsATVMenuDark.h */, 134 | CD72364E1DCA29A900AD3466 /* PLBackdropViewSettingsATVMenuDark.m */, 135 | ); 136 | path = PLBackdropView; 137 | sourceTree = ""; 138 | }; 139 | CD8C3CDC1CBD39FB000D394F = { 140 | isa = PBXGroup; 141 | children = ( 142 | CD8C3CE81CBD39FB000D394F /* PLMenuBar */, 143 | CD8C3CF41CBD39FB000D394F /* PLMenuBarTests */, 144 | CD8C3D051CBD3B24000D394F /* PLMenuDemo */, 145 | CD8C3CE71CBD39FB000D394F /* Products */, 146 | ); 147 | sourceTree = ""; 148 | }; 149 | CD8C3CE71CBD39FB000D394F /* Products */ = { 150 | isa = PBXGroup; 151 | children = ( 152 | CD8C3CE61CBD39FB000D394F /* PLMenuBar.framework */, 153 | CD8C3CF01CBD39FB000D394F /* PLMenuBarTests.xctest */, 154 | CD8C3D041CBD3B24000D394F /* PLMenuDemo.app */, 155 | ); 156 | name = Products; 157 | sourceTree = ""; 158 | }; 159 | CD8C3CE81CBD39FB000D394F /* PLMenuBar */ = { 160 | isa = PBXGroup; 161 | children = ( 162 | CDA34F961CBEB2E0003AD6F6 /* Resources */, 163 | CD7C504A1CBE1F7B00D84809 /* PLBackdropView */, 164 | CD8C3CE91CBD39FB000D394F /* PLMenuBar.h */, 165 | CD8C3CEB1CBD39FB000D394F /* Info.plist */, 166 | CD8C3D131CBD3B95000D394F /* PLMenuBarView.swift */, 167 | CDA34F941CBEADAF003AD6F6 /* PLMenuDetailComboRowView.swift */, 168 | CDA34F921CBE9927003AD6F6 /* PLMenuDetailComboSectionView.swift */, 169 | CD4785271CBE628E00DCA1D3 /* PLMenuDetailComboView.swift */, 170 | CD4785291CBE62C900DCA1D3 /* PLMenuDetailDescView.swift */, 171 | CD47852B1CBE655900DCA1D3 /* PLMenuDetailView.swift */, 172 | ); 173 | path = PLMenuBar; 174 | sourceTree = ""; 175 | }; 176 | CD8C3CF41CBD39FB000D394F /* PLMenuBarTests */ = { 177 | isa = PBXGroup; 178 | children = ( 179 | CD8C3CF51CBD39FB000D394F /* PLMenuBarTests.swift */, 180 | CD8C3CF71CBD39FB000D394F /* Info.plist */, 181 | ); 182 | path = PLMenuBarTests; 183 | sourceTree = ""; 184 | }; 185 | CD8C3D051CBD3B24000D394F /* PLMenuDemo */ = { 186 | isa = PBXGroup; 187 | children = ( 188 | CD8C3D061CBD3B24000D394F /* AppDelegate.swift */, 189 | CD8C3D081CBD3B24000D394F /* ViewController.swift */, 190 | CD8C3D0A1CBD3B24000D394F /* Main.storyboard */, 191 | CD8C3D0D1CBD3B24000D394F /* Assets.xcassets */, 192 | CD8C3D0F1CBD3B24000D394F /* Info.plist */, 193 | ); 194 | path = PLMenuDemo; 195 | sourceTree = ""; 196 | }; 197 | CDA34F961CBEB2E0003AD6F6 /* Resources */ = { 198 | isa = PBXGroup; 199 | children = ( 200 | CDA34F971CBEB2E0003AD6F6 /* button-check.png */, 201 | ); 202 | path = Resources; 203 | sourceTree = ""; 204 | }; 205 | /* End PBXGroup section */ 206 | 207 | /* Begin PBXHeadersBuildPhase section */ 208 | CD8C3CE31CBD39FB000D394F /* Headers */ = { 209 | isa = PBXHeadersBuildPhase; 210 | buildActionMask = 2147483647; 211 | files = ( 212 | CD72364F1DCA29A900AD3466 /* PLBackdropViewSettingsATVMenuDark.h in Headers */, 213 | CD7C50551CBE1F7B00D84809 /* PLBackdropViewSettingsATVMenuLight.h in Headers */, 214 | CD8C3CEA1CBD39FB000D394F /* PLMenuBar.h in Headers */, 215 | CD7C50511CBE1F7B00D84809 /* PLBackdropView.h in Headers */, 216 | CD7C50531CBE1F7B00D84809 /* PLBackdropViewSettings.h in Headers */, 217 | ); 218 | runOnlyForDeploymentPostprocessing = 0; 219 | }; 220 | /* End PBXHeadersBuildPhase section */ 221 | 222 | /* Begin PBXNativeTarget section */ 223 | CD8C3CE51CBD39FB000D394F /* PLMenuBar */ = { 224 | isa = PBXNativeTarget; 225 | buildConfigurationList = CD8C3CFA1CBD39FB000D394F /* Build configuration list for PBXNativeTarget "PLMenuBar" */; 226 | buildPhases = ( 227 | CD8C3CE11CBD39FB000D394F /* Sources */, 228 | CD8C3CE21CBD39FB000D394F /* Frameworks */, 229 | CD8C3CE31CBD39FB000D394F /* Headers */, 230 | CD8C3CE41CBD39FB000D394F /* Resources */, 231 | ); 232 | buildRules = ( 233 | ); 234 | dependencies = ( 235 | ); 236 | name = PLMenuBar; 237 | productName = PLMenuBar; 238 | productReference = CD8C3CE61CBD39FB000D394F /* PLMenuBar.framework */; 239 | productType = "com.apple.product-type.framework"; 240 | }; 241 | CD8C3CEF1CBD39FB000D394F /* PLMenuBarTests */ = { 242 | isa = PBXNativeTarget; 243 | buildConfigurationList = CD8C3CFD1CBD39FB000D394F /* Build configuration list for PBXNativeTarget "PLMenuBarTests" */; 244 | buildPhases = ( 245 | CD8C3CEC1CBD39FB000D394F /* Sources */, 246 | CD8C3CED1CBD39FB000D394F /* Frameworks */, 247 | CD8C3CEE1CBD39FB000D394F /* Resources */, 248 | ); 249 | buildRules = ( 250 | ); 251 | dependencies = ( 252 | CD8C3CF31CBD39FB000D394F /* PBXTargetDependency */, 253 | ); 254 | name = PLMenuBarTests; 255 | productName = PLMenuBarTests; 256 | productReference = CD8C3CF01CBD39FB000D394F /* PLMenuBarTests.xctest */; 257 | productType = "com.apple.product-type.bundle.unit-test"; 258 | }; 259 | CD8C3D031CBD3B24000D394F /* PLMenuDemo */ = { 260 | isa = PBXNativeTarget; 261 | buildConfigurationList = CD8C3D101CBD3B24000D394F /* Build configuration list for PBXNativeTarget "PLMenuDemo" */; 262 | buildPhases = ( 263 | CD8C3D001CBD3B24000D394F /* Sources */, 264 | CD8C3D011CBD3B24000D394F /* Frameworks */, 265 | CD8C3D021CBD3B24000D394F /* Resources */, 266 | CD7C4FCC1CBDF5B600D84809 /* Embed Frameworks */, 267 | ); 268 | buildRules = ( 269 | ); 270 | dependencies = ( 271 | CD8C3D161CBD3C8D000D394F /* PBXTargetDependency */, 272 | ); 273 | name = PLMenuDemo; 274 | productName = PLMenuDemo; 275 | productReference = CD8C3D041CBD3B24000D394F /* PLMenuDemo.app */; 276 | productType = "com.apple.product-type.application"; 277 | }; 278 | /* End PBXNativeTarget section */ 279 | 280 | /* Begin PBXProject section */ 281 | CD8C3CDD1CBD39FB000D394F /* Project object */ = { 282 | isa = PBXProject; 283 | attributes = { 284 | LastSwiftUpdateCheck = 0720; 285 | LastUpgradeCheck = 0810; 286 | ORGANIZATIONNAME = "Patrick Lin"; 287 | TargetAttributes = { 288 | CD8C3CE51CBD39FB000D394F = { 289 | CreatedOnToolsVersion = 7.2.1; 290 | LastSwiftMigration = 0810; 291 | }; 292 | CD8C3CEF1CBD39FB000D394F = { 293 | CreatedOnToolsVersion = 7.2.1; 294 | }; 295 | CD8C3D031CBD3B24000D394F = { 296 | CreatedOnToolsVersion = 7.2.1; 297 | LastSwiftMigration = 0810; 298 | }; 299 | }; 300 | }; 301 | buildConfigurationList = CD8C3CE01CBD39FB000D394F /* Build configuration list for PBXProject "PLMenuBar" */; 302 | compatibilityVersion = "Xcode 3.2"; 303 | developmentRegion = English; 304 | hasScannedForEncodings = 0; 305 | knownRegions = ( 306 | en, 307 | Base, 308 | ); 309 | mainGroup = CD8C3CDC1CBD39FB000D394F; 310 | productRefGroup = CD8C3CE71CBD39FB000D394F /* Products */; 311 | projectDirPath = ""; 312 | projectRoot = ""; 313 | targets = ( 314 | CD8C3CE51CBD39FB000D394F /* PLMenuBar */, 315 | CD8C3CEF1CBD39FB000D394F /* PLMenuBarTests */, 316 | CD8C3D031CBD3B24000D394F /* PLMenuDemo */, 317 | ); 318 | }; 319 | /* End PBXProject section */ 320 | 321 | /* Begin PBXResourcesBuildPhase section */ 322 | CD8C3CE41CBD39FB000D394F /* Resources */ = { 323 | isa = PBXResourcesBuildPhase; 324 | buildActionMask = 2147483647; 325 | files = ( 326 | CDA34F981CBEB2E0003AD6F6 /* button-check.png in Resources */, 327 | ); 328 | runOnlyForDeploymentPostprocessing = 0; 329 | }; 330 | CD8C3CEE1CBD39FB000D394F /* Resources */ = { 331 | isa = PBXResourcesBuildPhase; 332 | buildActionMask = 2147483647; 333 | files = ( 334 | ); 335 | runOnlyForDeploymentPostprocessing = 0; 336 | }; 337 | CD8C3D021CBD3B24000D394F /* Resources */ = { 338 | isa = PBXResourcesBuildPhase; 339 | buildActionMask = 2147483647; 340 | files = ( 341 | CD8C3D0E1CBD3B24000D394F /* Assets.xcassets in Resources */, 342 | CD8C3D0C1CBD3B24000D394F /* Main.storyboard in Resources */, 343 | ); 344 | runOnlyForDeploymentPostprocessing = 0; 345 | }; 346 | /* End PBXResourcesBuildPhase section */ 347 | 348 | /* Begin PBXSourcesBuildPhase section */ 349 | CD8C3CE11CBD39FB000D394F /* Sources */ = { 350 | isa = PBXSourcesBuildPhase; 351 | buildActionMask = 2147483647; 352 | files = ( 353 | CDA34F951CBEADAF003AD6F6 /* PLMenuDetailComboRowView.swift in Sources */, 354 | CD7C50541CBE1F7B00D84809 /* PLBackdropViewSettings.m in Sources */, 355 | CD47852A1CBE62C900DCA1D3 /* PLMenuDetailDescView.swift in Sources */, 356 | CD7C50561CBE1F7B00D84809 /* PLBackdropViewSettingsATVMenuLight.m in Sources */, 357 | CD7236501DCA29A900AD3466 /* PLBackdropViewSettingsATVMenuDark.m in Sources */, 358 | CD7C50521CBE1F7B00D84809 /* PLBackdropView.m in Sources */, 359 | CD47852C1CBE655900DCA1D3 /* PLMenuDetailView.swift in Sources */, 360 | CDA34F931CBE9927003AD6F6 /* PLMenuDetailComboSectionView.swift in Sources */, 361 | CD8C3D141CBD3B95000D394F /* PLMenuBarView.swift in Sources */, 362 | CD4785281CBE628E00DCA1D3 /* PLMenuDetailComboView.swift in Sources */, 363 | ); 364 | runOnlyForDeploymentPostprocessing = 0; 365 | }; 366 | CD8C3CEC1CBD39FB000D394F /* Sources */ = { 367 | isa = PBXSourcesBuildPhase; 368 | buildActionMask = 2147483647; 369 | files = ( 370 | CD8C3CF61CBD39FB000D394F /* PLMenuBarTests.swift in Sources */, 371 | ); 372 | runOnlyForDeploymentPostprocessing = 0; 373 | }; 374 | CD8C3D001CBD3B24000D394F /* Sources */ = { 375 | isa = PBXSourcesBuildPhase; 376 | buildActionMask = 2147483647; 377 | files = ( 378 | CD8C3D091CBD3B24000D394F /* ViewController.swift in Sources */, 379 | CD8C3D071CBD3B24000D394F /* AppDelegate.swift in Sources */, 380 | ); 381 | runOnlyForDeploymentPostprocessing = 0; 382 | }; 383 | /* End PBXSourcesBuildPhase section */ 384 | 385 | /* Begin PBXTargetDependency section */ 386 | CD8C3CF31CBD39FB000D394F /* PBXTargetDependency */ = { 387 | isa = PBXTargetDependency; 388 | target = CD8C3CE51CBD39FB000D394F /* PLMenuBar */; 389 | targetProxy = CD8C3CF21CBD39FB000D394F /* PBXContainerItemProxy */; 390 | }; 391 | CD8C3D161CBD3C8D000D394F /* PBXTargetDependency */ = { 392 | isa = PBXTargetDependency; 393 | target = CD8C3CE51CBD39FB000D394F /* PLMenuBar */; 394 | targetProxy = CD8C3D151CBD3C8D000D394F /* PBXContainerItemProxy */; 395 | }; 396 | /* End PBXTargetDependency section */ 397 | 398 | /* Begin PBXVariantGroup section */ 399 | CD8C3D0A1CBD3B24000D394F /* Main.storyboard */ = { 400 | isa = PBXVariantGroup; 401 | children = ( 402 | CD8C3D0B1CBD3B24000D394F /* Base */, 403 | ); 404 | name = Main.storyboard; 405 | sourceTree = ""; 406 | }; 407 | /* End PBXVariantGroup section */ 408 | 409 | /* Begin XCBuildConfiguration section */ 410 | CD8C3CF81CBD39FB000D394F /* Debug */ = { 411 | isa = XCBuildConfiguration; 412 | buildSettings = { 413 | ALWAYS_SEARCH_USER_PATHS = NO; 414 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 415 | CLANG_CXX_LIBRARY = "libc++"; 416 | CLANG_ENABLE_MODULES = YES; 417 | CLANG_ENABLE_OBJC_ARC = YES; 418 | CLANG_WARN_BOOL_CONVERSION = YES; 419 | CLANG_WARN_CONSTANT_CONVERSION = YES; 420 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 421 | CLANG_WARN_EMPTY_BODY = YES; 422 | CLANG_WARN_ENUM_CONVERSION = YES; 423 | CLANG_WARN_INFINITE_RECURSION = YES; 424 | CLANG_WARN_INT_CONVERSION = YES; 425 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 426 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 427 | CLANG_WARN_UNREACHABLE_CODE = YES; 428 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 429 | COPY_PHASE_STRIP = NO; 430 | CURRENT_PROJECT_VERSION = 1; 431 | DEBUG_INFORMATION_FORMAT = dwarf; 432 | ENABLE_STRICT_OBJC_MSGSEND = YES; 433 | ENABLE_TESTABILITY = YES; 434 | GCC_C_LANGUAGE_STANDARD = gnu99; 435 | GCC_DYNAMIC_NO_PIC = NO; 436 | GCC_NO_COMMON_BLOCKS = YES; 437 | GCC_OPTIMIZATION_LEVEL = 0; 438 | GCC_PREPROCESSOR_DEFINITIONS = ( 439 | "DEBUG=1", 440 | "$(inherited)", 441 | ); 442 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 443 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 444 | GCC_WARN_UNDECLARED_SELECTOR = YES; 445 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 446 | GCC_WARN_UNUSED_FUNCTION = YES; 447 | GCC_WARN_UNUSED_VARIABLE = YES; 448 | MTL_ENABLE_DEBUG_INFO = YES; 449 | ONLY_ACTIVE_ARCH = YES; 450 | SDKROOT = appletvos; 451 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 452 | TARGETED_DEVICE_FAMILY = 3; 453 | TVOS_DEPLOYMENT_TARGET = 9.1; 454 | VERSIONING_SYSTEM = "apple-generic"; 455 | VERSION_INFO_PREFIX = ""; 456 | }; 457 | name = Debug; 458 | }; 459 | CD8C3CF91CBD39FB000D394F /* Release */ = { 460 | isa = XCBuildConfiguration; 461 | buildSettings = { 462 | ALWAYS_SEARCH_USER_PATHS = NO; 463 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 464 | CLANG_CXX_LIBRARY = "libc++"; 465 | CLANG_ENABLE_MODULES = YES; 466 | CLANG_ENABLE_OBJC_ARC = YES; 467 | CLANG_WARN_BOOL_CONVERSION = YES; 468 | CLANG_WARN_CONSTANT_CONVERSION = YES; 469 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 470 | CLANG_WARN_EMPTY_BODY = YES; 471 | CLANG_WARN_ENUM_CONVERSION = YES; 472 | CLANG_WARN_INFINITE_RECURSION = YES; 473 | CLANG_WARN_INT_CONVERSION = YES; 474 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 475 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 476 | CLANG_WARN_UNREACHABLE_CODE = YES; 477 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 478 | COPY_PHASE_STRIP = NO; 479 | CURRENT_PROJECT_VERSION = 1; 480 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 481 | ENABLE_NS_ASSERTIONS = NO; 482 | ENABLE_STRICT_OBJC_MSGSEND = YES; 483 | GCC_C_LANGUAGE_STANDARD = gnu99; 484 | GCC_NO_COMMON_BLOCKS = YES; 485 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 486 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 487 | GCC_WARN_UNDECLARED_SELECTOR = YES; 488 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 489 | GCC_WARN_UNUSED_FUNCTION = YES; 490 | GCC_WARN_UNUSED_VARIABLE = YES; 491 | MTL_ENABLE_DEBUG_INFO = NO; 492 | SDKROOT = appletvos; 493 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 494 | TARGETED_DEVICE_FAMILY = 3; 495 | TVOS_DEPLOYMENT_TARGET = 9.1; 496 | VALIDATE_PRODUCT = YES; 497 | VERSIONING_SYSTEM = "apple-generic"; 498 | VERSION_INFO_PREFIX = ""; 499 | }; 500 | name = Release; 501 | }; 502 | CD8C3CFB1CBD39FB000D394F /* Debug */ = { 503 | isa = XCBuildConfiguration; 504 | buildSettings = { 505 | CLANG_ENABLE_MODULES = YES; 506 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; 507 | DEFINES_MODULE = YES; 508 | DYLIB_COMPATIBILITY_VERSION = 1; 509 | DYLIB_CURRENT_VERSION = 1; 510 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 511 | INFOPLIST_FILE = PLMenuBar/Info.plist; 512 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 513 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 514 | PRODUCT_BUNDLE_IDENTIFIER = com.storeboot.PLMenuBar; 515 | PRODUCT_NAME = "$(TARGET_NAME)"; 516 | SKIP_INSTALL = YES; 517 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 518 | SWIFT_VERSION = 3.0; 519 | }; 520 | name = Debug; 521 | }; 522 | CD8C3CFC1CBD39FB000D394F /* Release */ = { 523 | isa = XCBuildConfiguration; 524 | buildSettings = { 525 | CLANG_ENABLE_MODULES = YES; 526 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; 527 | DEFINES_MODULE = YES; 528 | DYLIB_COMPATIBILITY_VERSION = 1; 529 | DYLIB_CURRENT_VERSION = 1; 530 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 531 | INFOPLIST_FILE = PLMenuBar/Info.plist; 532 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 533 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 534 | PRODUCT_BUNDLE_IDENTIFIER = com.storeboot.PLMenuBar; 535 | PRODUCT_NAME = "$(TARGET_NAME)"; 536 | SKIP_INSTALL = YES; 537 | SWIFT_VERSION = 3.0; 538 | }; 539 | name = Release; 540 | }; 541 | CD8C3CFE1CBD39FB000D394F /* Debug */ = { 542 | isa = XCBuildConfiguration; 543 | buildSettings = { 544 | INFOPLIST_FILE = PLMenuBarTests/Info.plist; 545 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 546 | PRODUCT_BUNDLE_IDENTIFIER = com.storeboot.PLMenuBarTests; 547 | PRODUCT_NAME = "$(TARGET_NAME)"; 548 | }; 549 | name = Debug; 550 | }; 551 | CD8C3CFF1CBD39FB000D394F /* Release */ = { 552 | isa = XCBuildConfiguration; 553 | buildSettings = { 554 | INFOPLIST_FILE = PLMenuBarTests/Info.plist; 555 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 556 | PRODUCT_BUNDLE_IDENTIFIER = com.storeboot.PLMenuBarTests; 557 | PRODUCT_NAME = "$(TARGET_NAME)"; 558 | }; 559 | name = Release; 560 | }; 561 | CD8C3D111CBD3B24000D394F /* Debug */ = { 562 | isa = XCBuildConfiguration; 563 | buildSettings = { 564 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 565 | ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; 566 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 567 | INFOPLIST_FILE = PLMenuDemo/Info.plist; 568 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 569 | PRODUCT_BUNDLE_IDENTIFIER = com.storeboot.PLMenuDemo; 570 | PRODUCT_NAME = "$(TARGET_NAME)"; 571 | SWIFT_VERSION = 3.0; 572 | }; 573 | name = Debug; 574 | }; 575 | CD8C3D121CBD3B24000D394F /* Release */ = { 576 | isa = XCBuildConfiguration; 577 | buildSettings = { 578 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 579 | ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; 580 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 581 | INFOPLIST_FILE = PLMenuDemo/Info.plist; 582 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 583 | PRODUCT_BUNDLE_IDENTIFIER = com.storeboot.PLMenuDemo; 584 | PRODUCT_NAME = "$(TARGET_NAME)"; 585 | SWIFT_VERSION = 3.0; 586 | }; 587 | name = Release; 588 | }; 589 | /* End XCBuildConfiguration section */ 590 | 591 | /* Begin XCConfigurationList section */ 592 | CD8C3CE01CBD39FB000D394F /* Build configuration list for PBXProject "PLMenuBar" */ = { 593 | isa = XCConfigurationList; 594 | buildConfigurations = ( 595 | CD8C3CF81CBD39FB000D394F /* Debug */, 596 | CD8C3CF91CBD39FB000D394F /* Release */, 597 | ); 598 | defaultConfigurationIsVisible = 0; 599 | defaultConfigurationName = Release; 600 | }; 601 | CD8C3CFA1CBD39FB000D394F /* Build configuration list for PBXNativeTarget "PLMenuBar" */ = { 602 | isa = XCConfigurationList; 603 | buildConfigurations = ( 604 | CD8C3CFB1CBD39FB000D394F /* Debug */, 605 | CD8C3CFC1CBD39FB000D394F /* Release */, 606 | ); 607 | defaultConfigurationIsVisible = 0; 608 | defaultConfigurationName = Release; 609 | }; 610 | CD8C3CFD1CBD39FB000D394F /* Build configuration list for PBXNativeTarget "PLMenuBarTests" */ = { 611 | isa = XCConfigurationList; 612 | buildConfigurations = ( 613 | CD8C3CFE1CBD39FB000D394F /* Debug */, 614 | CD8C3CFF1CBD39FB000D394F /* Release */, 615 | ); 616 | defaultConfigurationIsVisible = 0; 617 | defaultConfigurationName = Release; 618 | }; 619 | CD8C3D101CBD3B24000D394F /* Build configuration list for PBXNativeTarget "PLMenuDemo" */ = { 620 | isa = XCConfigurationList; 621 | buildConfigurations = ( 622 | CD8C3D111CBD3B24000D394F /* Debug */, 623 | CD8C3D121CBD3B24000D394F /* Release */, 624 | ); 625 | defaultConfigurationIsVisible = 0; 626 | defaultConfigurationName = Release; 627 | }; 628 | /* End XCConfigurationList section */ 629 | }; 630 | rootObject = CD8C3CDD1CBD39FB000D394F /* Project object */; 631 | } 632 | -------------------------------------------------------------------------------- /PLMenuBar.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /PLMenuBar/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /PLMenuBar/PLBackdropView/PLBackdropView.h: -------------------------------------------------------------------------------- 1 | // 2 | // PLBackdropView.h 3 | // PLMenuBar 4 | // 5 | // Created by Patrick Lin on 4/12/16. 6 | // Copyright © 2016 Patrick Lin. All rights reserved. 7 | // 8 | 9 | #include 10 | 11 | @class PLBackdropViewSettings; 12 | 13 | enum PLBackdropViewStyle 14 | { 15 | PLBackdropViewStyleLight = 0, 16 | PLBackdropViewStyleDark, 17 | }; 18 | 19 | @interface PLBackdropView : UIView 20 | { 21 | @public 22 | 23 | id _backdropView; 24 | 25 | PLBackdropViewSettings* _settings; 26 | } 27 | 28 | @property(nonatomic, assign) enum PLBackdropViewStyle style; 29 | 30 | - (id)initWithFrame:(CGRect)frame settings:(PLBackdropViewSettings *)settings; 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /PLMenuBar/PLBackdropView/PLBackdropView.m: -------------------------------------------------------------------------------- 1 | // 2 | // PLBackdropView.m 3 | // PLMenuBar 4 | // 5 | // Created by Patrick Lin on 4/12/16. 6 | // Copyright © 2016 Patrick Lin. All rights reserved. 7 | // 8 | 9 | #import "PLBackdropView.h" 10 | #import "PLBackdropViewSettings.h" 11 | #import "PLBackdropViewSettingsATVMenuLight.h" 12 | #import "PLBackdropViewSettingsATVMenuDark.h" 13 | 14 | @implementation PLBackdropView 15 | 16 | - (void)setStyle:(enum PLBackdropViewStyle)style 17 | { 18 | PLBackdropViewSettings* settings = nil; 19 | 20 | if (style == PLBackdropViewStyleLight) { settings = [[PLBackdropViewSettingsATVMenuLight alloc] init]; } 21 | 22 | else if (style == PLBackdropViewStyleDark) { settings = [[PLBackdropViewSettingsATVMenuDark alloc] init]; } 23 | 24 | if (settings != nil) { [self setupBackDropViewWithSettings:settings]; } 25 | } 26 | 27 | - (enum PLBackdropViewStyle)style 28 | { 29 | if ([_settings isKindOfClass: [PLBackdropViewSettingsATVMenuLight class]]) { return PLBackdropViewStyleLight; } 30 | 31 | else if ([_settings isKindOfClass: [PLBackdropViewSettingsATVMenuDark class]]) { return PLBackdropViewStyleDark; } 32 | 33 | return PLBackdropViewStyleLight; 34 | } 35 | 36 | - (void)setupBackDropViewWithSettings:(PLBackdropViewSettings *)settings 37 | { 38 | Class klass = NSClassFromString(@"_UIBackdropView"); 39 | 40 | id backdropView = [klass alloc]; 41 | 42 | CGRect bounds = self.bounds; 43 | 44 | id backdropViewSettings = settings->_backdropViewSettings; 45 | 46 | SEL sel = NSSelectorFromString(@"initWithFrame:settings:"); 47 | 48 | NSMethodSignature *msig = [backdropView methodSignatureForSelector:sel]; 49 | 50 | NSInvocation *inv = [NSInvocation invocationWithMethodSignature:msig]; 51 | 52 | [inv setSelector:sel]; 53 | 54 | [inv setTarget:backdropView]; 55 | 56 | [inv setArgument:&bounds atIndex:2]; 57 | 58 | [inv setArgument:&backdropViewSettings atIndex:3]; 59 | 60 | [inv invoke]; 61 | 62 | if (_backdropView == nil) { 63 | 64 | [self addSubview:backdropView]; 65 | 66 | } 67 | 68 | else { 69 | 70 | NSUInteger index = [self.subviews indexOfObject:_backdropView]; 71 | 72 | [_backdropView removeFromSuperview]; 73 | 74 | [self insertSubview:backdropView atIndex:index]; 75 | 76 | [backdropView setFrame:self.bounds]; 77 | 78 | } 79 | 80 | [backdropView setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight]; 81 | 82 | _backdropView = backdropView; 83 | 84 | _settings = settings; 85 | 86 | } 87 | 88 | - (id)initWithFrame:(CGRect)frame settings:(PLBackdropViewSettings *)settings 89 | { 90 | self = [super initWithFrame:frame]; 91 | 92 | if (!self) return nil; 93 | 94 | self.backgroundColor = [UIColor clearColor]; 95 | 96 | [self setupBackDropViewWithSettings: settings]; 97 | 98 | return self; 99 | } 100 | 101 | - (id)initWithFrame:(CGRect)frame 102 | { 103 | PLBackdropViewSettingsATVMenuLight *settings = [[PLBackdropViewSettingsATVMenuLight alloc] initWithDefaultValues]; 104 | 105 | self = [self initWithFrame:frame settings:settings]; 106 | 107 | if (!self) return nil; 108 | 109 | return self; 110 | } 111 | 112 | @end 113 | -------------------------------------------------------------------------------- /PLMenuBar/PLBackdropView/PLBackdropViewSettings.h: -------------------------------------------------------------------------------- 1 | // 2 | // PLBackdropViewSettings.h 3 | // PLMenuBar 4 | // 5 | // Created by Patrick Lin on 4/12/16. 6 | // Copyright © 2016 Patrick Lin. All rights reserved. 7 | // 8 | 9 | #include 10 | 11 | @interface PLBackdropViewSettings : NSObject 12 | { 13 | @public 14 | 15 | id _backdropViewSettings; 16 | } 17 | 18 | - (id)init; 19 | 20 | - (id)initWithDefaultValues; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /PLMenuBar/PLBackdropView/PLBackdropViewSettings.m: -------------------------------------------------------------------------------- 1 | // 2 | // PLBackdropViewSettings.m 3 | // PLMenuBar 4 | // 5 | // Created by Patrick Lin on 4/12/16. 6 | // Copyright © 2016 Patrick Lin. All rights reserved. 7 | // 8 | 9 | #import "PLBackdropViewSettings.h" 10 | 11 | @implementation PLBackdropViewSettings 12 | 13 | - (id)init 14 | { 15 | self = [super init]; 16 | 17 | if (!self) return nil; 18 | 19 | [self _configureBackdropViewSettings]; 20 | 21 | return self; 22 | } 23 | 24 | - (id)initWithDefaultValues 25 | { 26 | self = [super init]; 27 | 28 | if (!self) return nil; 29 | 30 | [self _configureBackdropViewSettingsWithDefaultValues]; 31 | 32 | return self; 33 | } 34 | 35 | - (Class)_wrappingClass 36 | { 37 | return NSClassFromString(@"_UIBackdropViewSettings"); 38 | } 39 | 40 | - (void)_configureBackdropViewSettings 41 | { 42 | Class klass = [self _wrappingClass]; 43 | 44 | #pragma clang diagnostic push 45 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 46 | 47 | SEL sel = NSSelectorFromString(@"init"); 48 | 49 | _backdropViewSettings = [[klass alloc] performSelector:sel]; 50 | 51 | #pragma clang diagnostic pop 52 | 53 | } 54 | 55 | - (void)_configureBackdropViewSettingsWithDefaultValues 56 | { 57 | Class klass = [self _wrappingClass]; 58 | 59 | #pragma clang diagnostic push 60 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 61 | 62 | SEL sel = NSSelectorFromString(@"initWithDefaultValues"); 63 | 64 | _backdropViewSettings = [[klass alloc] performSelector:sel]; 65 | 66 | #pragma clang diagnostic pop 67 | 68 | } 69 | 70 | @end 71 | -------------------------------------------------------------------------------- /PLMenuBar/PLBackdropView/PLBackdropViewSettingsATVMenuDark.h: -------------------------------------------------------------------------------- 1 | // 2 | // PLBackdropViewSettingsATVMenuDark.h 3 | // PLMenuBar 4 | // 5 | // Created by Patrick Lin on 02/11/2016. 6 | // Copyright © 2016 Patrick Lin. All rights reserved. 7 | // 8 | 9 | #import "PLBackdropViewSettings.h" 10 | 11 | @interface PLBackdropViewSettingsATVMenuDark : PLBackdropViewSettings 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /PLMenuBar/PLBackdropView/PLBackdropViewSettingsATVMenuDark.m: -------------------------------------------------------------------------------- 1 | // 2 | // PLBackdropViewSettingsATVMenuDark.m 3 | // PLMenuBar 4 | // 5 | // Created by Patrick Lin on 02/11/2016. 6 | // Copyright © 2016 Patrick Lin. All rights reserved. 7 | // 8 | 9 | #import "PLBackdropViewSettingsATVMenuDark.h" 10 | 11 | @implementation PLBackdropViewSettingsATVMenuDark 12 | 13 | - (Class)_wrappingClass 14 | { 15 | return NSClassFromString(@"_UIBackdropViewSettingsATVMenuDark"); 16 | } 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /PLMenuBar/PLBackdropView/PLBackdropViewSettingsATVMenuLight.h: -------------------------------------------------------------------------------- 1 | // 2 | // PLBackdropViewSettingsATVMenuLight.h 3 | // PLMenuBar 4 | // 5 | // Created by Patrick Lin on 4/12/16. 6 | // Copyright © 2016 Patrick Lin. All rights reserved. 7 | // 8 | 9 | #import "PLBackdropViewSettings.h" 10 | 11 | @interface PLBackdropViewSettingsATVMenuLight : PLBackdropViewSettings 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /PLMenuBar/PLBackdropView/PLBackdropViewSettingsATVMenuLight.m: -------------------------------------------------------------------------------- 1 | // 2 | // PLBackdropViewSettingsATVMenuLight.m 3 | // PLMenuBar 4 | // 5 | // Created by Patrick Lin on 4/12/16. 6 | // Copyright © 2016 Patrick Lin. All rights reserved. 7 | // 8 | 9 | #import "PLBackdropViewSettingsATVMenuLight.h" 10 | 11 | @implementation PLBackdropViewSettingsATVMenuLight 12 | 13 | - (Class)_wrappingClass 14 | { 15 | return NSClassFromString(@"_UIBackdropViewSettingsATVMenuLight"); 16 | } 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /PLMenuBar/PLMenuBar.h: -------------------------------------------------------------------------------- 1 | // 2 | // PLMenuBar.h 3 | // PLMenuBar 4 | // 5 | // Created by Patrick Lin on 4/12/16. 6 | // Copyright © 2016 Patrick Lin. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for PLMenuBar. 12 | FOUNDATION_EXPORT double PLMenuBarVersionNumber; 13 | 14 | //! Project version string for PLMenuBar. 15 | FOUNDATION_EXPORT const unsigned char PLMenuBarVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | #import 20 | #import 21 | #import 22 | #import 23 | -------------------------------------------------------------------------------- /PLMenuBar/PLMenuBarView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PLMenuBarView.swift 3 | // PLMenuBar 4 | // 5 | // Created by Patrick Lin on 4/12/16. 6 | // Copyright © 2016 Patrick Lin. All rights reserved. 7 | // 8 | 9 | @objc public protocol PLMenuBarDelegate: NSObjectProtocol { 10 | 11 | func numberOfItemsInMenubar() -> Int 12 | 13 | func menuBar(_ menuBar: PLMenuBarView, titleForItemAtIndex index: Int) -> String 14 | 15 | @objc optional func menuBar(_ menuBar: PLMenuBarView, detailItemForItemAtIndex index: Int) -> PLMenuDetailItem 16 | 17 | @objc optional func menuBar(_ menuBar: PLMenuBarView, didSelectItemAtIndex index: Int) 18 | 19 | @objc optional func menuBar(_ menuBar: PLMenuBarView, didSelectDetailAtRow row: Int, Section section: Int, forItemAtIndex index: Int) 20 | 21 | } 22 | 23 | public enum PLMenuBarViewStyle: UInt32 { 24 | 25 | case Light, Dark 26 | 27 | } 28 | 29 | open class PLMenuComboSection: NSObject { 30 | 31 | open var preferredIndex: Int = -1 32 | 33 | open var title: String = "" 34 | 35 | open var items: [String] = [String]() 36 | 37 | public override init() { 38 | 39 | super.init() 40 | 41 | } 42 | 43 | public init(title: String = "", items: [String], preferredIndex: Int = -1) { 44 | 45 | self.title.append(title) 46 | 47 | self.items.append(contentsOf: items) 48 | 49 | self.preferredIndex = preferredIndex 50 | 51 | } 52 | 53 | } 54 | 55 | open class PLMenuDetailComboItem: PLMenuDetailItem { 56 | 57 | open var items: [PLMenuComboSection] = [PLMenuComboSection]() 58 | 59 | public init(title: String = "", items: [PLMenuComboSection]) { 60 | 61 | super.init(title: title) 62 | 63 | self.items.append(contentsOf: items) 64 | 65 | } 66 | 67 | } 68 | 69 | open class PLMenuDetailDescItem: PLMenuDetailItem { 70 | 71 | open var text: String = "" 72 | 73 | public init(title: String = "", text: String = "") { 74 | 75 | super.init(title: title) 76 | 77 | self.text.append(text) 78 | 79 | } 80 | 81 | } 82 | 83 | open class PLMenuDetailItem: NSObject { 84 | 85 | open var title: String = "" 86 | 87 | public init(title: String = "") { 88 | 89 | self.title.append(title) 90 | 91 | } 92 | 93 | } 94 | 95 | open class PLMenuBarView: UIView, UITabBarDelegate, UITableViewDelegate, PLMenuDetailComboViewDelegate { 96 | 97 | static let MenuBarMinHeight: CGFloat = 140 98 | 99 | static let MenuBarDetailMinHeight: CGFloat = 210 100 | 101 | static let MenuBarDetailPadding: CGFloat = 150 102 | 103 | static let MenuBarBorderHeight: CGFloat = 1 104 | 105 | fileprivate var selectedIndexOfItem: Int = -1 106 | 107 | fileprivate var shouldShowDetailView: Bool = false 108 | 109 | fileprivate var guides: [UIFocusGuide] = [UIFocusGuide]() 110 | 111 | fileprivate var menuBar: UITabBar! 112 | 113 | fileprivate var detailView: PLBackdropView! 114 | 115 | fileprivate var borderView: UIView! 116 | 117 | fileprivate var contentView: PLMenuDetailView? 118 | 119 | open var style: PLMenuBarViewStyle = .Light { 120 | didSet 121 | { 122 | if oldValue == self.style { return } 123 | 124 | self.updateStyle() 125 | } 126 | } 127 | 128 | open var delegate: PLMenuBarDelegate? 129 | 130 | // MARK: Combo Delegate Methods 131 | 132 | open func combo(_ combo: PLMenuDetailComboView, didChangeValueAtSection section: Int, Row row: Int) { 133 | 134 | self.delegate?.menuBar?(self, didSelectDetailAtRow: row, Section: section, forItemAtIndex: (self.menuBar.selectedItem?.tag)!) 135 | 136 | } 137 | 138 | // MARK: Tabbar Delegate Methods 139 | 140 | open func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) { 141 | 142 | let index = item.tag 143 | 144 | if self.delegate == nil || item.tag == self.selectedIndexOfItem { return } 145 | 146 | self.selectedIndexOfItem = item.tag 147 | 148 | let detailItem = self.delegate!.menuBar?(self, detailItemForItemAtIndex: index) 149 | 150 | if detailItem != nil && ((detailItem! is PLMenuDetailDescItem) || (detailItem! is PLMenuDetailComboItem)) { 151 | 152 | for (_, guide) in self.guides.enumerated() { 153 | 154 | guide.owningView?.removeLayoutGuide(guide) 155 | 156 | } 157 | 158 | self.guides.removeAll() 159 | 160 | if self.contentView != nil { 161 | 162 | self.contentView?.removeFromSuperview() 163 | 164 | self.contentView = nil 165 | 166 | } 167 | 168 | let contentFrame = CGRect(x: PLMenuBarView.MenuBarDetailPadding, y: 0, width: self.detailView.bounds.size.width - (PLMenuBarView.MenuBarDetailPadding * 2), height: PLMenuBarView.MenuBarDetailMinHeight) 169 | 170 | if detailItem is PLMenuDetailDescItem { 171 | 172 | self.contentView = PLMenuDetailDescView(text: (detailItem as! PLMenuDetailDescItem).text) 173 | 174 | self.contentView!.frame = contentFrame 175 | 176 | self.detailView.addSubview(self.contentView!) 177 | 178 | } 179 | 180 | else if detailItem is PLMenuDetailComboItem { 181 | 182 | self.contentView = PLMenuDetailComboView(items: (detailItem as! PLMenuDetailComboItem).items) 183 | 184 | (self.contentView as! PLMenuDetailComboView).delegate = self 185 | 186 | self.contentView!.frame = contentFrame 187 | 188 | self.detailView.addSubview(self.contentView!) 189 | 190 | for (_, sectionView) in self.contentView!.contentViews.enumerated() { 191 | 192 | for (_, rowView) in (sectionView as! PLMenuDetailComboSectionView).rowViews.enumerated() { 193 | 194 | let guide = UIFocusGuide() 195 | 196 | self.addLayoutGuide(guide) 197 | 198 | guide.preferredFocusedView = rowView.contentBtn 199 | 200 | guide.topAnchor.constraint(equalTo: rowView.contentBtn.topAnchor).isActive = true 201 | 202 | guide.leftAnchor.constraint(equalTo: rowView.contentBtn.leftAnchor).isActive = true 203 | 204 | guide.widthAnchor.constraint(equalTo: rowView.contentBtn.widthAnchor).isActive = true 205 | 206 | guide.heightAnchor.constraint(equalTo: rowView.contentBtn.heightAnchor).isActive = true 207 | 208 | self.guides.append(guide) 209 | 210 | } 211 | 212 | } 213 | 214 | } 215 | 216 | self.shouldShowDetailView = true 217 | 218 | self.frame = CGRect(x: 0, y: 0, width: (self.superview?.frame.size.width)!, height: PLMenuBarView.MenuBarMinHeight + PLMenuBarView.MenuBarDetailMinHeight) 219 | 220 | } 221 | 222 | else { 223 | 224 | self.shouldShowDetailView = false 225 | 226 | self.frame = CGRect(x: 0, y: 0, width: (self.superview?.frame.size.width)!, height: PLMenuBarView.MenuBarMinHeight) 227 | 228 | } 229 | 230 | } 231 | 232 | // MARK: Public Methods 233 | 234 | open override func layoutSubviews() { 235 | 236 | super.layoutSubviews() 237 | 238 | if PLMenuBarViewStyle(rawValue: self.detailView.style.rawValue) != self.style { 239 | 240 | self.detailView.style = PLBackdropViewStyle(rawValue: self.style.rawValue) 241 | 242 | } 243 | 244 | if self.shouldShowDetailView == true { 245 | 246 | self.borderView.isHidden = false 247 | 248 | self.contentView?.alpha = 0 249 | 250 | UIView.animate(withDuration: 0.3, delay: 0, options: UIViewAnimationOptions.curveEaseIn, animations: { 251 | 252 | self.menuBar.frame = CGRect(x: 0, y: 0, width: self.frame.size.width, height: PLMenuBarView.MenuBarMinHeight) 253 | 254 | self.detailView.frame = CGRect(x: 0, y: PLMenuBarView.MenuBarMinHeight, width: self.frame.size.width, height: PLMenuBarView.MenuBarDetailMinHeight) 255 | 256 | self.contentView?.alpha = 1 257 | 258 | self.borderView.frame = CGRect(x: 0, y: PLMenuBarView.MenuBarMinHeight, width: self.frame.size.width, height: PLMenuBarView.MenuBarBorderHeight) 259 | 260 | self.borderView.alpha = 1 261 | 262 | }, completion: { (isCompleted: Bool) in 263 | 264 | 265 | 266 | }) 267 | 268 | } 269 | 270 | else { 271 | 272 | UIView.animate(withDuration: 0.3, delay: 0, options: UIViewAnimationOptions.curveEaseIn, animations: { 273 | 274 | self.menuBar.frame = CGRect(x: 0, y: 0, width: self.frame.size.width, height: PLMenuBarView.MenuBarMinHeight) 275 | 276 | self.detailView.frame = CGRect(x: 0, y: PLMenuBarView.MenuBarMinHeight, width: self.frame.size.width, height: 0) 277 | 278 | self.contentView?.alpha = 0 279 | 280 | self.borderView.frame = CGRect(x: 0, y: PLMenuBarView.MenuBarMinHeight, width: self.frame.size.width, height: PLMenuBarView.MenuBarBorderHeight) 281 | 282 | self.borderView.alpha = 0 283 | 284 | }, completion: { (isCompleted: Bool) in 285 | 286 | self.borderView.isHidden = true 287 | 288 | }) 289 | 290 | } 291 | 292 | } 293 | 294 | open override func willMove(toSuperview newSuperview: UIView?) { 295 | 296 | super.willMove(toSuperview: newSuperview) 297 | 298 | if newSuperview != nil { 299 | 300 | self.frame = CGRect(x: 0, y: 0, width: newSuperview!.frame.size.width, height: PLMenuBarView.MenuBarMinHeight) 301 | 302 | if delegate != nil { 303 | 304 | var items: [UITabBarItem] = [UITabBarItem]() 305 | 306 | let countOfItems = self.delegate!.numberOfItemsInMenubar() 307 | 308 | for i in 0.. 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /PLMenuBarTests/PLMenuBarTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PLMenuBarTests.swift 3 | // PLMenuBarTests 4 | // 5 | // Created by Patrick Lin on 4/12/16. 6 | // Copyright © 2016 Patrick Lin. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import PLMenuBar 11 | 12 | class PLMenuBarTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | // Use XCTAssert and related functions to verify your tests produce the correct results. 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measureBlock { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /PLMenuDemo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // PLMenuDemo 4 | // 5 | // Created by Patrick Lin on 4/12/16. 6 | // Copyright © 2016 Patrick Lin. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /PLMenuDemo/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv", 5 | "scale" : "1x" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /PLMenuDemo/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /PLMenuDemo/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "layers" : [ 3 | { 4 | "filename" : "Front.imagestacklayer" 5 | }, 6 | { 7 | "filename" : "Middle.imagestacklayer" 8 | }, 9 | { 10 | "filename" : "Back.imagestacklayer" 11 | } 12 | ], 13 | "info" : { 14 | "version" : 1, 15 | "author" : "xcode" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /PLMenuDemo/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv", 5 | "scale" : "1x" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /PLMenuDemo/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /PLMenuDemo/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv", 5 | "scale" : "1x" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /PLMenuDemo/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /PLMenuDemo/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv", 5 | "scale" : "1x" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /PLMenuDemo/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /PLMenuDemo/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "layers" : [ 3 | { 4 | "filename" : "Front.imagestacklayer" 5 | }, 6 | { 7 | "filename" : "Middle.imagestacklayer" 8 | }, 9 | { 10 | "filename" : "Back.imagestacklayer" 11 | } 12 | ], 13 | "info" : { 14 | "version" : 1, 15 | "author" : "xcode" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /PLMenuDemo/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv", 5 | "scale" : "1x" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /PLMenuDemo/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /PLMenuDemo/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv", 5 | "scale" : "1x" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /PLMenuDemo/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /PLMenuDemo/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "assets" : [ 3 | { 4 | "size" : "1280x768", 5 | "idiom" : "tv", 6 | "filename" : "App Icon - Large.imagestack", 7 | "role" : "primary-app-icon" 8 | }, 9 | { 10 | "size" : "400x240", 11 | "idiom" : "tv", 12 | "filename" : "App Icon - Small.imagestack", 13 | "role" : "primary-app-icon" 14 | }, 15 | { 16 | "size" : "1920x720", 17 | "idiom" : "tv", 18 | "filename" : "Top Shelf Image.imageset", 19 | "role" : "top-shelf-image" 20 | } 21 | ], 22 | "info" : { 23 | "version" : 1, 24 | "author" : "xcode" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /PLMenuDemo/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv", 5 | "scale" : "1x" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /PLMenuDemo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /PLMenuDemo/Assets.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "landscape", 5 | "idiom" : "tv", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "9.0", 8 | "scale" : "1x" 9 | } 10 | ], 11 | "info" : { 12 | "version" : 1, 13 | "author" : "xcode" 14 | } 15 | } -------------------------------------------------------------------------------- /PLMenuDemo/Base.lproj/Main.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 | -------------------------------------------------------------------------------- /PLMenuDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | arm64 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /PLMenuDemo/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // PLMenuDemo 4 | // 5 | // Created by Patrick Lin on 4/12/16. 6 | // Copyright © 2016 Patrick Lin. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import PLMenuBar 11 | 12 | class ViewController: UIViewController, PLMenuBarDelegate { 13 | 14 | var menuBar: PLMenuBarView! 15 | 16 | var menuDetailItems: [PLMenuDetailItem]! 17 | 18 | override var preferredFocusedView: UIView? 19 | { 20 | get 21 | { 22 | return (self.menuBar.isHidden == true) ? self.view : nil 23 | } 24 | } 25 | 26 | // MARK: PLMenuBar Delegate Methods 27 | 28 | func numberOfItemsInMenubar() -> Int { 29 | 30 | return self.menuDetailItems.count 31 | 32 | } 33 | 34 | func menuBar(_ menuBar: PLMenuBarView, titleForItemAtIndex index: Int) -> String { 35 | 36 | return self.menuDetailItems[index].title 37 | 38 | } 39 | 40 | func menuBar(_ menuBar: PLMenuBarView, detailItemForItemAtIndex index: Int) -> PLMenuDetailItem { 41 | 42 | return self.menuDetailItems[index] 43 | 44 | } 45 | 46 | func menuBar(_ menuBar: PLMenuBarView, didSelectDetailAtRow row: Int, Section section: Int, forItemAtIndex index: Int) { 47 | 48 | print("index: \(index), section: \(section), row: \(row)") 49 | 50 | ((self.menuDetailItems[index] as! PLMenuDetailComboItem).items[section]).preferredIndex = row 51 | 52 | } 53 | 54 | // MARK: Gesture Methods 55 | 56 | func swipe(_ gesture: UISwipeGestureRecognizer) { 57 | 58 | if gesture.direction == UISwipeGestureRecognizerDirection.up { 59 | 60 | if UIScreen.main.focusedView != nil && NSStringFromClass(UIScreen.main.focusedView!.classForCoder) == "UITabBarButton" { 61 | 62 | UIView.animate(withDuration: 0.3, delay: 0, options: UIViewAnimationOptions.beginFromCurrentState, animations: { 63 | 64 | self.menuBar.alpha = 0 65 | 66 | self.menuBar.frame = CGRect(x: 0, y: self.menuBar.bounds.size.height * -1, width: self.menuBar.bounds.size.width, height: self.menuBar.bounds.size.height) 67 | 68 | }, completion: { (isCompleted: Bool) in 69 | 70 | self.menuBar.isHidden = true 71 | 72 | self.setNeedsFocusUpdate() 73 | 74 | self.updateFocusIfNeeded() 75 | 76 | }) 77 | 78 | } 79 | 80 | } 81 | 82 | else if gesture.direction == UISwipeGestureRecognizerDirection.down { 83 | 84 | if self.menuBar.isHidden == true { 85 | 86 | self.menuBar.isHidden = false 87 | 88 | UIView.animate(withDuration: 0.3, delay: 0, options: UIViewAnimationOptions.beginFromCurrentState, animations: { 89 | 90 | self.menuBar.alpha = 1 91 | 92 | self.menuBar.frame = CGRect(x: 0, y: 0, width: self.menuBar.bounds.size.width, height: self.menuBar.bounds.size.height) 93 | 94 | }, completion: { (isCompleted: Bool) in 95 | 96 | self.view.setNeedsFocusUpdate() 97 | 98 | self.view.updateFocusIfNeeded() 99 | 100 | }) 101 | 102 | } 103 | 104 | } 105 | 106 | } 107 | 108 | // MARK: Init Methods 109 | 110 | override func viewDidLoad() { 111 | 112 | super.viewDidLoad() 113 | 114 | let testString = "Every page in a client-server app is built on a TVML template. TVML templates define what elements can be used and in what order. Each template is designed to display information in a specific way. For example, the loadingTemplate shows a spinner and a quick description of what is happening, while the ratingTemplate shows the rating for a product. You create a new TVML file that contains a single template for each page in a client-server app. Each template page occupies the entire TV screen. \n\nEach template page uses compound and simple elements. Compound elements contain other elements, while simple elements are single lines of TVML. Elements contain the information and images that are displayed on the screen." 115 | 116 | self.menuDetailItems = [ 117 | PLMenuDetailDescItem(title: "TabBarItem with Desc", text: testString), 118 | PLMenuDetailItem(title: "TabBarItem with Nothing"), 119 | PLMenuDetailComboItem(title: "TabBarItem with Combo", items: [ 120 | PLMenuComboSection(title: "Section1", items: ["option1", "option2"], preferredIndex: 1), 121 | PLMenuComboSection(title: "Section2", items: ["option1", "option2"], preferredIndex: 0), 122 | PLMenuComboSection(title: "Section3", items: ["option1", "option2"], preferredIndex: 1), 123 | PLMenuComboSection(title: "Section4", items: ["option1", "option2"], preferredIndex: 0), 124 | PLMenuComboSection(title: "Section5", items: ["option1", "option2"], preferredIndex: 1), 125 | ]) 126 | ] 127 | 128 | self.menuBar = PLMenuBarView() 129 | 130 | self.menuBar.style = .Light 131 | 132 | self.menuBar.delegate = self 133 | 134 | self.view.addSubview(menuBar) 135 | 136 | let gesture_up = UISwipeGestureRecognizer(target: self, action: #selector(ViewController.swipe(_:))) 137 | 138 | gesture_up.direction = UISwipeGestureRecognizerDirection.up 139 | 140 | let gesture_down = UISwipeGestureRecognizer(target: self, action: #selector(ViewController.swipe(_:))) 141 | 142 | gesture_down.direction = UISwipeGestureRecognizerDirection.down 143 | 144 | self.view.addGestureRecognizer(gesture_up) 145 | 146 | self.view.addGestureRecognizer(gesture_down) 147 | 148 | } 149 | 150 | } 151 | 152 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | #PLMenuBar 4 | 5 | PLMenuBar is a customized menubar for tvOS, simple and easy to use. 6 | 7 | ####Preview 8 | 9 | ![preview](http://i.imgur.com/i5WkB1s.gif) 10 | 11 | ####Requirement 12 | 13 | - XCode 7.3 14 | 15 | ####Support 16 | 17 | - Support desc detail view 18 | - Support combo detail view 19 | - Magic 20 | 21 | ####Delegate 22 | 23 | ```swift 24 | @objc public protocol PLMenuBarDelegate: NSObjectProtocol { 25 | 26 | func numberOfItemsInMenubar() -> Int; 27 | 28 | func menuBar(menuBar: PLMenuBarView, titleForItemAtIndex index: Int) -> String; 29 | 30 | optional func menuBar(menuBar: PLMenuBarView, detailItemForItemAtIndex index: Int) -> PLMenuDetailItem; 31 | 32 | optional func menuBar(menuBar: PLMenuBarView, didSelectItemAtIndex index: Int); 33 | 34 | optional func menuBar(menuBar: PLMenuBarView, didSelectDetailAtRow row: Int, Section section: Int, forItemAtIndex index: Int); 35 | 36 | } 37 | ``` 38 | 39 | ####Usage 40 | 41 | ```swift 42 | self.menuDetailItems = [ 43 | PLMenuDetailDescItem(title: "TabBarItem with Desc", text: "Hello, World"), 44 | PLMenuDetailItem(title: "TabBarItem with Nothing"), 45 | PLMenuDetailComboItem(title: "TabBarItem with Combo", items: [ 46 | PLMenuComboSection(title: "Section1", items: ["option1", "option2"], preferredIndex: 1), 47 | PLMenuComboSection(title: "Section2", items: ["option1", "option2"], preferredIndex: 0) 48 | ]) 49 | ]; 50 | 51 | self.menuBar = PLMenuBarView(); 52 | 53 | self.menuBar.delegate = self; 54 | 55 | self.view.addSubview(menuBar); 56 | ``` 57 | --------------------------------------------------------------------------------