├── .gitignore
├── LICENSE
├── README.md
├── SidebarProject.xcodeproj
├── project.pbxproj
└── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ └── IDEWorkspaceChecks.plist
└── SidebarProject
├── AppDelegate.swift
├── Assets.xcassets
├── AccentColor.colorset
│ └── Contents.json
├── AppIcon.appiconset
│ └── Contents.json
└── Contents.json
├── Base.lproj
├── LaunchScreen.storyboard
└── Main.storyboard
├── DetailViewController.swift
├── FirstViewController.swift
├── Info.plist
├── SceneDelegate.swift
├── SecondViewController.swift
├── SidebarProject.entitlements
├── SidebarViewController.swift
├── SplitViewDelegate.swift
├── TitleViewCell.swift
└── ToolbarDelegate.swift
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## User settings
6 | xcuserdata/
7 |
8 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
9 | *.xcscmblueprint
10 | *.xccheckout
11 |
12 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
13 | build/
14 | DerivedData/
15 | *.moved-aside
16 | *.pbxuser
17 | !default.pbxuser
18 | *.mode1v3
19 | !default.mode1v3
20 | *.mode2v3
21 | !default.mode2v3
22 | *.perspectivev3
23 | !default.perspectivev3
24 |
25 | ## Obj-C/Swift specific
26 | *.hmap
27 |
28 | ## App packaging
29 | *.ipa
30 | *.dSYM.zip
31 | *.dSYM
32 |
33 | ## Playgrounds
34 | timeline.xctimeline
35 | playground.xcworkspace
36 |
37 | # Swift Package Manager
38 | #
39 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
40 | # Packages/
41 | # Package.pins
42 | # Package.resolved
43 | # *.xcodeproj
44 | #
45 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
46 | # hence it is not needed unless you have added a package configuration file to your project
47 | # .swiftpm
48 |
49 | .build/
50 |
51 | # CocoaPods
52 | #
53 | # We recommend against adding the Pods directory to your .gitignore. However
54 | # you should judge for yourself, the pros and cons are mentioned at:
55 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
56 | #
57 | # Pods/
58 | #
59 | # Add this line if you want to avoid checking in source code from the Xcode workspace
60 | # *.xcworkspace
61 |
62 | # Carthage
63 | #
64 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
65 | # Carthage/Checkouts
66 |
67 | Carthage/Build/
68 |
69 | # Accio dependency management
70 | Dependencies/
71 | .accio/
72 |
73 | # fastlane
74 | #
75 | # It is recommended to not store the screenshots in the git repo.
76 | # Instead, use fastlane to re-generate the screenshots whenever they are needed.
77 | # For more information about the recommended setup visit:
78 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
79 |
80 | fastlane/report.xml
81 | fastlane/Preview.html
82 | fastlane/screenshots/**/*.png
83 | fastlane/test_output
84 |
85 | # Code Injection
86 | #
87 | # After new code Injection tools there's a generated folder /iOSInjectionProject
88 | # https://github.com/johnno1962/injectionforxcode
89 |
90 | iOSInjectionProject/
91 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Jung Kim
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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SidebarProject
2 |
3 | 본 프로젝트는 iOS 14+ 이상에서 지원하는 Sidebar 스타일을 확인할 수 있는 프로젝트 템플릿입니다.
4 | splitview + master-detail 을 모두 지원합니다.
5 |
6 | ### iPhone
7 |
8 | 아이폰에서는 TabBar 형식으로 First master + Second master를 표시합니다.
9 |
10 |
11 |
12 | ### iPad
13 |
14 | 아이패드에서는 Sidebar 스타일로 SplitViewController를 표시합니다.
15 |
16 | 
17 |
18 | ### macOS Catalyst
19 |
20 | 맥에서도 Sidebar + Toolbar 스타일로 SplitViewController를 표시합니다.
21 |
22 | 
23 |
--------------------------------------------------------------------------------
/SidebarProject.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 83B46BFC25DEAD2D0045BB88 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B46BFB25DEAD2D0045BB88 /* AppDelegate.swift */; };
11 | 83B46BFE25DEAD2D0045BB88 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B46BFD25DEAD2D0045BB88 /* SceneDelegate.swift */; };
12 | 83B46C0325DEAD2D0045BB88 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 83B46C0125DEAD2D0045BB88 /* Main.storyboard */; };
13 | 83B46C0525DEAD2D0045BB88 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 83B46C0425DEAD2D0045BB88 /* Assets.xcassets */; };
14 | 83B46C0825DEAD2D0045BB88 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 83B46C0625DEAD2D0045BB88 /* LaunchScreen.storyboard */; };
15 | 83B46C1925DEAD5C0045BB88 /* SecondViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B46C1825DEAD5C0045BB88 /* SecondViewController.swift */; };
16 | 83B46C1A25DEAD5C0045BB88 /* DetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B46C1425DEAD5C0045BB88 /* DetailViewController.swift */; };
17 | 83B46C1B25DEAD5C0045BB88 /* SidebarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B46C1525DEAD5C0045BB88 /* SidebarViewController.swift */; };
18 | 83B46C1C25DEAD5C0045BB88 /* ToolbarDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B46C1225DEAD5C0045BB88 /* ToolbarDelegate.swift */; };
19 | 83B46C1D25DEAD5C0045BB88 /* TitleViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B46C1325DEAD5C0045BB88 /* TitleViewCell.swift */; };
20 | 83B46C1E25DEAD5C0045BB88 /* FirstViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B46C1625DEAD5C0045BB88 /* FirstViewController.swift */; };
21 | 83B46C1F25DEAD5C0045BB88 /* SplitViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B46C1725DEAD5C0045BB88 /* SplitViewDelegate.swift */; };
22 | 83B46C2825DEAEBB0045BB88 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 83B46C2725DEAEBB0045BB88 /* README.md */; };
23 | /* End PBXBuildFile section */
24 |
25 | /* Begin PBXFileReference section */
26 | 83B46BF825DEAD2D0045BB88 /* SidebarProject.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SidebarProject.app; sourceTree = BUILT_PRODUCTS_DIR; };
27 | 83B46BFB25DEAD2D0045BB88 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
28 | 83B46BFD25DEAD2D0045BB88 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; };
29 | 83B46C0225DEAD2D0045BB88 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
30 | 83B46C0425DEAD2D0045BB88 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
31 | 83B46C0725DEAD2D0045BB88 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
32 | 83B46C0925DEAD2D0045BB88 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
33 | 83B46C1025DEAD340045BB88 /* SidebarProject.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = SidebarProject.entitlements; sourceTree = ""; };
34 | 83B46C1225DEAD5C0045BB88 /* ToolbarDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToolbarDelegate.swift; sourceTree = ""; };
35 | 83B46C1325DEAD5C0045BB88 /* TitleViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TitleViewCell.swift; sourceTree = ""; };
36 | 83B46C1425DEAD5C0045BB88 /* DetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailViewController.swift; sourceTree = ""; };
37 | 83B46C1525DEAD5C0045BB88 /* SidebarViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarViewController.swift; sourceTree = ""; };
38 | 83B46C1625DEAD5C0045BB88 /* FirstViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirstViewController.swift; sourceTree = ""; };
39 | 83B46C1725DEAD5C0045BB88 /* SplitViewDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitViewDelegate.swift; sourceTree = ""; };
40 | 83B46C1825DEAD5C0045BB88 /* SecondViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecondViewController.swift; sourceTree = ""; };
41 | 83B46C2725DEAEBB0045BB88 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; };
42 | /* End PBXFileReference section */
43 |
44 | /* Begin PBXFrameworksBuildPhase section */
45 | 83B46BF525DEAD2D0045BB88 /* Frameworks */ = {
46 | isa = PBXFrameworksBuildPhase;
47 | buildActionMask = 2147483647;
48 | files = (
49 | );
50 | runOnlyForDeploymentPostprocessing = 0;
51 | };
52 | /* End PBXFrameworksBuildPhase section */
53 |
54 | /* Begin PBXGroup section */
55 | 83B46BEF25DEAD2D0045BB88 = {
56 | isa = PBXGroup;
57 | children = (
58 | 83B46C2725DEAEBB0045BB88 /* README.md */,
59 | 83B46BFA25DEAD2D0045BB88 /* SidebarProject */,
60 | 83B46BF925DEAD2D0045BB88 /* Products */,
61 | );
62 | sourceTree = "";
63 | };
64 | 83B46BF925DEAD2D0045BB88 /* Products */ = {
65 | isa = PBXGroup;
66 | children = (
67 | 83B46BF825DEAD2D0045BB88 /* SidebarProject.app */,
68 | );
69 | name = Products;
70 | sourceTree = "";
71 | };
72 | 83B46BFA25DEAD2D0045BB88 /* SidebarProject */ = {
73 | isa = PBXGroup;
74 | children = (
75 | 83B46C1025DEAD340045BB88 /* SidebarProject.entitlements */,
76 | 83B46BFB25DEAD2D0045BB88 /* AppDelegate.swift */,
77 | 83B46BFD25DEAD2D0045BB88 /* SceneDelegate.swift */,
78 | 83B46C1225DEAD5C0045BB88 /* ToolbarDelegate.swift */,
79 | 83B46C1725DEAD5C0045BB88 /* SplitViewDelegate.swift */,
80 | 83B46C1525DEAD5C0045BB88 /* SidebarViewController.swift */,
81 | 83B46C1625DEAD5C0045BB88 /* FirstViewController.swift */,
82 | 83B46C1825DEAD5C0045BB88 /* SecondViewController.swift */,
83 | 83B46C1425DEAD5C0045BB88 /* DetailViewController.swift */,
84 | 83B46C1325DEAD5C0045BB88 /* TitleViewCell.swift */,
85 | 83B46C0125DEAD2D0045BB88 /* Main.storyboard */,
86 | 83B46C0425DEAD2D0045BB88 /* Assets.xcassets */,
87 | 83B46C0625DEAD2D0045BB88 /* LaunchScreen.storyboard */,
88 | 83B46C0925DEAD2D0045BB88 /* Info.plist */,
89 | );
90 | path = SidebarProject;
91 | sourceTree = "";
92 | };
93 | /* End PBXGroup section */
94 |
95 | /* Begin PBXNativeTarget section */
96 | 83B46BF725DEAD2D0045BB88 /* SidebarProject */ = {
97 | isa = PBXNativeTarget;
98 | buildConfigurationList = 83B46C0C25DEAD2D0045BB88 /* Build configuration list for PBXNativeTarget "SidebarProject" */;
99 | buildPhases = (
100 | 83B46BF425DEAD2D0045BB88 /* Sources */,
101 | 83B46BF525DEAD2D0045BB88 /* Frameworks */,
102 | 83B46BF625DEAD2D0045BB88 /* Resources */,
103 | );
104 | buildRules = (
105 | );
106 | dependencies = (
107 | );
108 | name = SidebarProject;
109 | productName = SidebarProject;
110 | productReference = 83B46BF825DEAD2D0045BB88 /* SidebarProject.app */;
111 | productType = "com.apple.product-type.application";
112 | };
113 | /* End PBXNativeTarget section */
114 |
115 | /* Begin PBXProject section */
116 | 83B46BF025DEAD2D0045BB88 /* Project object */ = {
117 | isa = PBXProject;
118 | attributes = {
119 | LastSwiftUpdateCheck = 1240;
120 | LastUpgradeCheck = 1240;
121 | TargetAttributes = {
122 | 83B46BF725DEAD2D0045BB88 = {
123 | CreatedOnToolsVersion = 12.4;
124 | };
125 | };
126 | };
127 | buildConfigurationList = 83B46BF325DEAD2D0045BB88 /* Build configuration list for PBXProject "SidebarProject" */;
128 | compatibilityVersion = "Xcode 9.3";
129 | developmentRegion = en;
130 | hasScannedForEncodings = 0;
131 | knownRegions = (
132 | en,
133 | Base,
134 | );
135 | mainGroup = 83B46BEF25DEAD2D0045BB88;
136 | productRefGroup = 83B46BF925DEAD2D0045BB88 /* Products */;
137 | projectDirPath = "";
138 | projectRoot = "";
139 | targets = (
140 | 83B46BF725DEAD2D0045BB88 /* SidebarProject */,
141 | );
142 | };
143 | /* End PBXProject section */
144 |
145 | /* Begin PBXResourcesBuildPhase section */
146 | 83B46BF625DEAD2D0045BB88 /* Resources */ = {
147 | isa = PBXResourcesBuildPhase;
148 | buildActionMask = 2147483647;
149 | files = (
150 | 83B46C0825DEAD2D0045BB88 /* LaunchScreen.storyboard in Resources */,
151 | 83B46C0525DEAD2D0045BB88 /* Assets.xcassets in Resources */,
152 | 83B46C2825DEAEBB0045BB88 /* README.md in Resources */,
153 | 83B46C0325DEAD2D0045BB88 /* Main.storyboard in Resources */,
154 | );
155 | runOnlyForDeploymentPostprocessing = 0;
156 | };
157 | /* End PBXResourcesBuildPhase section */
158 |
159 | /* Begin PBXSourcesBuildPhase section */
160 | 83B46BF425DEAD2D0045BB88 /* Sources */ = {
161 | isa = PBXSourcesBuildPhase;
162 | buildActionMask = 2147483647;
163 | files = (
164 | 83B46C1E25DEAD5C0045BB88 /* FirstViewController.swift in Sources */,
165 | 83B46C1925DEAD5C0045BB88 /* SecondViewController.swift in Sources */,
166 | 83B46C1B25DEAD5C0045BB88 /* SidebarViewController.swift in Sources */,
167 | 83B46C1D25DEAD5C0045BB88 /* TitleViewCell.swift in Sources */,
168 | 83B46C1A25DEAD5C0045BB88 /* DetailViewController.swift in Sources */,
169 | 83B46C1F25DEAD5C0045BB88 /* SplitViewDelegate.swift in Sources */,
170 | 83B46C1C25DEAD5C0045BB88 /* ToolbarDelegate.swift in Sources */,
171 | 83B46BFC25DEAD2D0045BB88 /* AppDelegate.swift in Sources */,
172 | 83B46BFE25DEAD2D0045BB88 /* SceneDelegate.swift in Sources */,
173 | );
174 | runOnlyForDeploymentPostprocessing = 0;
175 | };
176 | /* End PBXSourcesBuildPhase section */
177 |
178 | /* Begin PBXVariantGroup section */
179 | 83B46C0125DEAD2D0045BB88 /* Main.storyboard */ = {
180 | isa = PBXVariantGroup;
181 | children = (
182 | 83B46C0225DEAD2D0045BB88 /* Base */,
183 | );
184 | name = Main.storyboard;
185 | sourceTree = "";
186 | };
187 | 83B46C0625DEAD2D0045BB88 /* LaunchScreen.storyboard */ = {
188 | isa = PBXVariantGroup;
189 | children = (
190 | 83B46C0725DEAD2D0045BB88 /* Base */,
191 | );
192 | name = LaunchScreen.storyboard;
193 | sourceTree = "";
194 | };
195 | /* End PBXVariantGroup section */
196 |
197 | /* Begin XCBuildConfiguration section */
198 | 83B46C0A25DEAD2D0045BB88 /* Debug */ = {
199 | isa = XCBuildConfiguration;
200 | buildSettings = {
201 | ALWAYS_SEARCH_USER_PATHS = NO;
202 | CLANG_ANALYZER_NONNULL = YES;
203 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
204 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
205 | CLANG_CXX_LIBRARY = "libc++";
206 | CLANG_ENABLE_MODULES = YES;
207 | CLANG_ENABLE_OBJC_ARC = YES;
208 | CLANG_ENABLE_OBJC_WEAK = YES;
209 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
210 | CLANG_WARN_BOOL_CONVERSION = YES;
211 | CLANG_WARN_COMMA = YES;
212 | CLANG_WARN_CONSTANT_CONVERSION = YES;
213 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
214 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
215 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
216 | CLANG_WARN_EMPTY_BODY = YES;
217 | CLANG_WARN_ENUM_CONVERSION = YES;
218 | CLANG_WARN_INFINITE_RECURSION = YES;
219 | CLANG_WARN_INT_CONVERSION = YES;
220 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
221 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
222 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
223 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
224 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
225 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
226 | CLANG_WARN_STRICT_PROTOTYPES = YES;
227 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
228 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
229 | CLANG_WARN_UNREACHABLE_CODE = YES;
230 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
231 | COPY_PHASE_STRIP = NO;
232 | DEBUG_INFORMATION_FORMAT = dwarf;
233 | ENABLE_STRICT_OBJC_MSGSEND = YES;
234 | ENABLE_TESTABILITY = YES;
235 | GCC_C_LANGUAGE_STANDARD = gnu11;
236 | GCC_DYNAMIC_NO_PIC = NO;
237 | GCC_NO_COMMON_BLOCKS = YES;
238 | GCC_OPTIMIZATION_LEVEL = 0;
239 | GCC_PREPROCESSOR_DEFINITIONS = (
240 | "DEBUG=1",
241 | "$(inherited)",
242 | );
243 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
244 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
245 | GCC_WARN_UNDECLARED_SELECTOR = YES;
246 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
247 | GCC_WARN_UNUSED_FUNCTION = YES;
248 | GCC_WARN_UNUSED_VARIABLE = YES;
249 | IPHONEOS_DEPLOYMENT_TARGET = 14.4;
250 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
251 | MTL_FAST_MATH = YES;
252 | ONLY_ACTIVE_ARCH = YES;
253 | SDKROOT = iphoneos;
254 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
255 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
256 | };
257 | name = Debug;
258 | };
259 | 83B46C0B25DEAD2D0045BB88 /* Release */ = {
260 | isa = XCBuildConfiguration;
261 | buildSettings = {
262 | ALWAYS_SEARCH_USER_PATHS = NO;
263 | CLANG_ANALYZER_NONNULL = YES;
264 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
265 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
266 | CLANG_CXX_LIBRARY = "libc++";
267 | CLANG_ENABLE_MODULES = YES;
268 | CLANG_ENABLE_OBJC_ARC = YES;
269 | CLANG_ENABLE_OBJC_WEAK = YES;
270 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
271 | CLANG_WARN_BOOL_CONVERSION = YES;
272 | CLANG_WARN_COMMA = YES;
273 | CLANG_WARN_CONSTANT_CONVERSION = YES;
274 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
275 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
276 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
277 | CLANG_WARN_EMPTY_BODY = YES;
278 | CLANG_WARN_ENUM_CONVERSION = YES;
279 | CLANG_WARN_INFINITE_RECURSION = YES;
280 | CLANG_WARN_INT_CONVERSION = YES;
281 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
282 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
283 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
284 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
285 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
286 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
287 | CLANG_WARN_STRICT_PROTOTYPES = YES;
288 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
289 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
290 | CLANG_WARN_UNREACHABLE_CODE = YES;
291 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
292 | COPY_PHASE_STRIP = NO;
293 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
294 | ENABLE_NS_ASSERTIONS = NO;
295 | ENABLE_STRICT_OBJC_MSGSEND = YES;
296 | GCC_C_LANGUAGE_STANDARD = gnu11;
297 | GCC_NO_COMMON_BLOCKS = YES;
298 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
299 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
300 | GCC_WARN_UNDECLARED_SELECTOR = YES;
301 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
302 | GCC_WARN_UNUSED_FUNCTION = YES;
303 | GCC_WARN_UNUSED_VARIABLE = YES;
304 | IPHONEOS_DEPLOYMENT_TARGET = 14.4;
305 | MTL_ENABLE_DEBUG_INFO = NO;
306 | MTL_FAST_MATH = YES;
307 | SDKROOT = iphoneos;
308 | SWIFT_COMPILATION_MODE = wholemodule;
309 | SWIFT_OPTIMIZATION_LEVEL = "-O";
310 | VALIDATE_PRODUCT = YES;
311 | };
312 | name = Release;
313 | };
314 | 83B46C0D25DEAD2D0045BB88 /* Debug */ = {
315 | isa = XCBuildConfiguration;
316 | buildSettings = {
317 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
318 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
319 | CODE_SIGN_ENTITLEMENTS = SidebarProject/SidebarProject.entitlements;
320 | CODE_SIGN_STYLE = Automatic;
321 | DEVELOPMENT_TEAM = "";
322 | INFOPLIST_FILE = SidebarProject/Info.plist;
323 | LD_RUNPATH_SEARCH_PATHS = (
324 | "$(inherited)",
325 | "@executable_path/Frameworks",
326 | );
327 | PRODUCT_BUNDLE_IDENTIFIER = kr.codesquad.SidebarProject;
328 | PRODUCT_NAME = "$(TARGET_NAME)";
329 | SUPPORTS_MACCATALYST = YES;
330 | SWIFT_VERSION = 5.0;
331 | TARGETED_DEVICE_FAMILY = "1,2";
332 | };
333 | name = Debug;
334 | };
335 | 83B46C0E25DEAD2D0045BB88 /* Release */ = {
336 | isa = XCBuildConfiguration;
337 | buildSettings = {
338 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
339 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
340 | CODE_SIGN_ENTITLEMENTS = SidebarProject/SidebarProject.entitlements;
341 | CODE_SIGN_STYLE = Automatic;
342 | DEVELOPMENT_TEAM = "";
343 | INFOPLIST_FILE = SidebarProject/Info.plist;
344 | LD_RUNPATH_SEARCH_PATHS = (
345 | "$(inherited)",
346 | "@executable_path/Frameworks",
347 | );
348 | PRODUCT_BUNDLE_IDENTIFIER = kr.codesquad.SidebarProject;
349 | PRODUCT_NAME = "$(TARGET_NAME)";
350 | SUPPORTS_MACCATALYST = YES;
351 | SWIFT_VERSION = 5.0;
352 | TARGETED_DEVICE_FAMILY = "1,2";
353 | };
354 | name = Release;
355 | };
356 | /* End XCBuildConfiguration section */
357 |
358 | /* Begin XCConfigurationList section */
359 | 83B46BF325DEAD2D0045BB88 /* Build configuration list for PBXProject "SidebarProject" */ = {
360 | isa = XCConfigurationList;
361 | buildConfigurations = (
362 | 83B46C0A25DEAD2D0045BB88 /* Debug */,
363 | 83B46C0B25DEAD2D0045BB88 /* Release */,
364 | );
365 | defaultConfigurationIsVisible = 0;
366 | defaultConfigurationName = Release;
367 | };
368 | 83B46C0C25DEAD2D0045BB88 /* Build configuration list for PBXNativeTarget "SidebarProject" */ = {
369 | isa = XCConfigurationList;
370 | buildConfigurations = (
371 | 83B46C0D25DEAD2D0045BB88 /* Debug */,
372 | 83B46C0E25DEAD2D0045BB88 /* Release */,
373 | );
374 | defaultConfigurationIsVisible = 0;
375 | defaultConfigurationName = Release;
376 | };
377 | /* End XCConfigurationList section */
378 | };
379 | rootObject = 83B46BF025DEAD2D0045BB88 /* Project object */;
380 | }
381 |
--------------------------------------------------------------------------------
/SidebarProject.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/SidebarProject.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/SidebarProject/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // SidebarProject
4 | //
5 | // Created by JK on 2021/02/18.
6 | //
7 |
8 | import UIKit
9 |
10 | @main
11 | class AppDelegate: UIResponder, UIApplicationDelegate {
12 |
13 |
14 |
15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
16 | // Override point for customization after application launch.
17 | return true
18 | }
19 |
20 | // MARK: UISceneSession Lifecycle
21 |
22 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
23 | // Called when a new scene session is being created.
24 | // Use this method to select a configuration to create the new scene with.
25 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
26 | }
27 |
28 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) {
29 | // Called when the user discards a scene session.
30 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
31 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
32 | }
33 |
34 |
35 | }
36 |
37 |
--------------------------------------------------------------------------------
/SidebarProject/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/SidebarProject/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "scale" : "2x",
6 | "size" : "20x20"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "scale" : "3x",
11 | "size" : "20x20"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "scale" : "2x",
16 | "size" : "29x29"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "scale" : "3x",
21 | "size" : "29x29"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "scale" : "2x",
26 | "size" : "40x40"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "scale" : "3x",
31 | "size" : "40x40"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "scale" : "2x",
36 | "size" : "60x60"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "scale" : "3x",
41 | "size" : "60x60"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "scale" : "1x",
46 | "size" : "20x20"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "scale" : "2x",
51 | "size" : "20x20"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "scale" : "1x",
56 | "size" : "29x29"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "scale" : "2x",
61 | "size" : "29x29"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "scale" : "1x",
66 | "size" : "40x40"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "scale" : "2x",
71 | "size" : "40x40"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "scale" : "1x",
76 | "size" : "76x76"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "scale" : "2x",
81 | "size" : "76x76"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "scale" : "2x",
86 | "size" : "83.5x83.5"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "scale" : "1x",
91 | "size" : "1024x1024"
92 | }
93 | ],
94 | "info" : {
95 | "author" : "xcode",
96 | "version" : 1
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/SidebarProject/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/SidebarProject/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 |
--------------------------------------------------------------------------------
/SidebarProject/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 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
--------------------------------------------------------------------------------
/SidebarProject/DetailViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DetailViewController.swift
3 | // HelloMaster
4 | //
5 | // Created by JK on 2021/02/18.
6 | //
7 |
8 | import UIKit
9 |
10 | class DetailViewController: UIViewController {
11 |
12 | static let storyboardID = "DetailView"
13 | static func instantiateFromStoryboard() -> DetailViewController? {
14 | let storyboard = UIStoryboard(name: "Main", bundle: .main)
15 | return storyboard.instantiateViewController(identifier: storyboardID) as? DetailViewController
16 | }
17 |
18 | @IBOutlet weak var detailDescriptionLabel: UILabel!
19 |
20 | func configureView() {
21 | // Update the user interface for the detail item.
22 | if let detail = detailItem {
23 | if let label = detailDescriptionLabel {
24 | label.text = detail.description
25 | }
26 | }
27 | }
28 |
29 | override func viewDidLoad() {
30 | super.viewDidLoad()
31 | // Do any additional setup after loading the view.
32 | configureView()
33 | }
34 |
35 | var detailItem: NSDate? {
36 | didSet {
37 | // Update the view.
38 | configureView()
39 | }
40 | }
41 |
42 |
43 | }
44 |
45 |
--------------------------------------------------------------------------------
/SidebarProject/FirstViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FirstViewController.swift
3 | // HelloMaster
4 | //
5 | // Created by JK on 2021/02/18.
6 | //
7 |
8 | import UIKit
9 |
10 | class FirstViewController: UITableViewController {
11 |
12 | static let storyboardID = "FirstView"
13 | static func instantiateFromStoryboard() -> FirstViewController? {
14 | let storyboard = UIStoryboard(name: "Main", bundle: .main)
15 | return storyboard.instantiateViewController(identifier: storyboardID) as? FirstViewController
16 | }
17 |
18 | var objects = [Any]()
19 |
20 | override func viewDidLoad() {
21 | super.viewDidLoad()
22 | // Do any additional setup after loading the view.
23 | let addButton = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(insertNewObject(_:)))
24 | navigationItem.rightBarButtonItem = addButton
25 | }
26 |
27 | override func viewWillAppear(_ animated: Bool) {
28 | clearsSelectionOnViewWillAppear = splitViewController!.isCollapsed
29 | super.viewWillAppear(animated)
30 | }
31 |
32 | @objc
33 | func insertNewObject(_ sender: Any) {
34 | objects.insert(NSDate(), at: 0)
35 | let indexPath = IndexPath(row: 0, section: 0)
36 | tableView.insertRows(at: [indexPath], with: .automatic)
37 | }
38 |
39 | // MARK: - Segues
40 |
41 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
42 | if segue.identifier == "showDetail" {
43 | if let indexPath = tableView.indexPathForSelectedRow {
44 | let object = objects[indexPath.row] as! NSDate
45 | let controller = (segue.destination as! UINavigationController).topViewController as! DetailViewController
46 | controller.detailItem = object
47 | controller.navigationItem.leftBarButtonItem = splitViewController?.displayModeButtonItem
48 | controller.navigationItem.leftItemsSupplementBackButton = true
49 | }
50 | }
51 | }
52 |
53 | // MARK: - Table View
54 |
55 | override func numberOfSections(in tableView: UITableView) -> Int {
56 | return 1
57 | }
58 |
59 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
60 | return objects.count
61 | }
62 |
63 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
64 | let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
65 | let object = objects[indexPath.row] as! NSDate
66 | cell.textLabel!.text = object.description
67 | return cell
68 | }
69 |
70 | override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
71 | // Return false if you do not want the specified item to be editable.
72 | return true
73 | }
74 |
75 | override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
76 | if editingStyle == .delete {
77 | objects.remove(at: indexPath.row)
78 | tableView.deleteRows(at: [indexPath], with: .fade)
79 | } else if editingStyle == .insert {
80 | // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view.
81 | }
82 | }
83 |
84 |
85 | }
86 |
87 |
--------------------------------------------------------------------------------
/SidebarProject/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 | LSRequiresIPhoneOS
22 |
23 | UIApplicationSceneManifest
24 |
25 | UIApplicationSupportsMultipleScenes
26 |
27 | UISceneConfigurations
28 |
29 | UIWindowSceneSessionRoleApplication
30 |
31 |
32 | UISceneConfigurationName
33 | Default Configuration
34 | UISceneDelegateClassName
35 | $(PRODUCT_MODULE_NAME).SceneDelegate
36 | UISceneStoryboardFile
37 | Main
38 |
39 |
40 |
41 |
42 | UIApplicationSupportsIndirectInputEvents
43 |
44 | UILaunchStoryboardName
45 | LaunchScreen
46 | UIMainStoryboardFile
47 | Main
48 | UIRequiredDeviceCapabilities
49 |
50 | armv7
51 |
52 | UISupportedInterfaceOrientations
53 |
54 | UIInterfaceOrientationPortrait
55 | UIInterfaceOrientationLandscapeLeft
56 | UIInterfaceOrientationLandscapeRight
57 |
58 | UISupportedInterfaceOrientations~ipad
59 |
60 | UIInterfaceOrientationPortrait
61 | UIInterfaceOrientationPortraitUpsideDown
62 | UIInterfaceOrientationLandscapeLeft
63 | UIInterfaceOrientationLandscapeRight
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/SidebarProject/SceneDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SceneDelegate.swift
3 | // SidebarProject
4 | //
5 | // Created by JK on 2021/02/18.
6 | //
7 |
8 | import UIKit
9 |
10 | class SceneDelegate: UIResponder, UIWindowSceneDelegate {
11 |
12 | var window: UIWindow?
13 | var splitViewDelegate = SplitViewDelegate()
14 | var toolbarDelegate = ToolbarDelegate()
15 |
16 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
17 | if #available(iOS 14, *) {
18 | if window?.traitCollection.userInterfaceIdiom == .pad {
19 | if let splitViewController = createThreeColumnSplitViewController() {
20 | window?.rootViewController = splitViewController
21 | }
22 | }
23 | }
24 |
25 | configureSplitViewController()
26 |
27 | #if targetEnvironment(macCatalyst)
28 | guard let windowScene = scene as? UIWindowScene else { return }
29 |
30 | let toolbar = NSToolbar(identifier: "main")
31 | toolbar.delegate = toolbarDelegate
32 | toolbar.displayMode = .iconOnly
33 |
34 | if let titlebar = windowScene.titlebar {
35 | titlebar.toolbar = toolbar
36 | titlebar.toolbarStyle = .automatic
37 | }
38 | #endif
39 | }
40 |
41 | private func configureSplitViewController() {
42 | guard
43 | let window = window,
44 | let splitViewController = window.rootViewController as? UISplitViewController
45 | else {
46 | fatalError()
47 | }
48 | splitViewController.delegate = splitViewDelegate
49 | }
50 | }
51 |
52 | @available(iOS 14, *)
53 | extension SceneDelegate {
54 |
55 | private func createThreeColumnSplitViewController() -> UISplitViewController? {
56 | guard
57 | let firstViewController = FirstViewController.instantiateFromStoryboard(),
58 | let detailViewController = DetailViewController.instantiateFromStoryboard()
59 | else { return nil }
60 |
61 | let sidebarViewController = SidebarViewController()
62 |
63 | let splitViewController = UISplitViewController(style: .tripleColumn)
64 | splitViewController.primaryBackgroundStyle = .sidebar
65 | splitViewController.preferredDisplayMode = .twoBesideSecondary
66 |
67 | splitViewController.setViewController(sidebarViewController, for: .primary)
68 | splitViewController.setViewController(firstViewController, for: .supplementary)
69 | splitViewController.setViewController(detailViewController, for: .secondary)
70 | return splitViewController
71 | }
72 |
73 | }
74 |
--------------------------------------------------------------------------------
/SidebarProject/SecondViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // OtherViewController.swift
3 | // PRBoard
4 | //
5 | // Created by JK on 2021/02/18.
6 | //
7 |
8 | import UIKit
9 |
10 |
11 | class SecondViewController: UICollectionViewController {
12 |
13 | static let storyboardID = "SecondView"
14 | static func instantiateFromStoryboard() -> SecondViewController? {
15 | let storyboard = UIStoryboard(name: "Main", bundle: .main)
16 | return storyboard.instantiateViewController(identifier: storyboardID) as? SecondViewController
17 | }
18 |
19 | var objects = [Any]()
20 |
21 | override func viewDidLoad() {
22 | super.viewDidLoad()
23 |
24 | let addButton = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(insertNewObject(_:)))
25 | navigationItem.rightBarButtonItem = addButton
26 | }
27 |
28 | override func viewWillAppear(_ animated: Bool) {
29 | clearsSelectionOnViewWillAppear = splitViewController!.isCollapsed
30 | super.viewWillAppear(animated)
31 | }
32 |
33 | @objc
34 | func insertNewObject(_ sender: Any) {
35 | objects.insert(NSDate(), at: 0)
36 | let indexPath = IndexPath(row: 0, section: 0)
37 | collectionView.insertItems(at: [indexPath])
38 | }
39 |
40 | // MARK: - Segues
41 |
42 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
43 | if segue.identifier == "showDetail" {
44 | if let indexPath = collectionView.indexPathsForSelectedItems?.first {
45 | let object = objects[indexPath.row] as! NSDate
46 | let controller = (segue.destination as! UINavigationController).topViewController as! DetailViewController
47 | controller.detailItem = object
48 | controller.navigationItem.leftBarButtonItem = splitViewController?.displayModeButtonItem
49 | controller.navigationItem.leftItemsSupplementBackButton = true
50 | }
51 | }
52 | }
53 |
54 | // MARK: UICollectionViewDataSource
55 |
56 | override func numberOfSections(in collectionView: UICollectionView) -> Int {
57 | return 1
58 | }
59 |
60 |
61 | override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
62 | return objects.count
63 | }
64 |
65 | override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
66 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: TitleViewCell.reuseIdentifier, for: indexPath) as! TitleViewCell
67 |
68 | let object = objects[indexPath.row] as! NSDate
69 | cell.titleLabel.text = object.description
70 |
71 | return cell
72 | }
73 | }
74 |
75 | extension SecondViewController : UICollectionViewDelegateFlowLayout {
76 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
77 | return CGSize(width: 365, height: 55)
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/SidebarProject/SidebarProject.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.network.client
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/SidebarProject/SidebarViewController.swift:
--------------------------------------------------------------------------------
1 |
2 |
3 | import UIKit
4 |
5 | @available(iOS 14, *)
6 | class SidebarViewController: UIViewController {
7 |
8 | private enum SidebarItemType: Int {
9 | case header, expandableRow, row
10 | }
11 |
12 | private enum SidebarSection: Int {
13 | case first, second
14 | }
15 |
16 | private struct SidebarItem: Hashable, Identifiable {
17 | let id: UUID
18 | let type: SidebarItemType
19 | let title: String
20 | let subtitle: String?
21 |
22 | static func header(title: String, id: UUID = UUID()) -> Self {
23 | return SidebarItem(id: id, type: .header, title: title, subtitle: nil)
24 | }
25 |
26 | static func expandableRow(title: String, subtitle: String?, id: UUID = UUID()) -> Self {
27 | return SidebarItem(id: id, type: .expandableRow, title: title, subtitle: subtitle)
28 | }
29 |
30 | static func row(title: String, subtitle: String?, id: UUID = UUID()) -> Self {
31 | return SidebarItem(id: id, type: .row, title: title, subtitle: subtitle)
32 | }
33 | }
34 |
35 | private struct RowIdentifier {
36 | static let firstMasterRow = UUID()
37 | static let secondMasterRow = UUID()
38 | }
39 |
40 | private var collectionView: UICollectionView!
41 | private var dataSource: UICollectionViewDiffableDataSource!
42 | private var supplementaries : [UIViewController] = []
43 |
44 | override func viewDidLoad() {
45 | super.viewDidLoad()
46 |
47 | supplementaries = [self.splitViewController!.viewController(for: .supplementary)!, SecondViewController.instantiateFromStoryboard()!]
48 |
49 | configureCollectionView()
50 | configureDataSource()
51 | dataSource.apply(firstSnapshot(), to: .first, animatingDifferences: false)
52 | dataSource.apply(secondSnapshot(), to: .second, animatingDifferences: false)
53 | }
54 |
55 | override func viewWillAppear(_ animated: Bool) {
56 | super.viewWillAppear(animated)
57 |
58 | #if targetEnvironment(macCatalyst)
59 | navigationController?.setNavigationBarHidden(true, animated: animated)
60 | #endif
61 | }
62 | }
63 |
64 | @available(iOS 14, *)
65 | extension SidebarViewController {
66 | private func configureCollectionView() {
67 | collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: createLayout())
68 | collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
69 | collectionView.backgroundColor = .systemBackground
70 | collectionView.delegate = self
71 | view.addSubview(collectionView)
72 | }
73 |
74 | private func createLayout() -> UICollectionViewLayout {
75 | let layout = UICollectionViewCompositionalLayout() { (sectionIndex, layoutEnvironment) -> NSCollectionLayoutSection? in
76 | var configuration = UICollectionLayoutListConfiguration(appearance: .sidebar)
77 | configuration.showsSeparators = false
78 | configuration.headerMode = .firstItemInSection
79 | let section = NSCollectionLayoutSection.list(using: configuration, layoutEnvironment: layoutEnvironment)
80 | return section
81 | }
82 | return layout
83 | }
84 | }
85 |
86 | @available(iOS 14, *)
87 | extension SidebarViewController: UICollectionViewDelegate {
88 | func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
89 | guard
90 | let splitViewController = self.splitViewController
91 | else { return }
92 | let selected = supplementaries[indexPath.section]
93 | let supplementary = splitViewController.viewController(for: .supplementary)
94 | if supplementary !== selected {
95 | DispatchQueue.main.async {
96 | splitViewController.hide(.supplementary)
97 | splitViewController.setViewController(selected, for: .supplementary)
98 | splitViewController.show(.supplementary)
99 | print(self.supplementaries)
100 | }
101 | }
102 | collectionView.deselectItem(at: indexPath, animated: true)
103 | }
104 | }
105 |
106 | @available(iOS 14, *)
107 | extension SidebarViewController {
108 | private func configureDataSource() {
109 | let headerRegistration = UICollectionView.CellRegistration {
110 | (cell, indexPath, item) in
111 |
112 | var contentConfiguration = UIListContentConfiguration.sidebarHeader()
113 | contentConfiguration.text = item.title
114 | contentConfiguration.textProperties.font = .preferredFont(forTextStyle: .subheadline)
115 | contentConfiguration.textProperties.color = .secondaryLabel
116 |
117 | cell.contentConfiguration = contentConfiguration
118 | cell.accessories = [.outlineDisclosure()]
119 | }
120 |
121 | let expandableRowRegistration = UICollectionView.CellRegistration {
122 | (cell, indexPath, item) in
123 |
124 | var contentConfiguration = UIListContentConfiguration.sidebarSubtitleCell()
125 | contentConfiguration.text = item.title
126 | contentConfiguration.secondaryText = item.subtitle
127 |
128 | cell.contentConfiguration = contentConfiguration
129 | cell.accessories = [.outlineDisclosure()]
130 | }
131 |
132 | let rowRegistration = UICollectionView.CellRegistration {
133 | (cell, indexPath, item) in
134 |
135 | var contentConfiguration = UIListContentConfiguration.sidebarSubtitleCell()
136 | contentConfiguration.text = item.title
137 | contentConfiguration.secondaryText = item.subtitle
138 |
139 | cell.contentConfiguration = contentConfiguration
140 | }
141 |
142 | dataSource = UICollectionViewDiffableDataSource(collectionView: collectionView) {
143 | (collectionView, indexPath, item) -> UICollectionViewCell in
144 |
145 | switch item.type {
146 | case .header:
147 | return collectionView.dequeueConfiguredReusableCell(using: headerRegistration, for: indexPath, item: item)
148 | case .expandableRow:
149 | return collectionView.dequeueConfiguredReusableCell(using: expandableRowRegistration, for: indexPath, item: item)
150 | default:
151 | return collectionView.dequeueConfiguredReusableCell(using: rowRegistration, for: indexPath, item: item)
152 | }
153 | }
154 | }
155 |
156 | private func firstSnapshot() -> NSDiffableDataSourceSectionSnapshot {
157 | var snapshot = NSDiffableDataSourceSectionSnapshot()
158 | let header = SidebarItem.header(title: "First")
159 | let items: [SidebarItem] = [
160 | .row(title: "Master", subtitle: nil, id: RowIdentifier.firstMasterRow)
161 | ]
162 |
163 | snapshot.append([header])
164 | snapshot.expand([header])
165 | snapshot.append(items, to: header)
166 | return snapshot
167 | }
168 |
169 | private func secondSnapshot() -> NSDiffableDataSourceSectionSnapshot {
170 | var snapshot = NSDiffableDataSourceSectionSnapshot()
171 | let header = SidebarItem.header(title: "Second")
172 | let items: [SidebarItem] = [
173 | .row(title: "Master", subtitle: nil, id: RowIdentifier.secondMasterRow)
174 | ]
175 |
176 | snapshot.append([header])
177 | snapshot.expand([header])
178 | snapshot.append(items, to: header)
179 | return snapshot
180 | }
181 | }
182 |
183 |
--------------------------------------------------------------------------------
/SidebarProject/SplitViewDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | class SplitViewDelegate: NSObject, UISplitViewControllerDelegate {
4 |
5 | func splitViewController(_ splitViewController: UISplitViewController, showDetail vc: UIViewController, sender: Any?) -> Bool {
6 | guard
7 | splitViewController.isCollapsed == true,
8 | let tabBarController = splitViewController.viewControllers.first as? UITabBarController,
9 | let navigationController = tabBarController.selectedViewController as? UINavigationController
10 | else { return false }
11 |
12 | var viewControllerToPush = vc
13 | if let otherNavigationController = vc as? UINavigationController {
14 | if let topViewController = otherNavigationController.topViewController {
15 | viewControllerToPush = topViewController
16 | }
17 | }
18 | viewControllerToPush.hidesBottomBarWhenPushed = true
19 | navigationController.pushViewController(viewControllerToPush, animated: true)
20 |
21 | return true
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/SidebarProject/TitleViewCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TitleViewCell.swift
3 | // PRBoard
4 | //
5 | // Created by JK on 2021/02/18.
6 | //
7 |
8 | import UIKit
9 |
10 | class TitleViewCell: UICollectionViewCell {
11 | static let reuseIdentifier = "TitleViewCell"
12 |
13 | @IBOutlet weak var titleLabel: UILabel!
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/SidebarProject/ToolbarDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Combine
3 |
4 | class ToolbarDelegate: NSObject {
5 |
6 | #if targetEnvironment(macCatalyst)
7 | override init() {
8 | }
9 | #endif
10 |
11 | }
12 |
13 | #if targetEnvironment(macCatalyst)
14 | extension ToolbarDelegate: NSToolbarDelegate {
15 |
16 | func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
17 | let identifiers: [NSToolbarItem.Identifier] = [
18 | .toggleSidebar,
19 | .flexibleSpace,
20 | ]
21 | return identifiers
22 | }
23 |
24 | func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
25 | return toolbarDefaultItemIdentifiers(toolbar)
26 | }
27 |
28 | func toolbar(_ toolbar: NSToolbar,
29 | itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier,
30 | willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? {
31 |
32 | var toolbarItem: NSToolbarItem?
33 |
34 | switch itemIdentifier {
35 | case .toggleSidebar:
36 | toolbarItem = NSToolbarItem(itemIdentifier: itemIdentifier)
37 |
38 | default:
39 | toolbarItem = nil
40 | }
41 |
42 | return toolbarItem
43 | }
44 |
45 | }
46 | #endif
47 |
--------------------------------------------------------------------------------