├── Declarative_UIKit.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcuserdata │ └── eppz.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ └── xcschememanagement.plist └── Declarative_UIKit ├── AppDelegate.swift ├── Assets.xcassets ├── AccentColor.colorset │ └── Contents.json ├── AppIcon.appiconset │ └── Contents.json ├── Contents.json └── Earth.imageset │ ├── Contents.json │ ├── Earth.png │ ├── Earth@2x.png │ └── Earth@3x.png ├── Base.lproj └── LaunchScreen.storyboard ├── Extensions ├── PreviewView.swift ├── UIKit+Extensions.swift └── Withable.swift ├── Info.plist ├── Model └── Planets.swift ├── SceneDelegate.swift └── Views ├── ContentViewController.swift ├── ContentViewController_1.swift ├── ExamplesViewController.swift ├── ImperativeViewController.swift ├── MultiStackViewController.swift └── TitleLabelViewController.swift /Declarative_UIKit.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 553A7E4125750AC900310094 /* PreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553A7E4025750AC900310094 /* PreviewView.swift */; }; 11 | 554A94EB2574508400F2CFE6 /* ContentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 554A94EA2574508400F2CFE6 /* ContentViewController.swift */; }; 12 | 558DC015257440CC00A7298F /* ImperativeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 558DC014257440CB00A7298F /* ImperativeViewController.swift */; }; 13 | 558DC0202574463D00A7298F /* Planets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 558DC01F2574463D00A7298F /* Planets.swift */; }; 14 | 558DC0272574474000A7298F /* UIKit+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 558DC0262574474000A7298F /* UIKit+Extensions.swift */; }; 15 | 55B9EB8F2572FBC30050C887 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55B9EB8E2572FBC30050C887 /* AppDelegate.swift */; }; 16 | 55B9EB912572FBC30050C887 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55B9EB902572FBC30050C887 /* SceneDelegate.swift */; }; 17 | 55B9EB952572FBC90050C887 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 55B9EB942572FBC90050C887 /* Assets.xcassets */; }; 18 | 55B9EB9B2572FBC90050C887 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 55B9EB992572FBC90050C887 /* LaunchScreen.storyboard */; }; 19 | 55B9EBA42572FC030050C887 /* Withable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55B9EBA32572FC030050C887 /* Withable.swift */; }; 20 | 55B9EBB0257301410050C887 /* ContentViewController_1.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55B9EBAF257301410050C887 /* ContentViewController_1.swift */; }; 21 | 55D5D603257485F200FC5202 /* TitleLabelViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55D5D602257485F200FC5202 /* TitleLabelViewController.swift */; }; 22 | 55D5D607257497FA00FC5202 /* ExamplesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55D5D606257497FA00FC5202 /* ExamplesViewController.swift */; }; 23 | /* End PBXBuildFile section */ 24 | 25 | /* Begin PBXFileReference section */ 26 | 553A7E4025750AC900310094 /* PreviewView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviewView.swift; sourceTree = ""; }; 27 | 554A94EA2574508400F2CFE6 /* ContentViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentViewController.swift; sourceTree = ""; }; 28 | 558DC014257440CB00A7298F /* ImperativeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImperativeViewController.swift; sourceTree = ""; }; 29 | 558DC01F2574463D00A7298F /* Planets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Planets.swift; sourceTree = ""; }; 30 | 558DC0262574474000A7298F /* UIKit+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIKit+Extensions.swift"; sourceTree = ""; }; 31 | 55B9EB8B2572FBC30050C887 /* Declarative_UIKit.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Declarative_UIKit.app; sourceTree = BUILT_PRODUCTS_DIR; }; 32 | 55B9EB8E2572FBC30050C887 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 33 | 55B9EB902572FBC30050C887 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; 34 | 55B9EB942572FBC90050C887 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 35 | 55B9EB9A2572FBC90050C887 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 36 | 55B9EB9C2572FBC90050C887 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 37 | 55B9EBA32572FC030050C887 /* Withable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Withable.swift; sourceTree = ""; }; 38 | 55B9EBAF257301410050C887 /* ContentViewController_1.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentViewController_1.swift; sourceTree = ""; wrapsLines = 0; }; 39 | 55D5D602257485F200FC5202 /* TitleLabelViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TitleLabelViewController.swift; sourceTree = ""; usesTabs = 1; }; 40 | 55D5D606257497FA00FC5202 /* ExamplesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExamplesViewController.swift; sourceTree = ""; }; 41 | /* End PBXFileReference section */ 42 | 43 | /* Begin PBXFrameworksBuildPhase section */ 44 | 55B9EB882572FBC30050C887 /* Frameworks */ = { 45 | isa = PBXFrameworksBuildPhase; 46 | buildActionMask = 2147483647; 47 | files = ( 48 | ); 49 | runOnlyForDeploymentPostprocessing = 0; 50 | }; 51 | /* End PBXFrameworksBuildPhase section */ 52 | 53 | /* Begin PBXGroup section */ 54 | 5571D6332574699D00727C89 /* Model */ = { 55 | isa = PBXGroup; 56 | children = ( 57 | 558DC01F2574463D00A7298F /* Planets.swift */, 58 | ); 59 | path = Model; 60 | sourceTree = ""; 61 | }; 62 | 558DC0182574432700A7298F /* Extensions */ = { 63 | isa = PBXGroup; 64 | children = ( 65 | 55B9EBA32572FC030050C887 /* Withable.swift */, 66 | 558DC0262574474000A7298F /* UIKit+Extensions.swift */, 67 | 553A7E4025750AC900310094 /* PreviewView.swift */, 68 | ); 69 | path = Extensions; 70 | sourceTree = ""; 71 | }; 72 | 558DC0192574433000A7298F /* Views */ = { 73 | isa = PBXGroup; 74 | children = ( 75 | 55D5D602257485F200FC5202 /* TitleLabelViewController.swift */, 76 | 55D5D606257497FA00FC5202 /* ExamplesViewController.swift */, 77 | 558DC014257440CB00A7298F /* ImperativeViewController.swift */, 78 | 55B9EBAF257301410050C887 /* ContentViewController_1.swift */, 79 | 554A94EA2574508400F2CFE6 /* ContentViewController.swift */, 80 | ); 81 | path = Views; 82 | sourceTree = ""; 83 | }; 84 | 55B9EB822572FBC30050C887 = { 85 | isa = PBXGroup; 86 | children = ( 87 | 55B9EB8D2572FBC30050C887 /* Declarative_UIKit */, 88 | 55B9EB8C2572FBC30050C887 /* Products */, 89 | ); 90 | sourceTree = ""; 91 | }; 92 | 55B9EB8C2572FBC30050C887 /* Products */ = { 93 | isa = PBXGroup; 94 | children = ( 95 | 55B9EB8B2572FBC30050C887 /* Declarative_UIKit.app */, 96 | ); 97 | name = Products; 98 | sourceTree = ""; 99 | }; 100 | 55B9EB8D2572FBC30050C887 /* Declarative_UIKit */ = { 101 | isa = PBXGroup; 102 | children = ( 103 | 55B9EB8E2572FBC30050C887 /* AppDelegate.swift */, 104 | 55B9EB902572FBC30050C887 /* SceneDelegate.swift */, 105 | 55B9EB992572FBC90050C887 /* LaunchScreen.storyboard */, 106 | 55B9EB9C2572FBC90050C887 /* Info.plist */, 107 | 5571D6332574699D00727C89 /* Model */, 108 | 558DC0182574432700A7298F /* Extensions */, 109 | 558DC0192574433000A7298F /* Views */, 110 | 55B9EB942572FBC90050C887 /* Assets.xcassets */, 111 | ); 112 | path = Declarative_UIKit; 113 | sourceTree = ""; 114 | }; 115 | /* End PBXGroup section */ 116 | 117 | /* Begin PBXNativeTarget section */ 118 | 55B9EB8A2572FBC30050C887 /* Declarative_UIKit */ = { 119 | isa = PBXNativeTarget; 120 | buildConfigurationList = 55B9EB9F2572FBC90050C887 /* Build configuration list for PBXNativeTarget "Declarative_UIKit" */; 121 | buildPhases = ( 122 | 55B9EB872572FBC30050C887 /* Sources */, 123 | 55B9EB882572FBC30050C887 /* Frameworks */, 124 | 55B9EB892572FBC30050C887 /* Resources */, 125 | ); 126 | buildRules = ( 127 | ); 128 | dependencies = ( 129 | ); 130 | name = Declarative_UIKit; 131 | productName = Declerative_UIKit; 132 | productReference = 55B9EB8B2572FBC30050C887 /* Declarative_UIKit.app */; 133 | productType = "com.apple.product-type.application"; 134 | }; 135 | /* End PBXNativeTarget section */ 136 | 137 | /* Begin PBXProject section */ 138 | 55B9EB832572FBC30050C887 /* Project object */ = { 139 | isa = PBXProject; 140 | attributes = { 141 | LastSwiftUpdateCheck = 1220; 142 | LastUpgradeCheck = 1220; 143 | TargetAttributes = { 144 | 55B9EB8A2572FBC30050C887 = { 145 | CreatedOnToolsVersion = 12.2; 146 | }; 147 | }; 148 | }; 149 | buildConfigurationList = 55B9EB862572FBC30050C887 /* Build configuration list for PBXProject "Declarative_UIKit" */; 150 | compatibilityVersion = "Xcode 9.3"; 151 | developmentRegion = en; 152 | hasScannedForEncodings = 0; 153 | knownRegions = ( 154 | en, 155 | Base, 156 | ); 157 | mainGroup = 55B9EB822572FBC30050C887; 158 | productRefGroup = 55B9EB8C2572FBC30050C887 /* Products */; 159 | projectDirPath = ""; 160 | projectRoot = ""; 161 | targets = ( 162 | 55B9EB8A2572FBC30050C887 /* Declarative_UIKit */, 163 | ); 164 | }; 165 | /* End PBXProject section */ 166 | 167 | /* Begin PBXResourcesBuildPhase section */ 168 | 55B9EB892572FBC30050C887 /* Resources */ = { 169 | isa = PBXResourcesBuildPhase; 170 | buildActionMask = 2147483647; 171 | files = ( 172 | 55B9EB9B2572FBC90050C887 /* LaunchScreen.storyboard in Resources */, 173 | 55B9EB952572FBC90050C887 /* Assets.xcassets in Resources */, 174 | ); 175 | runOnlyForDeploymentPostprocessing = 0; 176 | }; 177 | /* End PBXResourcesBuildPhase section */ 178 | 179 | /* Begin PBXSourcesBuildPhase section */ 180 | 55B9EB872572FBC30050C887 /* Sources */ = { 181 | isa = PBXSourcesBuildPhase; 182 | buildActionMask = 2147483647; 183 | files = ( 184 | 558DC015257440CC00A7298F /* ImperativeViewController.swift in Sources */, 185 | 55D5D607257497FA00FC5202 /* ExamplesViewController.swift in Sources */, 186 | 558DC0272574474000A7298F /* UIKit+Extensions.swift in Sources */, 187 | 558DC0202574463D00A7298F /* Planets.swift in Sources */, 188 | 55B9EB8F2572FBC30050C887 /* AppDelegate.swift in Sources */, 189 | 554A94EB2574508400F2CFE6 /* ContentViewController.swift in Sources */, 190 | 55B9EBA42572FC030050C887 /* Withable.swift in Sources */, 191 | 553A7E4125750AC900310094 /* PreviewView.swift in Sources */, 192 | 55B9EBB0257301410050C887 /* ContentViewController_1.swift in Sources */, 193 | 55B9EB912572FBC30050C887 /* SceneDelegate.swift in Sources */, 194 | 55D5D603257485F200FC5202 /* TitleLabelViewController.swift in Sources */, 195 | ); 196 | runOnlyForDeploymentPostprocessing = 0; 197 | }; 198 | /* End PBXSourcesBuildPhase section */ 199 | 200 | /* Begin PBXVariantGroup section */ 201 | 55B9EB992572FBC90050C887 /* LaunchScreen.storyboard */ = { 202 | isa = PBXVariantGroup; 203 | children = ( 204 | 55B9EB9A2572FBC90050C887 /* Base */, 205 | ); 206 | name = LaunchScreen.storyboard; 207 | sourceTree = ""; 208 | }; 209 | /* End PBXVariantGroup section */ 210 | 211 | /* Begin XCBuildConfiguration section */ 212 | 55B9EB9D2572FBC90050C887 /* Debug */ = { 213 | isa = XCBuildConfiguration; 214 | buildSettings = { 215 | ALWAYS_SEARCH_USER_PATHS = NO; 216 | CLANG_ANALYZER_NONNULL = YES; 217 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 218 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 219 | CLANG_CXX_LIBRARY = "libc++"; 220 | CLANG_ENABLE_MODULES = YES; 221 | CLANG_ENABLE_OBJC_ARC = YES; 222 | CLANG_ENABLE_OBJC_WEAK = YES; 223 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 224 | CLANG_WARN_BOOL_CONVERSION = YES; 225 | CLANG_WARN_COMMA = YES; 226 | CLANG_WARN_CONSTANT_CONVERSION = YES; 227 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 228 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 229 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 230 | CLANG_WARN_EMPTY_BODY = YES; 231 | CLANG_WARN_ENUM_CONVERSION = YES; 232 | CLANG_WARN_INFINITE_RECURSION = YES; 233 | CLANG_WARN_INT_CONVERSION = YES; 234 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 235 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 236 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 237 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 238 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 239 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 240 | CLANG_WARN_STRICT_PROTOTYPES = YES; 241 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 242 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 243 | CLANG_WARN_UNREACHABLE_CODE = YES; 244 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 245 | COPY_PHASE_STRIP = NO; 246 | DEBUG_INFORMATION_FORMAT = dwarf; 247 | ENABLE_STRICT_OBJC_MSGSEND = YES; 248 | ENABLE_TESTABILITY = YES; 249 | GCC_C_LANGUAGE_STANDARD = gnu11; 250 | GCC_DYNAMIC_NO_PIC = NO; 251 | GCC_NO_COMMON_BLOCKS = YES; 252 | GCC_OPTIMIZATION_LEVEL = 0; 253 | GCC_PREPROCESSOR_DEFINITIONS = ( 254 | "DEBUG=1", 255 | "$(inherited)", 256 | ); 257 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 258 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 259 | GCC_WARN_UNDECLARED_SELECTOR = YES; 260 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 261 | GCC_WARN_UNUSED_FUNCTION = YES; 262 | GCC_WARN_UNUSED_VARIABLE = YES; 263 | IPHONEOS_DEPLOYMENT_TARGET = 14.2; 264 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 265 | MTL_FAST_MATH = YES; 266 | ONLY_ACTIVE_ARCH = YES; 267 | SDKROOT = iphoneos; 268 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 269 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 270 | }; 271 | name = Debug; 272 | }; 273 | 55B9EB9E2572FBC90050C887 /* Release */ = { 274 | isa = XCBuildConfiguration; 275 | buildSettings = { 276 | ALWAYS_SEARCH_USER_PATHS = NO; 277 | CLANG_ANALYZER_NONNULL = YES; 278 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 279 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 280 | CLANG_CXX_LIBRARY = "libc++"; 281 | CLANG_ENABLE_MODULES = YES; 282 | CLANG_ENABLE_OBJC_ARC = YES; 283 | CLANG_ENABLE_OBJC_WEAK = YES; 284 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 285 | CLANG_WARN_BOOL_CONVERSION = YES; 286 | CLANG_WARN_COMMA = YES; 287 | CLANG_WARN_CONSTANT_CONVERSION = YES; 288 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 289 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 290 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 291 | CLANG_WARN_EMPTY_BODY = YES; 292 | CLANG_WARN_ENUM_CONVERSION = YES; 293 | CLANG_WARN_INFINITE_RECURSION = YES; 294 | CLANG_WARN_INT_CONVERSION = YES; 295 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 296 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 297 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 298 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 299 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 300 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 301 | CLANG_WARN_STRICT_PROTOTYPES = YES; 302 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 303 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 304 | CLANG_WARN_UNREACHABLE_CODE = YES; 305 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 306 | COPY_PHASE_STRIP = NO; 307 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 308 | ENABLE_NS_ASSERTIONS = NO; 309 | ENABLE_STRICT_OBJC_MSGSEND = YES; 310 | GCC_C_LANGUAGE_STANDARD = gnu11; 311 | GCC_NO_COMMON_BLOCKS = YES; 312 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 313 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 314 | GCC_WARN_UNDECLARED_SELECTOR = YES; 315 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 316 | GCC_WARN_UNUSED_FUNCTION = YES; 317 | GCC_WARN_UNUSED_VARIABLE = YES; 318 | IPHONEOS_DEPLOYMENT_TARGET = 14.2; 319 | MTL_ENABLE_DEBUG_INFO = NO; 320 | MTL_FAST_MATH = YES; 321 | SDKROOT = iphoneos; 322 | SWIFT_COMPILATION_MODE = wholemodule; 323 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 324 | VALIDATE_PRODUCT = YES; 325 | }; 326 | name = Release; 327 | }; 328 | 55B9EBA02572FBC90050C887 /* Debug */ = { 329 | isa = XCBuildConfiguration; 330 | buildSettings = { 331 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 332 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 333 | CODE_SIGN_STYLE = Automatic; 334 | DEVELOPMENT_ASSET_PATHS = ""; 335 | DEVELOPMENT_TEAM = NG36MJV4JP; 336 | ENABLE_PREVIEWS = YES; 337 | INFOPLIST_FILE = Declarative_UIKit/Info.plist; 338 | LD_RUNPATH_SEARCH_PATHS = ( 339 | "$(inherited)", 340 | "@executable_path/Frameworks", 341 | ); 342 | PRODUCT_BUNDLE_IDENTIFIER = "com.eppz.Declarative-UIKit"; 343 | PRODUCT_NAME = "$(TARGET_NAME)"; 344 | SWIFT_VERSION = 5.0; 345 | TARGETED_DEVICE_FAMILY = "1,2"; 346 | }; 347 | name = Debug; 348 | }; 349 | 55B9EBA12572FBC90050C887 /* Release */ = { 350 | isa = XCBuildConfiguration; 351 | buildSettings = { 352 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 353 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 354 | CODE_SIGN_STYLE = Automatic; 355 | DEVELOPMENT_ASSET_PATHS = ""; 356 | DEVELOPMENT_TEAM = NG36MJV4JP; 357 | ENABLE_PREVIEWS = YES; 358 | INFOPLIST_FILE = Declarative_UIKit/Info.plist; 359 | LD_RUNPATH_SEARCH_PATHS = ( 360 | "$(inherited)", 361 | "@executable_path/Frameworks", 362 | ); 363 | PRODUCT_BUNDLE_IDENTIFIER = "com.eppz.Declarative-UIKit"; 364 | PRODUCT_NAME = "$(TARGET_NAME)"; 365 | SWIFT_VERSION = 5.0; 366 | TARGETED_DEVICE_FAMILY = "1,2"; 367 | }; 368 | name = Release; 369 | }; 370 | /* End XCBuildConfiguration section */ 371 | 372 | /* Begin XCConfigurationList section */ 373 | 55B9EB862572FBC30050C887 /* Build configuration list for PBXProject "Declarative_UIKit" */ = { 374 | isa = XCConfigurationList; 375 | buildConfigurations = ( 376 | 55B9EB9D2572FBC90050C887 /* Debug */, 377 | 55B9EB9E2572FBC90050C887 /* Release */, 378 | ); 379 | defaultConfigurationIsVisible = 0; 380 | defaultConfigurationName = Release; 381 | }; 382 | 55B9EB9F2572FBC90050C887 /* Build configuration list for PBXNativeTarget "Declarative_UIKit" */ = { 383 | isa = XCConfigurationList; 384 | buildConfigurations = ( 385 | 55B9EBA02572FBC90050C887 /* Debug */, 386 | 55B9EBA12572FBC90050C887 /* Release */, 387 | ); 388 | defaultConfigurationIsVisible = 0; 389 | defaultConfigurationName = Release; 390 | }; 391 | /* End XCConfigurationList section */ 392 | }; 393 | rootObject = 55B9EB832572FBC30050C887 /* Project object */; 394 | } 395 | -------------------------------------------------------------------------------- /Declarative_UIKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Declarative_UIKit.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Declarative_UIKit.xcodeproj/xcuserdata/eppz.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /Declarative_UIKit.xcodeproj/xcuserdata/eppz.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Declarative_UIKit.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | Declerative_UIKit.xcscheme_^#shared#^_ 13 | 14 | orderHint 15 | 0 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Declarative_UIKit/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Declarative_UIKit 4 | // 5 | // Created by Geri Borbás on 28/11/2020. 6 | // http://www.twitter.com/Geri_Borbas 7 | // 8 | 9 | import UIKit 10 | 11 | 12 | @main 13 | class AppDelegate: UIResponder, UIApplicationDelegate { 14 | 15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 16 | return true 17 | } 18 | 19 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 20 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 21 | } 22 | } 23 | 24 | -------------------------------------------------------------------------------- /Declarative_UIKit/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 | -------------------------------------------------------------------------------- /Declarative_UIKit/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 | -------------------------------------------------------------------------------- /Declarative_UIKit/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Declarative_UIKit/Assets.xcassets/Earth.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "Earth.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "Earth@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "Earth@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Declarative_UIKit/Assets.xcassets/Earth.imageset/Earth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Geri-Borbas/iOS.Blog.Declarative_UIKit/b20f281a51e725c3fcf9596646ba7030041175c8/Declarative_UIKit/Assets.xcassets/Earth.imageset/Earth.png -------------------------------------------------------------------------------- /Declarative_UIKit/Assets.xcassets/Earth.imageset/Earth@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Geri-Borbas/iOS.Blog.Declarative_UIKit/b20f281a51e725c3fcf9596646ba7030041175c8/Declarative_UIKit/Assets.xcassets/Earth.imageset/Earth@2x.png -------------------------------------------------------------------------------- /Declarative_UIKit/Assets.xcassets/Earth.imageset/Earth@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Geri-Borbas/iOS.Blog.Declarative_UIKit/b20f281a51e725c3fcf9596646ba7030041175c8/Declarative_UIKit/Assets.xcassets/Earth.imageset/Earth@3x.png -------------------------------------------------------------------------------- /Declarative_UIKit/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 | -------------------------------------------------------------------------------- /Declarative_UIKit/Extensions/PreviewView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PreviewView.swift 3 | // Declarative_UIKit 4 | // 5 | // Created by Geri Borbás on 30/11/2020. 6 | // 7 | 8 | import SwiftUI 9 | 10 | 11 | struct PreviewView: UIViewControllerRepresentable { 12 | 13 | let `for`: ViewControllerType 14 | 15 | func makeUIViewController(context: Context) -> ViewControllerType { 16 | `for` 17 | } 18 | 19 | func updateUIViewController(_ viewController: ViewControllerType, context: Context) { 20 | 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Declarative_UIKit/Extensions/UIKit+Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIStackView+Extensions.swift 3 | // Declarative_UIKit 4 | // 5 | // Created by Geri Borbás on 29/11/2020. 6 | // http://www.twitter.com/Geri_Borbas 7 | // 8 | 9 | import UIKit 10 | 11 | 12 | extension UIView { 13 | 14 | static var spacer: UIView { 15 | UIView().with { 16 | $0.setContentHuggingPriority(.required, for: .horizontal) 17 | $0.setContentHuggingPriority(.required, for: .vertical) 18 | } 19 | } 20 | 21 | func pin(to: UILayoutGuide, insets: UIEdgeInsets) { 22 | guard let _ = superview else { 23 | return 24 | } 25 | 26 | translatesAutoresizingMaskIntoConstraints = false 27 | topAnchor.constraint(equalTo: to.topAnchor, constant: insets.top).isActive = true 28 | bottomAnchor.constraint(equalTo: to.bottomAnchor, constant: -insets.bottom).isActive = true 29 | leftAnchor.constraint(equalTo: to.leftAnchor, constant: insets.left).isActive = true 30 | rightAnchor.constraint(equalTo: to.rightAnchor, constant: -insets.right).isActive = true 31 | } 32 | 33 | func pin(to: UIView, insets: UIEdgeInsets) { 34 | guard let _ = superview else { 35 | return 36 | } 37 | 38 | translatesAutoresizingMaskIntoConstraints = false 39 | topAnchor.constraint(equalTo: to.topAnchor, constant: insets.top).isActive = true 40 | bottomAnchor.constraint(equalTo: to.bottomAnchor, constant: -insets.bottom).isActive = true 41 | leftAnchor.constraint(equalTo: to.leftAnchor, constant: insets.left).isActive = true 42 | rightAnchor.constraint(equalTo: to.rightAnchor, constant: -insets.right).isActive = true 43 | } 44 | 45 | var withRedLines: UIView { 46 | with { 47 | $0.layer.borderWidth = 1 48 | $0.layer.cornerRadius = 2 49 | $0.layer.borderColor = UIColor.red.withAlphaComponent(0.3).cgColor 50 | $0.backgroundColor = UIColor.red.withAlphaComponent(0.1) 51 | } 52 | } 53 | 54 | var inspect: UIView { 55 | withRedLines 56 | } 57 | } 58 | 59 | 60 | extension UIStackView { 61 | 62 | func horizontal(spacing: CGFloat = 0) -> Self { 63 | with { 64 | $0.axis = .horizontal 65 | $0.spacing = spacing 66 | } 67 | } 68 | 69 | func vertical(spacing: CGFloat = 0) -> Self { 70 | with { 71 | $0.axis = .vertical 72 | $0.spacing = spacing 73 | } 74 | } 75 | 76 | func views(_ views: UIView ...) -> Self { 77 | views.forEach { self.addArrangedSubview($0) } 78 | return self 79 | } 80 | } 81 | 82 | 83 | extension UILabel { 84 | 85 | func with(text: String?) -> Self { 86 | with { 87 | $0.text = text 88 | } 89 | } 90 | } 91 | 92 | 93 | extension UIImageView { 94 | 95 | func with(image: UIImage?) -> Self { 96 | with { 97 | $0.image = image 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Declarative_UIKit/Extensions/Withable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Withable.swift 3 | // Declarative_UIKit 4 | // 5 | // Created by Geri Borbás on 28/11/2020. 6 | // http://www.twitter.com/Geri_Borbas 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | // MARK: - Withable for Objects 13 | 14 | protocol ObjectWithable: class { 15 | 16 | associatedtype T 17 | 18 | /// Provides a closure to configure instances inline. 19 | /// - Parameter closure: A closure `self` as the argument. 20 | /// - Returns: Simply returns the instance after called the `closure`. 21 | @discardableResult func with(_ closure: (_ instance: T) -> Void) -> T 22 | } 23 | 24 | extension ObjectWithable { 25 | 26 | @discardableResult func with(_ closure: (_ instance: Self) -> Void) -> Self { 27 | closure(self) 28 | return self 29 | } 30 | } 31 | 32 | extension NSObject: ObjectWithable { } 33 | 34 | 35 | // MARK: - Withable for Values 36 | 37 | protocol Withable { 38 | 39 | associatedtype T 40 | 41 | /// Provides a closure to configure instances inline. 42 | /// - Parameter closure: A closure with a mutable copy of `self` as the argument. 43 | /// - Returns: Simply returns the mutated copy of the instance after called the `closure`. 44 | @discardableResult func with(_ closure: (_ instance: inout T) -> Void) -> T 45 | } 46 | 47 | extension Withable { 48 | 49 | @discardableResult func with(_ closure: (_ instance: inout Self) -> Void) -> Self { 50 | var copy = self 51 | closure(©) 52 | return copy 53 | } 54 | } 55 | 56 | 57 | // MARK: - Examples 58 | 59 | struct Point: Withable { 60 | var x: Int = 0 61 | var y: Int = 0 62 | } 63 | 64 | extension PersonNameComponents: Withable { } 65 | 66 | struct Test { 67 | 68 | let formatter = DateFormatter().with { 69 | $0.dateStyle = .medium 70 | } 71 | 72 | let point = Point().with { 73 | $0.x = 10 74 | $0.y = 10 75 | } 76 | 77 | let name = PersonNameComponents().with { 78 | $0.givenName = "Geri" 79 | $0.familyName = "Borbás" 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Declarative_UIKit/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 | 37 | 38 | 39 | 40 | UIApplicationSupportsIndirectInputEvents 41 | 42 | UILaunchStoryboardName 43 | LaunchScreen 44 | UIRequiredDeviceCapabilities 45 | 46 | armv7 47 | 48 | UISupportedInterfaceOrientations 49 | 50 | UIInterfaceOrientationPortrait 51 | UIInterfaceOrientationLandscapeLeft 52 | UIInterfaceOrientationLandscapeRight 53 | 54 | UISupportedInterfaceOrientations~ipad 55 | 56 | UIInterfaceOrientationPortrait 57 | UIInterfaceOrientationPortraitUpsideDown 58 | UIInterfaceOrientationLandscapeLeft 59 | UIInterfaceOrientationLandscapeRight 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /Declarative_UIKit/Model/Planets.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Data.swift 3 | // Declarative_UIKit 4 | // 5 | // Created by Geri Borbás on 29/11/2020. 6 | // http://www.twitter.com/Geri_Borbas 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | struct Planets { 13 | 14 | let earth = ( 15 | title: "Earth", 16 | properties: ( 17 | size: "6,378 km", 18 | distance: "1.0 AU", 19 | mass: "5.972 × 10²⁴ kg" 20 | ), 21 | imageAssetName: "Earth", 22 | paragraphs: [ 23 | "Earth is the third planet from the Sun and the only astronomical object known to harbor life. About 29% of Earth's surface is land consisting of continents and islands. The remaining 71% is covered with water, mostly by oceans but also lakes, rivers and other fresh water, which together constitute the hydrosphere.", 24 | "Much of Earth's polar regions are covered in ice. Earth's outer layer is divided into several rigid tectonic plates that migrate across the surface over many millions of years." 25 | ] 26 | ) 27 | } 28 | -------------------------------------------------------------------------------- /Declarative_UIKit/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // Declarative_UIKit 4 | // 5 | // Created by Geri Borbás on 28/11/2020. 6 | // http://www.twitter.com/Geri_Borbas 7 | // 8 | 9 | import UIKit 10 | 11 | 12 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 13 | 14 | var window: UIWindow? 15 | var rootViewController: UIViewController { ContentViewController_1() } 16 | 17 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 18 | if let windowScene = scene as? UIWindowScene { 19 | self.window = UIWindow(windowScene: windowScene).with { 20 | $0.rootViewController = rootViewController 21 | $0.makeKeyAndVisible() 22 | } 23 | } 24 | } 25 | } 26 | 27 | -------------------------------------------------------------------------------- /Declarative_UIKit/Views/ContentViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentViewController.swift 3 | // Declarative_UIKit 4 | // 5 | // Created by Geri Borbás on 30/11/2020. 6 | // http://www.twitter.com/Geri_Borbas 7 | // 8 | 9 | import UIKit 10 | 11 | class ContentViewController: UIViewController { 12 | 13 | let viewModel = Planets().earth 14 | 15 | lazy var body = UIStackView().vertical(spacing: 10).views( 16 | UILabel() 17 | .with(text: viewModel.title) 18 | .withTitleStyle, 19 | UIStackView().vertical(spacing: 5).views( 20 | UIStackView().horizontal(spacing: 5).views( 21 | UILabel() 22 | .with(text: "size") 23 | .withPropertyStyle 24 | .withBox, 25 | UILabel() 26 | .with(text: viewModel.properties.size) 27 | .withPropertyValueStyle, 28 | UIView.spacer 29 | ), 30 | UIStackView().horizontal(spacing: 5).views( 31 | UILabel() 32 | .with(text: "distance") 33 | .withPropertyStyle 34 | .withBox, 35 | UILabel() 36 | .with(text: viewModel.properties.distance) 37 | .withPropertyValueStyle, 38 | UIView.spacer 39 | ), 40 | UIStackView().horizontal(spacing: 5).views( 41 | UILabel() 42 | .with(text: "mass") 43 | .withPropertyStyle 44 | .withBox, 45 | UILabel() 46 | .with(text: viewModel.properties.mass) 47 | .withPropertyValueStyle, 48 | UIView.spacer 49 | ) 50 | ), 51 | UIImageView() 52 | .with(image: UIImage(named: viewModel.imageAssetName)), 53 | UILabel() 54 | .with(text: viewModel.paragraphs.first) 55 | .withParagraphStyle, 56 | UILabel() 57 | .with(text: viewModel.paragraphs.last) 58 | .withParagraphStyle, 59 | UIView.spacer 60 | ) 61 | 62 | 63 | // MARK: Initialization 64 | 65 | override func viewDidLoad() { 66 | super.viewDidLoad() 67 | view.addSubview(body) 68 | view.backgroundColor = .systemBackground 69 | body.pin( 70 | to: view.safeAreaLayoutGuide, 71 | insets: UIEdgeInsets(top: 30, left: 30, bottom: 30, right: 30) 72 | ) 73 | } 74 | } 75 | 76 | 77 | // MARK: - Styles 78 | 79 | fileprivate extension UILabel { 80 | 81 | var withTitleStyle: UILabel { 82 | with { 83 | $0.textColor = .label 84 | $0.font = .preferredFont(forTextStyle: .largeTitle) 85 | } 86 | } 87 | 88 | var withPropertyStyle: UILabel { 89 | with { 90 | $0.textColor = .systemBackground 91 | $0.font = .preferredFont(forTextStyle: .headline) 92 | $0.setContentCompressionResistancePriority(.required, for: .vertical) 93 | } 94 | } 95 | 96 | var withPropertyValueStyle: UILabel { 97 | with { 98 | $0.textColor = .systemGray 99 | $0.font = .preferredFont(forTextStyle: .body) 100 | } 101 | } 102 | 103 | var withParagraphStyle: UILabel { 104 | with { 105 | $0.textColor = .label 106 | $0.numberOfLines = 0 107 | $0.font = .preferredFont(forTextStyle: .footnote) 108 | } 109 | } 110 | } 111 | 112 | extension UIView { 113 | 114 | var withBoxStyle: UIView { 115 | with { 116 | $0.backgroundColor = .systemGray 117 | $0.layer.cornerRadius = 5 118 | } 119 | } 120 | 121 | var withBox: UIView { 122 | UIView().withBoxStyle.with { 123 | $0.addSubview(self) 124 | self.pin( 125 | to: $0, 126 | insets: UIEdgeInsets(top: 2, left: 5, bottom: 2, right: 5) 127 | ) 128 | } 129 | } 130 | } 131 | 132 | 133 | // MARK: - Previews 134 | 135 | import SwiftUI 136 | 137 | struct DetailViewController_Previews: PreviewProvider { 138 | static var previews: some View { 139 | PreviewView(for: ContentViewController()) 140 | .environment(\.colorScheme, .dark) 141 | .edgesIgnoringSafeArea(.all) 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /Declarative_UIKit/Views/ContentViewController_1.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentViewController_1.swift 3 | // Declarative_UIKit 4 | // 5 | // Created by Geri Borbás on 28/11/2020. 6 | // http://www.twitter.com/Geri_Borbas 7 | // 8 | 9 | import UIKit 10 | 11 | 12 | class ContentViewController_1: UIViewController { 13 | 14 | let viewModel = Planets().earth 15 | 16 | lazy var stackView = UIStackView().with { 17 | $0.axis = .vertical 18 | $0.spacing = 10 19 | [ 20 | UILabel().with { 21 | $0.text = viewModel.title 22 | $0.textColor = .label 23 | $0.font = .preferredFont(forTextStyle: .largeTitle) 24 | }, 25 | UILabel().with { 26 | $0.text = """ 27 | size: \(viewModel.properties.size) 28 | distance: \(viewModel.properties.distance) 29 | mass: \(viewModel.properties.mass) 30 | """ 31 | $0.textColor = .systemGray 32 | $0.numberOfLines = 0 33 | $0.font = .preferredFont(forTextStyle: .headline) 34 | }, 35 | UIImageView().with { 36 | $0.image = UIImage(named: viewModel.imageAssetName) 37 | }, 38 | UILabel().with { 39 | $0.text = viewModel.paragraphs.first 40 | $0.textColor = .label 41 | $0.numberOfLines = 0 42 | $0.font = .preferredFont(forTextStyle: .footnote) 43 | }, 44 | UILabel().with { 45 | $0.text = viewModel.paragraphs.last 46 | $0.textColor = .label 47 | $0.numberOfLines = 0 48 | $0.font = .preferredFont(forTextStyle: .footnote) 49 | }, 50 | UIView() 51 | ].add(to: $0) 52 | } 53 | 54 | override func viewDidLoad() { 55 | super.viewDidLoad() 56 | view.backgroundColor = .systemBackground 57 | view.addSubview(stackView) 58 | stackView.with { 59 | $0.translatesAutoresizingMaskIntoConstraints = false 60 | $0.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 30).isActive = true 61 | $0.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -30).isActive = true 62 | $0.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: 30).isActive = true 63 | $0.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor, constant: -30).isActive = true 64 | } 65 | } 66 | } 67 | 68 | 69 | fileprivate extension UIView { 70 | 71 | func addRedLines() { 72 | layer.borderWidth = 1 73 | layer.cornerRadius = 2 74 | layer.borderColor = UIColor.red.withAlphaComponent(0.3).cgColor 75 | backgroundColor = UIColor.red.withAlphaComponent(0.1) 76 | } 77 | } 78 | 79 | 80 | fileprivate extension Array where Element: UIView { 81 | 82 | func add(to stackView: UIStackView) { 83 | forEach { stackView.addArrangedSubview($0) } 84 | } 85 | } 86 | 87 | 88 | // MARK: - Previews 89 | 90 | import SwiftUI 91 | 92 | struct WithableViewController_Previews: PreviewProvider { 93 | static var previews: some View { 94 | PreviewView(for: ContentViewController()) 95 | .environment(\.colorScheme, .dark) 96 | .edgesIgnoringSafeArea(.all) 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Declarative_UIKit/Views/ExamplesViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExamplesViewController.swift 3 | // Declarative_UIKit 4 | // 5 | // Created by Geri Borbás on 30/11/2020. 6 | // 7 | 8 | import UIKit 9 | 10 | 11 | class ExamplesViewController: UIViewController { 12 | 13 | private let label = UILabel().with { 14 | $0.text = "Hello World!" 15 | $0.textColor = .label 16 | $0.font = .preferredFont(forTextStyle: .largeTitle) 17 | } 18 | 19 | private lazy var submitButton = UIButton().with { 20 | $0.setTitle("Submit", for: .normal) 21 | $0.addTarget(self, action: #selector(didTapSubmitButton), for: .touchUpInside) 22 | } 23 | 24 | private lazy var displayLink = CADisplayLink( 25 | target: self, 26 | selector: #selector(update) 27 | ).with { 28 | $0.isPaused = true 29 | $0.preferredFramesPerSecond = 120 30 | $0.add(to: RunLoop.main, forMode: .common) 31 | } 32 | 33 | override func viewDidLoad() { 34 | super.viewDidLoad() 35 | 36 | // Hierarchy. 37 | view.addSubview(label) 38 | 39 | // Constraints. 40 | label.translatesAutoresizingMaskIntoConstraints = false 41 | label.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 30).isActive = true 42 | label.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -30).isActive = true 43 | label.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 30).isActive = true 44 | } 45 | 46 | @objc func didTapSubmitButton() { 47 | present(TitleLabelViewController().with { 48 | $0.modalTransitionStyle = .crossDissolve 49 | $0.modalPresentationStyle = .overCurrentContext 50 | }, animated: true) 51 | } 52 | 53 | @objc func update() { } 54 | 55 | func alert() { 56 | 57 | let title = "" 58 | let message = "" 59 | 60 | present(UIAlertController( 61 | title: title, 62 | message: message, 63 | preferredStyle: .alert 64 | ).with { 65 | $0.addAction(UIAlertAction(title: "Ok", style: .default, handler: nil)) 66 | }, animated: true) 67 | } 68 | 69 | func date() { 70 | let _ = DateFormatter().with { 71 | $0.dateStyle = .medium 72 | $0.locale = Locale(identifier: "en_US") 73 | }.string(from: Date()) 74 | } 75 | 76 | func compound() { 77 | present(UIAlertController(title: "Select file", message: nil, preferredStyle: .actionSheet).with { 78 | if UIImagePickerController.isSourceTypeAvailable(.camera) { 79 | $0.addAction(UIAlertAction(title: "From camera", style: .default) { _ in 80 | self.present(UIImagePickerController().with { 81 | $0.delegate = self 82 | $0.sourceType = .camera 83 | $0.cameraCaptureMode = .photo 84 | }, animated: true) 85 | }) 86 | } 87 | if UIImagePickerController.isSourceTypeAvailable(.photoLibrary) { 88 | $0.addAction(UIAlertAction(title: "From library", style: .default) { _ in 89 | self.present(UIImagePickerController().with { 90 | $0.delegate = self 91 | $0.sourceType = .photoLibrary 92 | }, animated: true) 93 | }) 94 | } 95 | $0.addAction(UIAlertAction(title: "Cancel", style: .cancel) { _ in }) 96 | }, animated: true) 97 | } 98 | } 99 | 100 | 101 | extension ExamplesViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate { 102 | 103 | func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { } 104 | } 105 | -------------------------------------------------------------------------------- /Declarative_UIKit/Views/ImperativeViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImperativeViewController.swift 3 | // Declarative_UIKit 4 | // 5 | // Created by Geri Borbás on 29/11/2020. 6 | // http://www.twitter.com/Geri_Borbas 7 | // 8 | 9 | import UIKit 10 | 11 | 12 | class ImperativeViewController: UIViewController { 13 | 14 | let viewModel = Planets().earth 15 | 16 | private let titleLabel = UILabel() 17 | private let propertiesLabel = UILabel() 18 | private let imageView = UIImageView() 19 | private let firstParagraphLabel = UILabel() 20 | private let lastParagraphLabel = UILabel() 21 | 22 | override func viewDidLoad() { 23 | super.viewDidLoad() 24 | view.backgroundColor = .systemBackground 25 | 26 | // Configure. 27 | titleLabel.text = viewModel.title 28 | titleLabel.textColor = .label 29 | titleLabel.font = .preferredFont(forTextStyle: .largeTitle) 30 | 31 | propertiesLabel.text = """ 32 | size: \(viewModel.properties.size) 33 | distance: \(viewModel.properties.distance) 34 | mass: \(viewModel.properties.mass) 35 | """ 36 | propertiesLabel.textColor = .systemGray 37 | propertiesLabel.numberOfLines = 0 38 | propertiesLabel.font = .preferredFont(forTextStyle: .headline) 39 | 40 | imageView.image = UIImage(named: viewModel.imageAssetName) 41 | 42 | firstParagraphLabel.text = viewModel.paragraphs.first 43 | firstParagraphLabel.textColor = .label 44 | firstParagraphLabel.numberOfLines = 0 45 | firstParagraphLabel.font = .preferredFont(forTextStyle: .footnote) 46 | 47 | lastParagraphLabel.text = viewModel.paragraphs.last 48 | lastParagraphLabel.textColor = .label 49 | lastParagraphLabel.numberOfLines = 0 50 | lastParagraphLabel.font = .preferredFont(forTextStyle: .footnote) 51 | 52 | // Red lines. 53 | titleLabel.addRedLines() 54 | propertiesLabel.addRedLines() 55 | imageView.addRedLines() 56 | firstParagraphLabel.addRedLines() 57 | lastParagraphLabel.addRedLines() 58 | 59 | // Hierarchy. 60 | view.addSubview(titleLabel) 61 | view.addSubview(propertiesLabel) 62 | view.addSubview(imageView) 63 | view.addSubview(firstParagraphLabel) 64 | view.addSubview(lastParagraphLabel) 65 | 66 | // Constraints. 67 | titleLabel.translatesAutoresizingMaskIntoConstraints = false 68 | propertiesLabel.translatesAutoresizingMaskIntoConstraints = false 69 | imageView.translatesAutoresizingMaskIntoConstraints = false 70 | firstParagraphLabel.translatesAutoresizingMaskIntoConstraints = false 71 | lastParagraphLabel.translatesAutoresizingMaskIntoConstraints = false 72 | 73 | view.addConstraints([ 74 | 75 | titleLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 30), 76 | titleLabel.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: 30), 77 | titleLabel.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor, constant: -30), 78 | 79 | propertiesLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 10), 80 | propertiesLabel.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: 30), 81 | propertiesLabel.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor, constant: -30), 82 | 83 | imageView.topAnchor.constraint(equalTo: propertiesLabel.bottomAnchor, constant: 10), 84 | imageView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: 30), 85 | imageView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor, constant: -30), 86 | 87 | firstParagraphLabel.topAnchor.constraint(equalTo: imageView.bottomAnchor, constant: 10), 88 | firstParagraphLabel.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: 30), 89 | firstParagraphLabel.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor, constant: -30), 90 | 91 | lastParagraphLabel.topAnchor.constraint(equalTo: firstParagraphLabel.bottomAnchor, constant: 10), 92 | lastParagraphLabel.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: 30), 93 | lastParagraphLabel.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor, constant: -30) 94 | ]) 95 | } 96 | } 97 | 98 | 99 | fileprivate extension UIView { 100 | 101 | 102 | func addRedLines() { 103 | layer.borderWidth = 1 104 | layer.cornerRadius = 2 105 | layer.borderColor = UIColor.red.withAlphaComponent(0.3).cgColor 106 | backgroundColor = UIColor.red.withAlphaComponent(0.1) 107 | } 108 | } 109 | 110 | 111 | // MARK: - Previews 112 | 113 | import SwiftUI 114 | 115 | struct ImperativeViewController_Previews: PreviewProvider { 116 | static var previews: some View { 117 | PreviewView(for: ImperativeViewController()) 118 | .environment(\.colorScheme, .dark) 119 | .edgesIgnoringSafeArea(.all) 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /Declarative_UIKit/Views/MultiStackViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EmbeddedStackViewsViewController.swift 3 | // Declerative_UIKit 4 | // 5 | // Created by Geri Borbás on 29/11/2020. 6 | // 7 | 8 | import UIKit 9 | 10 | 11 | class EmbeddedStackViewsViewController: UIViewController { 12 | 13 | 14 | // MARK: Views 15 | 16 | struct UI { 17 | static let insets = UIEdgeInsets(top: 30, left: 30, bottom: 30, right: 30) 18 | static let spacing = CGFloat(10) 19 | } 20 | 21 | private lazy var stackView = UIStackView().vertical(spacing: UI.spacing).withViews( 22 | UILabel().withTitleStyle.with { $0.text = Data.earth.title }.inspect, 23 | UIStackView().vertical(spacing: 5).withViews( 24 | UIStackView().horizontal(spacing: 5).withViews( 25 | UILabel().with { $0.text = "size" }.inspect, 26 | UILabel().with { $0.text = Data.earth.properties.size }.inspect 27 | ), 28 | UIStackView().horizontal(spacing: 5).withViews( 29 | UILabel().with { $0.text = "distance" }.inspect, 30 | UILabel().with { $0.text = Data.earth.properties.distance }.inspect 31 | ), 32 | UIStackView().horizontal(spacing: 5).withViews( 33 | UILabel().with { $0.text = "mass" }.inspect, 34 | UILabel().with { $0.text = Data.earth.properties.mass }.inspect 35 | ) 36 | ), 37 | UIImageView().with { $0.image = UIImage(named: Data.earth.imageAssetName) }.inspect, 38 | UILabel().withParagraphStyle.with { $0.text = Data.earth.paragraphs.first }.inspect, 39 | UILabel().withParagraphStyle.with { $0.text = Data.earth.paragraphs.last }.inspect, 40 | UIView.spacer.inspect 41 | ) 42 | 43 | 44 | // MARK: Initialization 45 | 46 | override func viewDidLoad() { 47 | super.viewDidLoad() 48 | view.addSubview(stackView) 49 | stackView.add(insets: UI.insets, to: view.safeAreaLayoutGuide) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Declarative_UIKit/Views/TitleLabelViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TitleLabelViewController.swift 3 | // Declarative_UIKit 4 | // 5 | // Created by Geri Borbás on 29/11/2020. 6 | // http://www.twitter.com/Geri_Borbas 7 | // 8 | 9 | import UIKit 10 | 11 | 12 | class TitleLabelViewController: UIViewController { 13 | 14 | // MARK: Properties 15 | 16 | let viewModel = Planets() 17 | 18 | // MARK: Initialization and Views 19 | 20 | override func viewDidLoad() { 21 | super.viewDidLoad() 22 | 23 | let titleLabel = UILabel() 24 | titleLabel.text = viewModel.earth.title 25 | titleLabel.textColor = .label 26 | titleLabel.font = .preferredFont(forTextStyle: .largeTitle) 27 | 28 | view.addSubview(titleLabel) 29 | 30 | titleLabel.translatesAutoresizingMaskIntoConstraints = false 31 | titleLabel.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 30).isActive = true 32 | titleLabel.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -30).isActive = true 33 | titleLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 30).isActive = true 34 | } 35 | } 36 | 37 | 38 | class TitleLabelViewController_2: UIViewController { 39 | 40 | // MARK: Properties 41 | 42 | let viewModel = Planets() 43 | 44 | // MARK: Views 45 | 46 | lazy var titleLabel: UILabel = { 47 | let label = UILabel() 48 | label.text = viewModel.earth.title 49 | label.textColor = .label 50 | label.font = .preferredFont(forTextStyle: .largeTitle) 51 | return label 52 | }() 53 | 54 | // MARK: Initialization 55 | 56 | override func viewDidLoad() { 57 | super.viewDidLoad() 58 | view.addSubview(titleLabel) 59 | 60 | titleLabel.translatesAutoresizingMaskIntoConstraints = false 61 | titleLabel.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 30).isActive = true 62 | titleLabel.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -30).isActive = true 63 | titleLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 30).isActive = true 64 | } 65 | } 66 | 67 | 68 | class TitleLabelViewController_3: UIViewController { 69 | 70 | let viewModel = Planets().earth 71 | 72 | lazy var titleLabel = UILabel().with { 73 | $0.text = viewModel.title 74 | $0.textColor = .label 75 | $0.font = .preferredFont(forTextStyle: .largeTitle) 76 | } 77 | 78 | // MARK: Initialization 79 | 80 | override func viewDidLoad() { 81 | super.viewDidLoad() 82 | 83 | // Hierarchy. 84 | view.addSubview(titleLabel) 85 | 86 | // Constraints. 87 | titleLabel.translatesAutoresizingMaskIntoConstraints = false 88 | titleLabel.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 30).isActive = true 89 | titleLabel.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -30).isActive = true 90 | titleLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 30).isActive = true 91 | } 92 | } 93 | 94 | 95 | fileprivate extension UILabel { 96 | 97 | func with(_ closure: (UILabel) -> Void) -> UILabel { 98 | closure(self) 99 | return self 100 | } 101 | } 102 | --------------------------------------------------------------------------------