From 578db11a25c1926a650de337ae3fec8c92df9206 Mon Sep 17 00:00:00 2001 From: Keith Harrison Date: Mon, 30 Nov 2015 20:06:58 +0000 Subject: [PATCH 001/184] Added AdaptivePopover project --- .../AdaptivePopover.xcodeproj/project.pbxproj | 306 ++++++++++++++++++ .../AdaptivePopover/AppDelegate.swift | 40 +++ .../AppIcon.appiconset/Contents.json | 68 ++++ .../Base.lproj/LaunchScreen.storyboard | 70 ++++ .../Base.lproj/Main.storyboard | 219 +++++++++++++ .../DetailViewController.swift | 47 +++ AdaptivePopover/AdaptivePopover/Info.plist | 47 +++ .../AdaptivePopover/RootViewController.swift | 107 ++++++ .../SimpleTableViewController.swift | 65 ++++ .../SimpleViewController.swift | 38 +++ README | 3 +- 11 files changed, 1009 insertions(+), 1 deletion(-) create mode 100644 AdaptivePopover/AdaptivePopover.xcodeproj/project.pbxproj create mode 100644 AdaptivePopover/AdaptivePopover/AppDelegate.swift create mode 100644 AdaptivePopover/AdaptivePopover/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 AdaptivePopover/AdaptivePopover/Base.lproj/LaunchScreen.storyboard create mode 100644 AdaptivePopover/AdaptivePopover/Base.lproj/Main.storyboard create mode 100644 AdaptivePopover/AdaptivePopover/DetailViewController.swift create mode 100644 AdaptivePopover/AdaptivePopover/Info.plist create mode 100644 AdaptivePopover/AdaptivePopover/RootViewController.swift create mode 100644 AdaptivePopover/AdaptivePopover/SimpleTableViewController.swift create mode 100644 AdaptivePopover/AdaptivePopover/SimpleViewController.swift diff --git a/AdaptivePopover/AdaptivePopover.xcodeproj/project.pbxproj b/AdaptivePopover/AdaptivePopover.xcodeproj/project.pbxproj new file mode 100644 index 0000000..442a648 --- /dev/null +++ b/AdaptivePopover/AdaptivePopover.xcodeproj/project.pbxproj @@ -0,0 +1,306 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 53BC63AE1C0A57E4009BFDDF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53BC63AD1C0A57E4009BFDDF /* AppDelegate.swift */; }; + 53BC63B31C0A57E4009BFDDF /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 53BC63B11C0A57E4009BFDDF /* Main.storyboard */; }; + 53BC63B51C0A57E4009BFDDF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 53BC63B41C0A57E4009BFDDF /* Assets.xcassets */; }; + 53BC63B81C0A57E4009BFDDF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 53BC63B61C0A57E4009BFDDF /* LaunchScreen.storyboard */; }; + 53BC63C01C0A591A009BFDDF /* RootViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53BC63BF1C0A591A009BFDDF /* RootViewController.swift */; }; + 53BC63C21C0A596C009BFDDF /* SimpleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53BC63C11C0A596C009BFDDF /* SimpleViewController.swift */; }; + 53BC63C41C0A5F59009BFDDF /* SimpleTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53BC63C31C0A5F59009BFDDF /* SimpleTableViewController.swift */; }; + 53BC63C61C0A5F72009BFDDF /* DetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53BC63C51C0A5F72009BFDDF /* DetailViewController.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 53BC63AA1C0A57E4009BFDDF /* AdaptivePopover.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AdaptivePopover.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 53BC63AD1C0A57E4009BFDDF /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 53BC63B21C0A57E4009BFDDF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 53BC63B41C0A57E4009BFDDF /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 53BC63B71C0A57E4009BFDDF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 53BC63B91C0A57E4009BFDDF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 53BC63BF1C0A591A009BFDDF /* RootViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RootViewController.swift; sourceTree = ""; }; + 53BC63C11C0A596C009BFDDF /* SimpleViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SimpleViewController.swift; sourceTree = ""; }; + 53BC63C31C0A5F59009BFDDF /* SimpleTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SimpleTableViewController.swift; sourceTree = ""; }; + 53BC63C51C0A5F72009BFDDF /* DetailViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DetailViewController.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 53BC63A71C0A57E4009BFDDF /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 53BC63A11C0A57E4009BFDDF = { + isa = PBXGroup; + children = ( + 53BC63AC1C0A57E4009BFDDF /* AdaptivePopover */, + 53BC63AB1C0A57E4009BFDDF /* Products */, + ); + sourceTree = ""; + }; + 53BC63AB1C0A57E4009BFDDF /* Products */ = { + isa = PBXGroup; + children = ( + 53BC63AA1C0A57E4009BFDDF /* AdaptivePopover.app */, + ); + name = Products; + sourceTree = ""; + }; + 53BC63AC1C0A57E4009BFDDF /* AdaptivePopover */ = { + isa = PBXGroup; + children = ( + 53BC63AD1C0A57E4009BFDDF /* AppDelegate.swift */, + 53BC63B11C0A57E4009BFDDF /* Main.storyboard */, + 53BC63C31C0A5F59009BFDDF /* SimpleTableViewController.swift */, + 53BC63C51C0A5F72009BFDDF /* DetailViewController.swift */, + 53BC63C11C0A596C009BFDDF /* SimpleViewController.swift */, + 53BC63BF1C0A591A009BFDDF /* RootViewController.swift */, + 53BC63B41C0A57E4009BFDDF /* Assets.xcassets */, + 53BC63B61C0A57E4009BFDDF /* LaunchScreen.storyboard */, + 53BC63B91C0A57E4009BFDDF /* Info.plist */, + ); + path = AdaptivePopover; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 53BC63A91C0A57E4009BFDDF /* AdaptivePopover */ = { + isa = PBXNativeTarget; + buildConfigurationList = 53BC63BC1C0A57E4009BFDDF /* Build configuration list for PBXNativeTarget "AdaptivePopover" */; + buildPhases = ( + 53BC63A61C0A57E4009BFDDF /* Sources */, + 53BC63A71C0A57E4009BFDDF /* Frameworks */, + 53BC63A81C0A57E4009BFDDF /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = AdaptivePopover; + productName = AdaptivePopover; + productReference = 53BC63AA1C0A57E4009BFDDF /* AdaptivePopover.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 53BC63A21C0A57E4009BFDDF /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0710; + LastUpgradeCheck = 0710; + ORGANIZATIONNAME = "Keith Harrison"; + TargetAttributes = { + 53BC63A91C0A57E4009BFDDF = { + CreatedOnToolsVersion = 7.1.1; + }; + }; + }; + buildConfigurationList = 53BC63A51C0A57E4009BFDDF /* Build configuration list for PBXProject "AdaptivePopover" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 53BC63A11C0A57E4009BFDDF; + productRefGroup = 53BC63AB1C0A57E4009BFDDF /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 53BC63A91C0A57E4009BFDDF /* AdaptivePopover */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 53BC63A81C0A57E4009BFDDF /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 53BC63B81C0A57E4009BFDDF /* LaunchScreen.storyboard in Resources */, + 53BC63B51C0A57E4009BFDDF /* Assets.xcassets in Resources */, + 53BC63B31C0A57E4009BFDDF /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 53BC63A61C0A57E4009BFDDF /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 53BC63C01C0A591A009BFDDF /* RootViewController.swift in Sources */, + 53BC63AE1C0A57E4009BFDDF /* AppDelegate.swift in Sources */, + 53BC63C61C0A5F72009BFDDF /* DetailViewController.swift in Sources */, + 53BC63C21C0A596C009BFDDF /* SimpleViewController.swift in Sources */, + 53BC63C41C0A5F59009BFDDF /* SimpleTableViewController.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 53BC63B11C0A57E4009BFDDF /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 53BC63B21C0A57E4009BFDDF /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 53BC63B61C0A57E4009BFDDF /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 53BC63B71C0A57E4009BFDDF /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 53BC63BA1C0A57E4009BFDDF /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.1; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 53BC63BB1C0A57E4009BFDDF /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.1; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 53BC63BD1C0A57E4009BFDDF /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = AdaptivePopover/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.useyourloaf.AdaptivePopover; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 53BC63BE1C0A57E4009BFDDF /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = AdaptivePopover/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.useyourloaf.AdaptivePopover; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 53BC63A51C0A57E4009BFDDF /* Build configuration list for PBXProject "AdaptivePopover" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 53BC63BA1C0A57E4009BFDDF /* Debug */, + 53BC63BB1C0A57E4009BFDDF /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 53BC63BC1C0A57E4009BFDDF /* Build configuration list for PBXNativeTarget "AdaptivePopover" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 53BC63BD1C0A57E4009BFDDF /* Debug */, + 53BC63BE1C0A57E4009BFDDF /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 53BC63A21C0A57E4009BFDDF /* Project object */; +} diff --git a/AdaptivePopover/AdaptivePopover/AppDelegate.swift b/AdaptivePopover/AdaptivePopover/AppDelegate.swift new file mode 100644 index 0000000..9833a11 --- /dev/null +++ b/AdaptivePopover/AdaptivePopover/AppDelegate.swift @@ -0,0 +1,40 @@ +// +// AppDelegate.swift +// AdaptivePopover +// +// Created by Keith Harrison http://useyourloaf.com +// Copyright (c) 2015 Keith Harrison. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + var window: UIWindow? +} + diff --git a/AdaptivePopover/AdaptivePopover/Assets.xcassets/AppIcon.appiconset/Contents.json b/AdaptivePopover/AdaptivePopover/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..36d2c80 --- /dev/null +++ b/AdaptivePopover/AdaptivePopover/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/AdaptivePopover/AdaptivePopover/Base.lproj/LaunchScreen.storyboard b/AdaptivePopover/AdaptivePopover/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..f9b6409 --- /dev/null +++ b/AdaptivePopover/AdaptivePopover/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AdaptivePopover/AdaptivePopover/Base.lproj/Main.storyboard b/AdaptivePopover/AdaptivePopover/Base.lproj/Main.storyboard new file mode 100644 index 0000000..b02fbee --- /dev/null +++ b/AdaptivePopover/AdaptivePopover/Base.lproj/Main.storyboard @@ -0,0 +1,219 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AdaptivePopover/AdaptivePopover/DetailViewController.swift b/AdaptivePopover/AdaptivePopover/DetailViewController.swift new file mode 100644 index 0000000..dbfe95e --- /dev/null +++ b/AdaptivePopover/AdaptivePopover/DetailViewController.swift @@ -0,0 +1,47 @@ +// +// DetailViewController.swift +// AdaptivePopover +// +// Created by Keith Harrison http://useyourloaf.com +// Copyright (c) 2015 Keith Harrison. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +import UIKit + +class DetailViewController: UIViewController { + + @IBOutlet private weak var detailLabel: UILabel! + + var detailText = "No detail" + + override func viewDidLoad() { + super.viewDidLoad() + detailLabel.text = detailText + title = detailText + } +} diff --git a/AdaptivePopover/AdaptivePopover/Info.plist b/AdaptivePopover/AdaptivePopover/Info.plist new file mode 100644 index 0000000..40c6215 --- /dev/null +++ b/AdaptivePopover/AdaptivePopover/Info.plist @@ -0,0 +1,47 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/AdaptivePopover/AdaptivePopover/RootViewController.swift b/AdaptivePopover/AdaptivePopover/RootViewController.swift new file mode 100644 index 0000000..6a2386c --- /dev/null +++ b/AdaptivePopover/AdaptivePopover/RootViewController.swift @@ -0,0 +1,107 @@ +// +// RootViewController.swift +// AdaptivePopover +// +// Created by Keith Harrison http://useyourloaf.com +// Copyright (c) 2015 Keith Harrison. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +import UIKit + +class RootViewController: UIViewController { + + @IBOutlet weak var simpleButton: UIButton! + @IBOutlet weak var embeddedButton: UIButton! + + override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { + switch segue.identifier { + case "SimpleSegue"?: + let simplePPC = segue.destinationViewController.popoverPresentationController + simplePPC?.delegate = self + simplePPC?.sourceView = simpleButton + simplePPC?.sourceRect = simpleButton.bounds + case "EmbeddedSegue"?: + let embeddedPPC = segue.destinationViewController.popoverPresentationController + embeddedPPC?.delegate = self + embeddedPPC?.sourceView = embeddedButton + embeddedPPC?.sourceRect = embeddedButton.bounds + default: + fatalError("Unknown segue: \(segue.identifier)") + } + } +} + + +// MARK: UIPopoverPresentationControllerDelegate +extension RootViewController: UIPopoverPresentationControllerDelegate { + + // In modal presentation we need to add a button to our popover + // to allow it to be dismissed. Handle the situation where + // our popover may be embedded in a navigation controller + + func presentationController(controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? { + guard style != .None else { + return controller.presentedViewController + } + + if let navController = controller.presentedViewController as? UINavigationController { + addDismissButton(navController) + return navController + } else { + let navController = UINavigationController.init(rootViewController: controller.presentedViewController) + addDismissButton(navController) + return navController + } + } + + // Check for when we present in a non modal style and remove the + // the dismiss button from the navigation bar. + +// func presentationController(presentationController: UIPresentationController, willPresentWithAdaptiveStyle style: UIModalPresentationStyle, transitionCoordinator: UIViewControllerTransitionCoordinator?) { +// if style == .None { +// if let navController = presentationController.presentedViewController as? UINavigationController { +// removeDismissButton(navController) +// } +// } +// } + + func didDismissPresentedView() { + presentedViewController?.dismissViewControllerAnimated(true, completion: nil) + } + + private func addDismissButton(navigationController: UINavigationController) { + let rootViewController = navigationController.viewControllers[0] + rootViewController.navigationItem.leftBarButtonItem = UIBarButtonItem.init(barButtonSystemItem: .Done, + target: self, action: "didDismissPresentedView") + } + + private func removeDismissButton(navigationController: UINavigationController) { + let rootViewController = navigationController.viewControllers[0] + rootViewController.navigationItem.leftBarButtonItem = nil; + } +} diff --git a/AdaptivePopover/AdaptivePopover/SimpleTableViewController.swift b/AdaptivePopover/AdaptivePopover/SimpleTableViewController.swift new file mode 100644 index 0000000..68f8ac9 --- /dev/null +++ b/AdaptivePopover/AdaptivePopover/SimpleTableViewController.swift @@ -0,0 +1,65 @@ +// +// SimpleTableViewController.swift +// AdaptivePopover +// +// Created by Keith Harrison http://useyourloaf.com +// Copyright (c) 2015 Keith Harrison. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +import UIKit + +class SimpleTableViewController: UIViewController { + + @IBOutlet var tableView: UITableView! + + override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { + if segue.identifier == "ShowDetailSegue" { + if let indexPath = tableView.indexPathForCell(sender as! UITableViewCell) { + if let detailViewController = segue.destinationViewController as? DetailViewController { + detailViewController.detailText = "Item \(indexPath.row)" + } + } + } + } +} + +extension SimpleTableViewController: UITableViewDataSource { + func numberOfSectionsInTableView(tableView: UITableView) -> Int { + return 1 + } + + func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return 10 + } + + func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCellWithIdentifier("SimpleCell", forIndexPath: indexPath) + cell.textLabel?.text = "Item \(indexPath.row)" + return cell + } +} diff --git a/AdaptivePopover/AdaptivePopover/SimpleViewController.swift b/AdaptivePopover/AdaptivePopover/SimpleViewController.swift new file mode 100644 index 0000000..384d46d --- /dev/null +++ b/AdaptivePopover/AdaptivePopover/SimpleViewController.swift @@ -0,0 +1,38 @@ +// +// SimpleViewController.swift +// AdaptivePopover +// +// Created by Keith Harrison http://useyourloaf.com +// Copyright (c) 2015 Keith Harrison. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +import UIKit + +class SimpleViewController: UIViewController { + +} diff --git a/README b/README index dd45a08..d351eef 100644 --- a/README +++ b/README @@ -3,11 +3,12 @@ See http://useyourloaf.com for details - This README last updated: 22 June 2015 + This README last updated: 30 Nov 2015 ======================================================================= + INDEX + AdaptivePopover Adapting a popover presentation (iOS 9, Swift) AirPrinter - Example of how to print from iOS AlertView - Demo iOS 5 changes to UIAlertView AlertController - UIAlertController changes in iOS 8 From 751fd3b31e6124e7c860a472398a4a2750142840 Mon Sep 17 00:00:00 2001 From: Keith Harrison Date: Mon, 30 Nov 2015 20:20:53 +0000 Subject: [PATCH 002/184] Add README --- .../AdaptivePopover.xcodeproj/project.pbxproj | 2 ++ AdaptivePopover/README | 11 +++++++++++ 2 files changed, 13 insertions(+) create mode 100644 AdaptivePopover/README diff --git a/AdaptivePopover/AdaptivePopover.xcodeproj/project.pbxproj b/AdaptivePopover/AdaptivePopover.xcodeproj/project.pbxproj index 442a648..e6de0ae 100644 --- a/AdaptivePopover/AdaptivePopover.xcodeproj/project.pbxproj +++ b/AdaptivePopover/AdaptivePopover.xcodeproj/project.pbxproj @@ -18,6 +18,7 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 53A8BF831C0CE6F3003C9B46 /* README */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README; sourceTree = ""; }; 53BC63AA1C0A57E4009BFDDF /* AdaptivePopover.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AdaptivePopover.app; sourceTree = BUILT_PRODUCTS_DIR; }; 53BC63AD1C0A57E4009BFDDF /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 53BC63B21C0A57E4009BFDDF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; @@ -44,6 +45,7 @@ 53BC63A11C0A57E4009BFDDF = { isa = PBXGroup; children = ( + 53A8BF831C0CE6F3003C9B46 /* README */, 53BC63AC1C0A57E4009BFDDF /* AdaptivePopover */, 53BC63AB1C0A57E4009BFDDF /* Products */, ); diff --git a/AdaptivePopover/README b/AdaptivePopover/README new file mode 100644 index 0000000..c60fb7d --- /dev/null +++ b/AdaptivePopover/README @@ -0,0 +1,11 @@ +======================================================================= +AdaptivePopover - Adapting popovers to size classes + +Version 1.0 30 Nov 2015 Initial version. +======================================================================= + +Example of how to use the UIPopoverPresentationController delegate +protocol to adapt a popover to compact size classes. See the following +post for further details: + +http://useyourloaf.com/blog/making-popovers-adapt-to-size-classes.html From 854be9065e5aff778c97ebe95d36c15a27807cf5 Mon Sep 17 00:00:00 2001 From: Keith Harrison Date: Sun, 6 Dec 2015 11:08:23 +0000 Subject: [PATCH 003/184] Uncomment willPresentWithAdaptiveStyle --- .../AdaptivePopover/RootViewController.swift | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/AdaptivePopover/AdaptivePopover/RootViewController.swift b/AdaptivePopover/AdaptivePopover/RootViewController.swift index 6a2386c..e73020f 100644 --- a/AdaptivePopover/AdaptivePopover/RootViewController.swift +++ b/AdaptivePopover/AdaptivePopover/RootViewController.swift @@ -82,13 +82,13 @@ extension RootViewController: UIPopoverPresentationControllerDelegate { // Check for when we present in a non modal style and remove the // the dismiss button from the navigation bar. -// func presentationController(presentationController: UIPresentationController, willPresentWithAdaptiveStyle style: UIModalPresentationStyle, transitionCoordinator: UIViewControllerTransitionCoordinator?) { -// if style == .None { -// if let navController = presentationController.presentedViewController as? UINavigationController { -// removeDismissButton(navController) -// } -// } -// } + func presentationController(presentationController: UIPresentationController, willPresentWithAdaptiveStyle style: UIModalPresentationStyle, transitionCoordinator: UIViewControllerTransitionCoordinator?) { + if style == .None { + if let navController = presentationController.presentedViewController as? UINavigationController { + removeDismissButton(navController) + } + } + } func didDismissPresentedView() { presentedViewController?.dismissViewControllerAnimated(true, completion: nil) From 2b96bae4c5802766da684acfabbd2a77c3e034f8 Mon Sep 17 00:00:00 2001 From: Keith Harrison Date: Tue, 15 Dec 2015 19:14:21 +0000 Subject: [PATCH 004/184] Added String playground --- Playgrounds/String.playground/Contents.swift | 296 ++++++++++++++++++ .../String.playground/contents.xcplayground | 4 + .../contents.xcworkspacedata | 7 + .../String.playground/timeline.xctimeline | 11 + 4 files changed, 318 insertions(+) create mode 100644 Playgrounds/String.playground/Contents.swift create mode 100644 Playgrounds/String.playground/contents.xcplayground create mode 100644 Playgrounds/String.playground/playground.xcworkspace/contents.xcworkspacedata create mode 100644 Playgrounds/String.playground/timeline.xctimeline diff --git a/Playgrounds/String.playground/Contents.swift b/Playgrounds/String.playground/Contents.swift new file mode 100644 index 0000000..1ee46d4 --- /dev/null +++ b/Playgrounds/String.playground/Contents.swift @@ -0,0 +1,296 @@ +// Swift Standard Librray - String +// Keith Harrison http://useyourloaf.com + +// Import Foundation if you want to bridge to NSString +import Foundation + +// ==== +// Initializing a String +// ==== + +var emptyString = "" // Empty String +var stillEmpty = String() // Another empty String +let helloWorld = "Hello World!" // String inferred + +let a = String(true) // from boolean: "true" +let b: Character = "A" // Explicit type required to create a Character +let c = String(b) // from character "A" +let d = String(3.14) // from Double "3.14" +let e = String(1000) // from Int "1000" +let f = "Result = \(d)" // Interpolation "Result = 3.14" +let g = "\u{2126}" // Unicode Ohm sign Ω +let h = String(count:3, repeatedValue:b) // Repeated character "AAA" + +// === +// Strings are Value Types +// === + +// Strings are value types that are copied when assigned +// or passed to a function. The copy is performed lazily +// on mutation. + +var aString = "Hello" +var bString = aString +bString += " World!" +print("\(aString)") // "Hello\n" + +// === +// Testing for empty String +// === + +emptyString.isEmpty // true + +// === +// Testing for equality +// === + +// Swift is Unicode correct so the equality operator +// (“==”) checks for Unicode canonical equivalence. +// This means that two Strings that are composed from +// different Unicode scalars will be considered equal +// if they have the same linguistic meaning and appearance: + +let spain = "España" +let tilde = "\u{303}" +let country = "Espan" + "\(tilde)" + "a" +if country == spain { + print("Matched!") // "Matched!\n" +} + +// Order +if "aaa" < "bbb" { + print("aaa is less than bbb") +} + +// === +// Testing for suffix/prefix +// === + +let line = "0001 Some test data here %%%%" +line.hasPrefix("0001") // true +line.hasSuffix("%%%%") // true + +// === +// Converting to upper/lower case +// === + +let mixedCase = "AbcDef" +let upper = mixedCase.uppercaseString // "ABCDEF" +let lower = mixedCase.lowercaseString // "abcdef" + +// === +// Views +// === + +// Strings are not collections of anything but do provide +// collection views for different representations accessed +// through the appropriate property: + +country.characters // characters +country.unicodeScalars // Unicode scalar 21-bit codes +country.utf16 // UTF-16 encoding +country.utf8 // UTF-8 encoding + +// === +// Counting +// == + +// String does not directly have a property to return a count +// as it only has meaning for a particular representation. +// So count is implemented on each of the collection views: + +// spain = "España" +print("\(spain.characters.count)") // 6 +print("\(spain.unicodeScalars.count)") // 6 +print("\(spain.utf16.count)") // 6 +print("\(spain.utf8.count)") // 7 + +// === +// Using Index to traverse a collection +// === + +// Each of the collection views has an Index +// that you use to traverse the collection. +// This is maybe one of the big causes of pain +// when getting to grips with String. You cannot +// randomly access an element in a string using +// a subscript (e.g. string[5]). + +// To iterate over all items in a collection +// with a for..in loop: + +var sentence = "Never odd or even" +for character in sentence.characters { + print(character) +} + +// Each collection has two instance properties you +// can use as subscripts to index into the collection: +// startIndex: the position of the first element if +// non-empty, else identical to endIndex. +// endIndex: the position just “past the end” of the string. +// +// Note the choice for endIndex means you cannot use it +// directly as a subscript as it is out of range. + +let cafe = "café" +cafe.startIndex // 0 +cafe.endIndex // 4 - after last char + +// The startIndex and endIndex properties become more +// useful when modified with the following methods: +// successor() to get the next element +// predecessor() to get the previous element +// advancedBy(n) to jump forward/backward by n elements +// +// Some examples, note that you can chain operations if required: + +cafe[cafe.startIndex] // "c" +cafe[cafe.startIndex.successor()] // "a" +cafe[cafe.startIndex.successor().successor()] // "f" +// Note that cafe[endIndex] is a run time error +cafe[cafe.endIndex.predecessor()] // "é" +cafe[cafe.startIndex.advancedBy(2)] // "f" + +// The indices property returns a range for all elements +// in a String that can be useful for iterating through +// the collection: + +for index in cafe.characters.indices { + print(cafe[index]) +} + +// You cannot use an index from one String to access a +// different string. You can convert an index to an integer +// value with the distanceTo method: + +let word1 = "ABCDEF" +let word2 = "012345" +let indexC = word1.startIndex.advancedBy(2) +let distance = word1.startIndex.distanceTo(indexC) // 2 +let digit = word2[word2.startIndex.advancedBy(distance)] // "2" + +// === +// Using a range +// === + +// To identify a range of elements in a String collection +// use a range. A range is just a start and end index: + +let fqdn = "useyourloaf.com" +let rangeOfTLD = Range(start: fqdn.endIndex.advancedBy(-3), end: fqdn.endIndex) +let tld = fqdn[rangeOfTLD] // "com" + +// Alternate creation of range (... or ..< syntax) +let rangeOfDomain = fqdn.startIndex.. + + + \ No newline at end of file diff --git a/Playgrounds/String.playground/playground.xcworkspace/contents.xcworkspacedata b/Playgrounds/String.playground/playground.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/Playgrounds/String.playground/playground.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Playgrounds/String.playground/timeline.xctimeline b/Playgrounds/String.playground/timeline.xctimeline new file mode 100644 index 0000000..656ee49 --- /dev/null +++ b/Playgrounds/String.playground/timeline.xctimeline @@ -0,0 +1,11 @@ + + + + + + + From b32c0a4c570579dfd71fdb72032d013d401eaed0 Mon Sep 17 00:00:00 2001 From: Keith Harrison Date: Tue, 15 Dec 2015 19:25:32 +0000 Subject: [PATCH 005/184] Cleanup format --- Playgrounds/String.playground/Contents.swift | 4 ++-- Playgrounds/String.playground/timeline.xctimeline | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Playgrounds/String.playground/Contents.swift b/Playgrounds/String.playground/Contents.swift index 1ee46d4..5ba129c 100644 --- a/Playgrounds/String.playground/Contents.swift +++ b/Playgrounds/String.playground/Contents.swift @@ -282,9 +282,9 @@ sequences.removeRange(midRange) // "ABA ABC" let welcome = "hello world!" welcome.capitalizedString // "Hello World!" -// == +// === // Searching for a substring -// +// === // An example of using NSString methods to perform a // search for a substring: diff --git a/Playgrounds/String.playground/timeline.xctimeline b/Playgrounds/String.playground/timeline.xctimeline index 656ee49..d0bc7bf 100644 --- a/Playgrounds/String.playground/timeline.xctimeline +++ b/Playgrounds/String.playground/timeline.xctimeline @@ -3,7 +3,7 @@ version = "3.0"> From 1bf94f06e421d6829c63e323850a4240b7f6f7b2 Mon Sep 17 00:00:00 2001 From: Keith Harrison Date: Thu, 17 Dec 2015 09:22:23 +0000 Subject: [PATCH 006/184] Correct output of dropFirst(3) --- Playgrounds/String.playground/Contents.swift | 2 +- Playgrounds/String.playground/timeline.xctimeline | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Playgrounds/String.playground/Contents.swift b/Playgrounds/String.playground/Contents.swift index 5ba129c..5293ca6 100644 --- a/Playgrounds/String.playground/Contents.swift +++ b/Playgrounds/String.playground/Contents.swift @@ -211,7 +211,7 @@ let subString4 = template.substringWithRange(indexStartOfText.. From c1d95ba4a9cf26c2f90b932c18d5515accd9b6e8 Mon Sep 17 00:00:00 2001 From: Keith Harrison Date: Tue, 29 Dec 2015 19:54:28 +0000 Subject: [PATCH 007/184] Properly URL encode query --- .../TwitterSearch.xcodeproj/project.pbxproj | 181 +++++++++++++++++- .../TwitterSearch/NSString+URLEncoding.h | 72 +++++++ .../TwitterSearch/NSString+URLEncoding.m | 64 +++++++ .../TwitterSearch/SearchViewController.m | 4 +- .../TwitterSearchUnitTests/Info.plist | 24 +++ .../NSStringURLEncodingUnitTests.m | 154 +++++++++++++++ 6 files changed, 494 insertions(+), 5 deletions(-) create mode 100644 TwitterSearch/TwitterSearch/NSString+URLEncoding.h create mode 100644 TwitterSearch/TwitterSearch/NSString+URLEncoding.m create mode 100644 TwitterSearch/TwitterSearchUnitTests/Info.plist create mode 100644 TwitterSearch/TwitterSearchUnitTests/NSStringURLEncodingUnitTests.m diff --git a/TwitterSearch/TwitterSearch.xcodeproj/project.pbxproj b/TwitterSearch/TwitterSearch.xcodeproj/project.pbxproj index 50360bb..4b86589 100644 --- a/TwitterSearch/TwitterSearch.xcodeproj/project.pbxproj +++ b/TwitterSearch/TwitterSearch.xcodeproj/project.pbxproj @@ -8,9 +8,11 @@ /* Begin PBXBuildFile section */ 53432FE8176E71DF0015F986 /* Storyboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 53432FE7176E71DF0015F986 /* Storyboard.storyboard */; }; - 536348091BA4AD6A005B59F3 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 536348081BA4AD6A005B59F3 /* Images.xcassets */; settings = {ASSET_TAGS = (); }; }; - 5363480B1BA4B3C6005B59F3 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5363480A1BA4B3C6005B59F3 /* LaunchScreen.storyboard */; settings = {ASSET_TAGS = (); }; }; - 5363480E1BA4BB37005B59F3 /* TweetCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 5363480D1BA4BB37005B59F3 /* TweetCell.m */; settings = {ASSET_TAGS = (); }; }; + 5345C8501C32C16C00B88AB7 /* NSString+URLEncoding.m in Sources */ = {isa = PBXBuildFile; fileRef = 5345C84F1C32C16C00B88AB7 /* NSString+URLEncoding.m */; }; + 5345C8581C32CC3C00B88AB7 /* NSStringURLEncodingUnitTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5345C8571C32CC3C00B88AB7 /* NSStringURLEncodingUnitTests.m */; }; + 536348091BA4AD6A005B59F3 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 536348081BA4AD6A005B59F3 /* Images.xcassets */; }; + 5363480B1BA4B3C6005B59F3 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5363480A1BA4B3C6005B59F3 /* LaunchScreen.storyboard */; }; + 5363480E1BA4BB37005B59F3 /* TweetCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 5363480D1BA4BB37005B59F3 /* TweetCell.m */; }; 53852EDA176B9A6900EC1107 /* Social.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 53852ED9176B9A6900EC1107 /* Social.framework */; }; 53852EDC176BA6DA00EC1107 /* Accounts.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 53852EDB176BA6DA00EC1107 /* Accounts.framework */; }; 53CB80FE139D70FF0030ADD7 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 53CB80FD139D70FF0030ADD7 /* UIKit.framework */; }; @@ -23,8 +25,23 @@ 53CB812B139D73810030ADD7 /* SearchViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 53CB812A139D73810030ADD7 /* SearchViewController.m */; }; /* End PBXBuildFile section */ +/* Begin PBXContainerItemProxy section */ + 5345C85A1C32CC3C00B88AB7 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 53CB80F0139D70FF0030ADD7 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 53CB80F8139D70FF0030ADD7; + remoteInfo = TwitterSearch; + }; +/* End PBXContainerItemProxy section */ + /* Begin PBXFileReference section */ 53432FE7176E71DF0015F986 /* Storyboard.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Storyboard.storyboard; sourceTree = ""; }; + 5345C84E1C32C16C00B88AB7 /* NSString+URLEncoding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+URLEncoding.h"; sourceTree = ""; }; + 5345C84F1C32C16C00B88AB7 /* NSString+URLEncoding.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+URLEncoding.m"; sourceTree = ""; }; + 5345C8551C32CC3C00B88AB7 /* TwitterSearchUnitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TwitterSearchUnitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 5345C8571C32CC3C00B88AB7 /* NSStringURLEncodingUnitTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NSStringURLEncodingUnitTests.m; sourceTree = ""; }; + 5345C8591C32CC3C00B88AB7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 536348081BA4AD6A005B59F3 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 5363480A1BA4B3C6005B59F3 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; }; 5363480C1BA4BB37005B59F3 /* TweetCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TweetCell.h; sourceTree = ""; }; @@ -47,6 +64,13 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 5345C8521C32CC3C00B88AB7 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 53CB80F6139D70FF0030ADD7 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -62,10 +86,20 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 5345C8561C32CC3C00B88AB7 /* TwitterSearchUnitTests */ = { + isa = PBXGroup; + children = ( + 5345C8571C32CC3C00B88AB7 /* NSStringURLEncodingUnitTests.m */, + 5345C8591C32CC3C00B88AB7 /* Info.plist */, + ); + path = TwitterSearchUnitTests; + sourceTree = ""; + }; 53CB80EE139D70FF0030ADD7 = { isa = PBXGroup; children = ( 53CB8103139D70FF0030ADD7 /* TwitterSearch */, + 5345C8561C32CC3C00B88AB7 /* TwitterSearchUnitTests */, 53CB80FC139D70FF0030ADD7 /* Frameworks */, 53CB80FA139D70FF0030ADD7 /* Products */, ); @@ -75,6 +109,7 @@ isa = PBXGroup; children = ( 53CB80F9139D70FF0030ADD7 /* TwitterSearch.app */, + 5345C8551C32CC3C00B88AB7 /* TwitterSearchUnitTests.xctest */, ); name = Products; sourceTree = ""; @@ -105,6 +140,8 @@ 53CB815C139D75DB0030ADD7 /* Resources */, 536348081BA4AD6A005B59F3 /* Images.xcassets */, 53CB8104139D70FF0030ADD7 /* Supporting Files */, + 5345C84E1C32C16C00B88AB7 /* NSString+URLEncoding.h */, + 5345C84F1C32C16C00B88AB7 /* NSString+URLEncoding.m */, ); path = TwitterSearch; sourceTree = ""; @@ -131,6 +168,24 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ + 5345C8541C32CC3C00B88AB7 /* TwitterSearchUnitTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 5345C85C1C32CC3C00B88AB7 /* Build configuration list for PBXNativeTarget "TwitterSearchUnitTests" */; + buildPhases = ( + 5345C8511C32CC3C00B88AB7 /* Sources */, + 5345C8521C32CC3C00B88AB7 /* Frameworks */, + 5345C8531C32CC3C00B88AB7 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 5345C85B1C32CC3C00B88AB7 /* PBXTargetDependency */, + ); + name = TwitterSearchUnitTests; + productName = TwitterSearchUnitTests; + productReference = 5345C8551C32CC3C00B88AB7 /* TwitterSearchUnitTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; 53CB80F8139D70FF0030ADD7 /* TwitterSearch */ = { isa = PBXNativeTarget; buildConfigurationList = 53CB811A139D70FF0030ADD7 /* Build configuration list for PBXNativeTarget "TwitterSearch" */; @@ -156,6 +211,10 @@ attributes = { LastUpgradeCheck = 0700; TargetAttributes = { + 5345C8541C32CC3C00B88AB7 = { + CreatedOnToolsVersion = 7.2; + TestTargetID = 53CB80F8139D70FF0030ADD7; + }; 53CB80F8139D70FF0030ADD7 = { DevelopmentTeam = LCC2J94N44; }; @@ -174,11 +233,19 @@ projectRoot = ""; targets = ( 53CB80F8139D70FF0030ADD7 /* TwitterSearch */, + 5345C8541C32CC3C00B88AB7 /* TwitterSearchUnitTests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 5345C8531C32CC3C00B88AB7 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 53CB80F7139D70FF0030ADD7 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -193,10 +260,19 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 5345C8511C32CC3C00B88AB7 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 5345C8581C32CC3C00B88AB7 /* NSStringURLEncodingUnitTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 53CB80F5139D70FF0030ADD7 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 5345C8501C32C16C00B88AB7 /* NSString+URLEncoding.m in Sources */, 53CB810B139D70FF0030ADD7 /* main.m in Sources */, 53CB810E139D70FF0030ADD7 /* TwitterSearchAppDelegate.m in Sources */, 53CB8114139D70FF0030ADD7 /* RootViewController.m in Sources */, @@ -207,6 +283,14 @@ }; /* End PBXSourcesBuildPhase section */ +/* Begin PBXTargetDependency section */ + 5345C85B1C32CC3C00B88AB7 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 53CB80F8139D70FF0030ADD7 /* TwitterSearch */; + targetProxy = 5345C85A1C32CC3C00B88AB7 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + /* Begin PBXVariantGroup section */ 53CB8106139D70FF0030ADD7 /* InfoPlist.strings */ = { isa = PBXVariantGroup; @@ -219,6 +303,89 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ + 5345C85D1C32CC3C00B88AB7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + INFOPLIST_FILE = TwitterSearchUnitTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.2; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_ENABLE_DEBUG_INFO = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.useyourloaf.TwitterSearchUnitTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TwitterSearch.app/TwitterSearch"; + }; + name = Debug; + }; + 5345C85E1C32CC3C00B88AB7 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + INFOPLIST_FILE = TwitterSearchUnitTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.2; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_BUNDLE_IDENTIFIER = com.useyourloaf.TwitterSearchUnitTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TwitterSearch.app/TwitterSearch"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; 53CB8118139D70FF0030ADD7 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -298,6 +465,14 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 5345C85C1C32CC3C00B88AB7 /* Build configuration list for PBXNativeTarget "TwitterSearchUnitTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 5345C85D1C32CC3C00B88AB7 /* Debug */, + 5345C85E1C32CC3C00B88AB7 /* Release */, + ); + defaultConfigurationIsVisible = 0; + }; 53CB80F3139D70FF0030ADD7 /* Build configuration list for PBXProject "TwitterSearch" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/TwitterSearch/TwitterSearch/NSString+URLEncoding.h b/TwitterSearch/TwitterSearch/NSString+URLEncoding.h new file mode 100644 index 0000000..c419d84 --- /dev/null +++ b/TwitterSearch/TwitterSearch/NSString+URLEncoding.h @@ -0,0 +1,72 @@ +// +// NSString+URLEncoding.h +// +// Created by Keith Harrison http://useyourloaf.com +// Copyright (c) 2015 Keith Harrison. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + + +#import + +/** + An NSString category that provides percent encoding of URL + strings. + */ + +@interface NSString (URLEncoding) + +/**-------------------------------------- + * @name Instance Methods + * -------------------------------------- + */ + +/** + Returns a new string made from the receiver by replacing characters which are + reserved in a URL query with percent encoded characters (see RFC 3986). + + @param plusForSpace A boolean flag which when set replaces spaces by a '+' + otherwise they are percent escaped as %20. + + @return Returns the encoded string, or nil if the transformation is not possible. + */ + +- (nullable NSString *)stringByAddingPercentEncodingForURLQuery:(BOOL)plusForSpace; + +/** + Returns a new string made from the receiver by replacing characters which are + reserved in a URL encoded form with percent encoded characters. + + @discussion Unlike -stringByAddingPercentEncodingForURLQuery: this method + does not escape '*', spaces are always replaced by '+' and the '~' is not + an allowed character so it is percent escaped. + + @return Returns the encoded string, or nil if the transformation is not possible. + */ + +- (nullable NSString *)stringByAddingPercentEncodingForURLFormData; +@end diff --git a/TwitterSearch/TwitterSearch/NSString+URLEncoding.m b/TwitterSearch/TwitterSearch/NSString+URLEncoding.m new file mode 100644 index 0000000..42d5cf7 --- /dev/null +++ b/TwitterSearch/TwitterSearch/NSString+URLEncoding.m @@ -0,0 +1,64 @@ +// +// NSString+URLEncoding.m +// +// Created by Keith Harrison http://useyourloaf.com +// Copyright (c) 2015 Keith Harrison. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + + +#import "NSString+URLEncoding.h" + +@implementation NSString (URLEncoding) + +- (nullable NSString *)stringByAddingPercentEncodingForURLQuery:(BOOL)plusForSpace { + NSString *unreserved = @"-._~"; + NSMutableCharacterSet *urlQueryAllowedCharacterSet = [NSMutableCharacterSet alphanumericCharacterSet]; + [urlQueryAllowedCharacterSet addCharactersInString:unreserved]; + + if (plusForSpace) { + [urlQueryAllowedCharacterSet addCharactersInString:@" "]; + } + + NSString *encodedString = [self stringByAddingPercentEncodingWithAllowedCharacters:urlQueryAllowedCharacterSet]; + if (plusForSpace) { + encodedString = [encodedString stringByReplacingOccurrencesOfString:@" " withString:@"+"]; + } + return encodedString; +} + +- (nullable NSString *)stringByAddingPercentEncodingForURLFormData { + NSString *unreserved = @"-._* "; + NSMutableCharacterSet *urlQueryAllowedCharacterSet = [NSMutableCharacterSet alphanumericCharacterSet]; + [urlQueryAllowedCharacterSet addCharactersInString:unreserved]; + + NSString *encodedString = [self stringByAddingPercentEncodingWithAllowedCharacters:urlQueryAllowedCharacterSet]; + encodedString = [encodedString stringByReplacingOccurrencesOfString:@" " withString:@"+"]; + return encodedString; +} + +@end diff --git a/TwitterSearch/TwitterSearch/SearchViewController.m b/TwitterSearch/TwitterSearch/SearchViewController.m index d3f5f61..3dc77bd 100644 --- a/TwitterSearch/TwitterSearch/SearchViewController.m +++ b/TwitterSearch/TwitterSearch/SearchViewController.m @@ -30,6 +30,7 @@ #import "SearchViewController.h" #import "TweetCell.h" +#import "NSString+URLEncoding.h" #import #import @@ -169,8 +170,7 @@ - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)ce - (void)loadQuery { self.searchState = UYLTwitterSearchStateLoading; - NSString *encodedQuery = [self.query stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; - + NSString *encodedQuery = [self.query stringByAddingPercentEncodingForURLFormData]; ACAccountType *accountType = [self.accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter]; [self.accountStore requestAccessToAccountsWithType:accountType options:NULL diff --git a/TwitterSearch/TwitterSearchUnitTests/Info.plist b/TwitterSearch/TwitterSearchUnitTests/Info.plist new file mode 100644 index 0000000..ba72822 --- /dev/null +++ b/TwitterSearch/TwitterSearchUnitTests/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/TwitterSearch/TwitterSearchUnitTests/NSStringURLEncodingUnitTests.m b/TwitterSearch/TwitterSearchUnitTests/NSStringURLEncodingUnitTests.m new file mode 100644 index 0000000..c7b0b0c --- /dev/null +++ b/TwitterSearch/TwitterSearchUnitTests/NSStringURLEncodingUnitTests.m @@ -0,0 +1,154 @@ +// +// NSStringURLEncodingUnitTests.m +// +// Created by Keith Harrison http://useyourloaf.com +// Copyright (c) 2015 Keith Harrison. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#import "NSString+URLEncoding.h" +#import + +@interface NSStringURLEncodingUnitTests : XCTestCase + +@end + +@implementation NSStringURLEncodingUnitTests + +- (void)setUp { + [super setUp]; +} + +- (void)tearDown { + [super tearDown]; +} + +- (void)testQueryAllowed { + NSString *input = @"ABC123abc"; + NSString *output = [input stringByAddingPercentEncodingForURLQuery:NO]; + NSString *expected = @"ABC123abc"; + BOOL result = [expected isEqualToString:output]; + XCTAssertTrue(result, "Expected: %@ got %@",expected,output); +} + +- (void)testFormAllowed { + NSString *input = @"ABC123abc"; + NSString *output = [input stringByAddingPercentEncodingForURLFormData]; + NSString *expected = @"ABC123abc"; + BOOL result = [expected isEqualToString:output]; + XCTAssertTrue(result, "Expected: %@ got %@",expected,output); +} + +- (void)testQuerySpaceIsPercentEncoded { + NSString *input = @"one two"; + NSString *output = [input stringByAddingPercentEncodingForURLQuery:NO]; + NSString *expected = @"one%20two"; + BOOL result = [expected isEqualToString:output]; + XCTAssertTrue(result, "Expected: %@ got %@",expected,output); +} + +- (void)testQuerySpaceIsPlusEncoded { + NSString *input = @"one two"; + NSString *output = [input stringByAddingPercentEncodingForURLQuery:YES]; + NSString *expected = @"one+two"; + BOOL result = [expected isEqualToString:output]; + XCTAssertTrue(result, "Expected: %@ got %@",expected,output); +} + +- (void)testFormSpaceIsPlusEncoded { + NSString *input = @"one two"; + NSString *output = [input stringByAddingPercentEncodingForURLFormData]; + NSString *expected = @"one+two"; + BOOL result = [expected isEqualToString:output]; + XCTAssertTrue(result, "Expected: %@ got %@",expected,output); +} + +- (void)testQueryPlusIsEncodedWhenUsingPlusForSpaces { + NSString *input = @"one+two"; + NSString *output = [input stringByAddingPercentEncodingForURLQuery:YES]; + NSString *expected = @"one%2Btwo"; + BOOL result = [expected isEqualToString:output]; + XCTAssertTrue(result, "Expected: %@ got %@",expected,output); +} + +- (void)testQueryPercentIsEncoded { + NSString *input = @"%"; + NSString *output = [input stringByAddingPercentEncodingForURLQuery:NO]; + NSString *expected = @"%25"; + BOOL result = [expected isEqualToString:output]; + XCTAssertTrue(result, "Expected: %@ got %@",expected,output); +} + +- (void)testFormPercentIsEncoded { + NSString *input = @"%"; + NSString *output = [input stringByAddingPercentEncodingForURLFormData]; + NSString *expected = @"%25"; + BOOL result = [expected isEqualToString:output]; + XCTAssertTrue(result, "Expected: %@ got %@",expected,output); +} + +- (void)testQueryReservedEncoded { + NSString *input = @"!#$&'()*+,/:;=?@[]"; + NSString *output = [input stringByAddingPercentEncodingForURLQuery:NO]; + NSString *expected = @"%21%23%24%26%27%28%29%2A%2B%2C%2F%3A%3B%3D%3F%40%5B%5D"; + BOOL result = [expected isEqualToString:output]; + XCTAssertTrue(result, "Expected: %@ got %@",expected,output); +} + +- (void)testFormReservedEncoded { + NSString *input = @"!#$&'()+,/:;=?@[]"; + NSString *output = [input stringByAddingPercentEncodingForURLFormData]; + NSString *expected = @"%21%23%24%26%27%28%29%2B%2C%2F%3A%3B%3D%3F%40%5B%5D"; + BOOL result = [expected isEqualToString:output]; + XCTAssertTrue(result, "Expected: %@ got %@",expected,output); +} + +- (void)testQueryUnreservedNotEncoded { + NSString *input = @"-._~"; + NSString *output = [input stringByAddingPercentEncodingForURLQuery:NO]; + NSString *expected = @"-._~"; + BOOL result = [expected isEqualToString:output]; + XCTAssertTrue(result, "Expected: %@ got %@",expected,output); +} + +- (void)testFormUnreservedNotEncoded { + NSString *input = @"-._*"; + NSString *output = [input stringByAddingPercentEncodingForURLFormData]; + NSString *expected = @"-._*"; + BOOL result = [expected isEqualToString:output]; + XCTAssertTrue(result, "Expected: %@ got %@",expected,output); +} + +- (void)testFormTidleEncoded { + NSString *input = @"~"; + NSString *output = [input stringByAddingPercentEncodingForURLFormData]; + NSString *expected = @"%7E"; + BOOL result = [expected isEqualToString:output]; + XCTAssertTrue(result, "Expected: %@ got %@",expected,output); +} + +@end From c15bc8b405b59728547c5a8cf96121830ee18b71 Mon Sep 17 00:00:00 2001 From: Keith Harrison Date: Tue, 29 Dec 2015 19:58:51 +0000 Subject: [PATCH 008/184] Add iPad Prop Icon --- .../AppIcon.appiconset/Contents.json | 6 ++++++ .../AppIcon.appiconset/Icon2-835@2x.png | Bin 0 -> 4089 bytes 2 files changed, 6 insertions(+) create mode 100644 TwitterSearch/TwitterSearch/Images.xcassets/AppIcon.appiconset/Icon2-835@2x.png diff --git a/TwitterSearch/TwitterSearch/Images.xcassets/AppIcon.appiconset/Contents.json b/TwitterSearch/TwitterSearch/Images.xcassets/AppIcon.appiconset/Contents.json index 8595f0a..49901b5 100644 --- a/TwitterSearch/TwitterSearch/Images.xcassets/AppIcon.appiconset/Contents.json +++ b/TwitterSearch/TwitterSearch/Images.xcassets/AppIcon.appiconset/Contents.json @@ -71,6 +71,12 @@ "idiom" : "ipad", "filename" : "Icon2-76@2x.png", "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon2-835@2x.png", + "scale" : "2x" } ], "info" : { diff --git a/TwitterSearch/TwitterSearch/Images.xcassets/AppIcon.appiconset/Icon2-835@2x.png b/TwitterSearch/TwitterSearch/Images.xcassets/AppIcon.appiconset/Icon2-835@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..70e3ba0d58901bcc48f0d7a03818671b3984b21c GIT binary patch literal 4089 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 Date: Thu, 31 Dec 2015 22:30:16 +0000 Subject: [PATCH 009/184] Slight refactoring to match Swift extension --- .../TwitterSearch/NSString+URLEncoding.h | 43 +++--- .../TwitterSearch/NSString+URLEncoding.m | 34 ++--- .../TwitterSearch/SearchViewController.m | 2 +- .../NSStringURLEncodingUnitTests.m | 128 ++++++++---------- 4 files changed, 99 insertions(+), 108 deletions(-) diff --git a/TwitterSearch/TwitterSearch/NSString+URLEncoding.h b/TwitterSearch/TwitterSearch/NSString+URLEncoding.h index c419d84..b72204d 100644 --- a/TwitterSearch/TwitterSearch/NSString+URLEncoding.h +++ b/TwitterSearch/TwitterSearch/NSString+URLEncoding.h @@ -35,38 +35,47 @@ /** An NSString category that provides percent encoding of URL - strings. + strings following RFC 3986 or the W3C HTML specification. */ @interface NSString (URLEncoding) -/**-------------------------------------- - * @name Instance Methods - * -------------------------------------- - */ - /** Returns a new string made from the receiver by replacing characters which are - reserved in a URL query with percent encoded characters (see RFC 3986). - - @param plusForSpace A boolean flag which when set replaces spaces by a '+' - otherwise they are percent escaped as %20. - + reserved in a URI query with percent encoded characters. + + @discussion The following characters are not considered reserved in a URI query + by RFC 3986: + + - Alpha "a...z" "A...Z" + - Numberic "0...9" + - Unreserved "-._~" + + In addition the reserved characters "/" and "?" have no reserved purpose in the + query component of a URI so do not need to be percent escaped. + @return Returns the encoded string, or nil if the transformation is not possible. */ -- (nullable NSString *)stringByAddingPercentEncodingForURLQuery:(BOOL)plusForSpace; +- (nullable NSString *)stringByAddingPercentEncodingForRFC3986; /** Returns a new string made from the receiver by replacing characters which are - reserved in a URL encoded form with percent encoded characters. + reserved in HTML forms (media type application/x-www-form-urlencoded) with + percent encoded characters. + + @discussion The W3C HTML5 specification, section 4.10.22.5 URL-encoded form + data percent encodes all characters except the following: + + - Space (0x20) is replaced by a "+" (0x2B) + - Bytes in the range 0x2A, 0x2D, 0x2E, 0x30-0x39, 0x41-0x5A, 0x5F, 0x61-0x7A + (alphanumeric + "*-._") - @discussion Unlike -stringByAddingPercentEncodingForURLQuery: this method - does not escape '*', spaces are always replaced by '+' and the '~' is not - an allowed character so it is percent escaped. + @param plusForSpace Boolean, when true replaces space with a '+' + otherwise uses percent encoding (%20). Default is false. @return Returns the encoded string, or nil if the transformation is not possible. */ -- (nullable NSString *)stringByAddingPercentEncodingForURLFormData; +- (nullable NSString *)stringByAddingPercentEncodingForFormData:(BOOL)plusForSpace; @end diff --git a/TwitterSearch/TwitterSearch/NSString+URLEncoding.m b/TwitterSearch/TwitterSearch/NSString+URLEncoding.m index 42d5cf7..a8568e3 100644 --- a/TwitterSearch/TwitterSearch/NSString+URLEncoding.m +++ b/TwitterSearch/TwitterSearch/NSString+URLEncoding.m @@ -35,30 +35,26 @@ @implementation NSString (URLEncoding) -- (nullable NSString *)stringByAddingPercentEncodingForURLQuery:(BOOL)plusForSpace { - NSString *unreserved = @"-._~"; - NSMutableCharacterSet *urlQueryAllowedCharacterSet = [NSMutableCharacterSet alphanumericCharacterSet]; - [urlQueryAllowedCharacterSet addCharactersInString:unreserved]; - +- (nullable NSString *)stringByAddingPercentEncodingForRFC3986 { + NSString *unreserved = @"-._~/?"; + NSMutableCharacterSet *allowedCharacterSet = [NSMutableCharacterSet alphanumericCharacterSet]; + [allowedCharacterSet addCharactersInString:unreserved]; + return [self stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet]; +} + +- (nullable NSString *)stringByAddingPercentEncodingForFormData:(BOOL)plusForSpace { + NSString *unreserved = @"*-._"; + NSMutableCharacterSet *allowedCharacterSet = [NSMutableCharacterSet alphanumericCharacterSet]; + [allowedCharacterSet addCharactersInString:unreserved]; if (plusForSpace) { - [urlQueryAllowedCharacterSet addCharactersInString:@" "]; + [allowedCharacterSet addCharactersInString:@" "]; } - NSString *encodedString = [self stringByAddingPercentEncodingWithAllowedCharacters:urlQueryAllowedCharacterSet]; + NSString *encoded = [self stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet]; if (plusForSpace) { - encodedString = [encodedString stringByReplacingOccurrencesOfString:@" " withString:@"+"]; + encoded = [encoded stringByReplacingOccurrencesOfString:@" " withString:@"+"]; } - return encodedString; -} - -- (nullable NSString *)stringByAddingPercentEncodingForURLFormData { - NSString *unreserved = @"-._* "; - NSMutableCharacterSet *urlQueryAllowedCharacterSet = [NSMutableCharacterSet alphanumericCharacterSet]; - [urlQueryAllowedCharacterSet addCharactersInString:unreserved]; - - NSString *encodedString = [self stringByAddingPercentEncodingWithAllowedCharacters:urlQueryAllowedCharacterSet]; - encodedString = [encodedString stringByReplacingOccurrencesOfString:@" " withString:@"+"]; - return encodedString; + return encoded; } @end diff --git a/TwitterSearch/TwitterSearch/SearchViewController.m b/TwitterSearch/TwitterSearch/SearchViewController.m index 3dc77bd..40cfbfb 100644 --- a/TwitterSearch/TwitterSearch/SearchViewController.m +++ b/TwitterSearch/TwitterSearch/SearchViewController.m @@ -170,7 +170,7 @@ - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)ce - (void)loadQuery { self.searchState = UYLTwitterSearchStateLoading; - NSString *encodedQuery = [self.query stringByAddingPercentEncodingForURLFormData]; + NSString *encodedQuery = [self.query stringByAddingPercentEncodingForFormData:NO]; ACAccountType *accountType = [self.accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter]; [self.accountStore requestAccessToAccountsWithType:accountType options:NULL diff --git a/TwitterSearch/TwitterSearchUnitTests/NSStringURLEncodingUnitTests.m b/TwitterSearch/TwitterSearchUnitTests/NSStringURLEncodingUnitTests.m index c7b0b0c..c791a5e 100644 --- a/TwitterSearch/TwitterSearchUnitTests/NSStringURLEncodingUnitTests.m +++ b/TwitterSearch/TwitterSearchUnitTests/NSStringURLEncodingUnitTests.m @@ -47,108 +47,94 @@ - (void)tearDown { [super tearDown]; } -- (void)testQueryAllowed { - NSString *input = @"ABC123abc"; - NSString *output = [input stringByAddingPercentEncodingForURLQuery:NO]; - NSString *expected = @"ABC123abc"; - BOOL result = [expected isEqualToString:output]; - XCTAssertTrue(result, "Expected: %@ got %@",expected,output); +- (void)testRFC3986AlphaNumericNotEncoded { + NSString *input = @"abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789"; + NSString *output = [input stringByAddingPercentEncodingForRFC3986]; + XCTAssertEqualObjects(input, output); } -- (void)testFormAllowed { - NSString *input = @"ABC123abc"; - NSString *output = [input stringByAddingPercentEncodingForURLFormData]; - NSString *expected = @"ABC123abc"; - BOOL result = [expected isEqualToString:output]; - XCTAssertTrue(result, "Expected: %@ got %@",expected,output); +- (void)testFormAlphaNumericNotEncoded { + NSString *input = @"abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789"; + NSString *output = [input stringByAddingPercentEncodingForFormData:NO]; + XCTAssertEqualObjects(input, output); } -- (void)testQuerySpaceIsPercentEncoded { +- (void)testRFC3986UnreservedNotEncoded { + NSString *input = @"-._~"; + NSString *output = [input stringByAddingPercentEncodingForRFC3986]; + XCTAssertEqualObjects(input, output); +} + +- (void)testRFC3986SlashQuestionNotEncoded { + NSString *input = @"/?"; + NSString *output = [input stringByAddingPercentEncodingForRFC3986]; + XCTAssertEqualObjects(input, output); +} + +- (void)testFormUnreservedNotEncoded { + NSString *input = @"*-._"; + NSString *output = [input stringByAddingPercentEncodingForFormData:NO]; + XCTAssertEqualObjects(input, output); +} + +- (void)testQuerySpacePercentEncoded { NSString *input = @"one two"; - NSString *output = [input stringByAddingPercentEncodingForURLQuery:NO]; + NSString *output = [input stringByAddingPercentEncodingForRFC3986]; NSString *expected = @"one%20two"; - BOOL result = [expected isEqualToString:output]; - XCTAssertTrue(result, "Expected: %@ got %@",expected,output); + XCTAssertEqualObjects(expected, output); } -- (void)testQuerySpaceIsPlusEncoded { +- (void)testFormSpacePercentEncoded { NSString *input = @"one two"; - NSString *output = [input stringByAddingPercentEncodingForURLQuery:YES]; - NSString *expected = @"one+two"; - BOOL result = [expected isEqualToString:output]; - XCTAssertTrue(result, "Expected: %@ got %@",expected,output); + NSString *output = [input stringByAddingPercentEncodingForFormData:NO]; + NSString *expected = @"one%20two"; + XCTAssertEqualObjects(expected, output); } -- (void)testFormSpaceIsPlusEncoded { +- (void)testFormSpacePlusEncoded { NSString *input = @"one two"; - NSString *output = [input stringByAddingPercentEncodingForURLFormData]; + NSString *output = [input stringByAddingPercentEncodingForFormData:YES]; NSString *expected = @"one+two"; - BOOL result = [expected isEqualToString:output]; - XCTAssertTrue(result, "Expected: %@ got %@",expected,output); + XCTAssertEqualObjects(expected, output); } -- (void)testQueryPlusIsEncodedWhenUsingPlusForSpaces { +- (void) testFormPlusIsPercentEncoded { NSString *input = @"one+two"; - NSString *output = [input stringByAddingPercentEncodingForURLQuery:YES]; + NSString *output = [input stringByAddingPercentEncodingForFormData:YES]; NSString *expected = @"one%2Btwo"; - BOOL result = [expected isEqualToString:output]; - XCTAssertTrue(result, "Expected: %@ got %@",expected,output); + XCTAssertEqualObjects(expected, output); } -- (void)testQueryPercentIsEncoded { +- (void) testQueryPercentPercentEncoded { NSString *input = @"%"; - NSString *output = [input stringByAddingPercentEncodingForURLQuery:NO]; + NSString *output = [input stringByAddingPercentEncodingForRFC3986]; NSString *expected = @"%25"; - BOOL result = [expected isEqualToString:output]; - XCTAssertTrue(result, "Expected: %@ got %@",expected,output); + XCTAssertEqualObjects(expected, output); } -- (void)testFormPercentIsEncoded { +- (void) testFormPercentPercentEncoded { NSString *input = @"%"; - NSString *output = [input stringByAddingPercentEncodingForURLFormData]; + NSString *output = [input stringByAddingPercentEncodingForFormData:NO]; NSString *expected = @"%25"; - BOOL result = [expected isEqualToString:output]; - XCTAssertTrue(result, "Expected: %@ got %@",expected,output); + XCTAssertEqualObjects(expected, output); } -- (void)testQueryReservedEncoded { - NSString *input = @"!#$&'()*+,/:;=?@[]"; - NSString *output = [input stringByAddingPercentEncodingForURLQuery:NO]; - NSString *expected = @"%21%23%24%26%27%28%29%2A%2B%2C%2F%3A%3B%3D%3F%40%5B%5D"; - BOOL result = [expected isEqualToString:output]; - XCTAssertTrue(result, "Expected: %@ got %@",expected,output); +- (void) testQueryReservedPercentEncoded { + NSString *input = @"!#$&'()*+,:;=@[]"; + NSString *output = [input stringByAddingPercentEncodingForRFC3986]; + NSString *expected = @"%21%23%24%26%27%28%29%2A%2B%2C%3A%3B%3D%40%5B%5D"; + XCTAssertEqualObjects(expected, output); } -- (void)testFormReservedEncoded { +- (void) testFormReservedPercentEncoded { NSString *input = @"!#$&'()+,/:;=?@[]"; - NSString *output = [input stringByAddingPercentEncodingForURLFormData]; + NSString *output = [input stringByAddingPercentEncodingForFormData:NO]; NSString *expected = @"%21%23%24%26%27%28%29%2B%2C%2F%3A%3B%3D%3F%40%5B%5D"; - BOOL result = [expected isEqualToString:output]; - XCTAssertTrue(result, "Expected: %@ got %@",expected,output); -} - -- (void)testQueryUnreservedNotEncoded { - NSString *input = @"-._~"; - NSString *output = [input stringByAddingPercentEncodingForURLQuery:NO]; - NSString *expected = @"-._~"; - BOOL result = [expected isEqualToString:output]; - XCTAssertTrue(result, "Expected: %@ got %@",expected,output); -} - -- (void)testFormUnreservedNotEncoded { - NSString *input = @"-._*"; - NSString *output = [input stringByAddingPercentEncodingForURLFormData]; - NSString *expected = @"-._*"; - BOOL result = [expected isEqualToString:output]; - XCTAssertTrue(result, "Expected: %@ got %@",expected,output); -} - -- (void)testFormTidleEncoded { - NSString *input = @"~"; - NSString *output = [input stringByAddingPercentEncodingForURLFormData]; - NSString *expected = @"%7E"; - BOOL result = [expected isEqualToString:output]; - XCTAssertTrue(result, "Expected: %@ got %@",expected,output); + XCTAssertEqualObjects(expected, output); } @end From 36543736610412b3549d4998486719bd377362f7 Mon Sep 17 00:00:00 2001 From: Keith Harrison Date: Fri, 1 Jan 2016 15:50:12 +0000 Subject: [PATCH 010/184] Add Encode project --- Encode/README.md | 44 ++ Encode/encode.xcodeproj/project.pbxproj | 523 ++++++++++++++++++ Encode/encode/AppDelegate.swift | 41 ++ .../AppIcon.appiconset/Contents.json | 73 +++ .../encode/Base.lproj/LaunchScreen.storyboard | 91 +++ Encode/encode/Base.lproj/Main.storyboard | 107 ++++ Encode/encode/Info.plist | 47 ++ Encode/encode/String+URLEncode.swift | 99 ++++ Encode/encode/ViewController.swift | 61 ++ Encode/encodeTests/Info.plist | 24 + Encode/encodeTests/String+URLEncodeTest.swift | 134 +++++ Encode/encodeUITests/Info.plist | 24 + Encode/encodeUITests/encodeUITests.swift | 86 +++ 13 files changed, 1354 insertions(+) create mode 100644 Encode/README.md create mode 100644 Encode/encode.xcodeproj/project.pbxproj create mode 100644 Encode/encode/AppDelegate.swift create mode 100644 Encode/encode/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 Encode/encode/Base.lproj/LaunchScreen.storyboard create mode 100644 Encode/encode/Base.lproj/Main.storyboard create mode 100644 Encode/encode/Info.plist create mode 100644 Encode/encode/String+URLEncode.swift create mode 100644 Encode/encode/ViewController.swift create mode 100644 Encode/encodeTests/Info.plist create mode 100644 Encode/encodeTests/String+URLEncodeTest.swift create mode 100644 Encode/encodeUITests/Info.plist create mode 100644 Encode/encodeUITests/encodeUITests.swift diff --git a/Encode/README.md b/Encode/README.md new file mode 100644 index 0000000..4b9a51e --- /dev/null +++ b/Encode/README.md @@ -0,0 +1,44 @@ +--- +Encode - experimenting with different URL encoding schemes + +Version 1.0 01 Jan 2016 Initial version. + +--- + +A String extension that provides two different approaches to +percent encode a URL query. + +### public func stringByAddingPercentEncodingForRFC3986() -> String? + +Returns a new string made from the receiver by replacing characters which are +reserved in a URI query with percent encoded characters. + +The following characters are not considered reserved in a URI query +by RFC 3986: + +- Alpha "a...z" "A...Z" +- Numberic "0...9" +- Unreserved "-._~" + +In addition the reserved characters "/" and "?" have no reserved purpose in the +query component of a URI so do not need to be percent escaped. + +**Returns:** The encoded string, or nil if the transformation is not possible. + +### public func stringByAddingPercentEncodingForFormData(plusForSpace: Bool=false) -> String? { + +Returns a new string made from the receiver by replacing characters which are +reserved in HTML forms (media type application/x-www-form-urlencoded) with +percent encoded characters. + +The W3C HTML5 specification, section 4.10.22.5 URL-encoded form +data percent encodes all characters except the following: + +- Space (0x20) is replaced by a "+" (0x2B) +- Bytes in the range 0x2A, 0x2D, 0x2E, 0x30-0x39, 0x41-0x5A, 0x5F, 0x61-0x7A +(alphanumeric + "*-._") + +**Parameter:** plusForSpace: Boolean, when true replaces space with a '+' +otherwise uses percent encoding (%20). Default is false. + +**Returns:** The encoded string, or nil if the transformation is not possible. \ No newline at end of file diff --git a/Encode/encode.xcodeproj/project.pbxproj b/Encode/encode.xcodeproj/project.pbxproj new file mode 100644 index 0000000..d1f32bb --- /dev/null +++ b/Encode/encode.xcodeproj/project.pbxproj @@ -0,0 +1,523 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 53BE6B2E1C356461001C8FC9 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53BE6B2D1C356461001C8FC9 /* AppDelegate.swift */; }; + 53BE6B301C356461001C8FC9 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53BE6B2F1C356461001C8FC9 /* ViewController.swift */; }; + 53BE6B331C356461001C8FC9 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 53BE6B311C356461001C8FC9 /* Main.storyboard */; }; + 53BE6B351C356461001C8FC9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 53BE6B341C356461001C8FC9 /* Assets.xcassets */; }; + 53BE6B381C356461001C8FC9 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 53BE6B361C356461001C8FC9 /* LaunchScreen.storyboard */; }; + 53BE6B531C35E198001C8FC9 /* String+URLEncode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53BE6B521C35E198001C8FC9 /* String+URLEncode.swift */; }; + 53BE6B541C35E198001C8FC9 /* String+URLEncode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53BE6B521C35E198001C8FC9 /* String+URLEncode.swift */; }; + 53BE6B561C35E1B6001C8FC9 /* String+URLEncodeTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53BE6B551C35E1B6001C8FC9 /* String+URLEncodeTest.swift */; }; + 53BE6B5E1C35E55B001C8FC9 /* encodeUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53BE6B5D1C35E55B001C8FC9 /* encodeUITests.swift */; }; + 53BE6B651C35E670001C8FC9 /* String+URLEncode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53BE6B521C35E198001C8FC9 /* String+URLEncode.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 53BE6B3F1C356462001C8FC9 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 53BE6B221C356461001C8FC9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 53BE6B291C356461001C8FC9; + remoteInfo = encode; + }; + 53BE6B601C35E55B001C8FC9 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 53BE6B221C356461001C8FC9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 53BE6B291C356461001C8FC9; + remoteInfo = encode; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 53BE6B2A1C356461001C8FC9 /* encode.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = encode.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 53BE6B2D1C356461001C8FC9 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 53BE6B2F1C356461001C8FC9 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + 53BE6B321C356461001C8FC9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 53BE6B341C356461001C8FC9 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 53BE6B371C356461001C8FC9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 53BE6B391C356461001C8FC9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 53BE6B3E1C356462001C8FC9 /* encodeTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = encodeTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 53BE6B441C356462001C8FC9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 53BE6B521C35E198001C8FC9 /* String+URLEncode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+URLEncode.swift"; sourceTree = ""; }; + 53BE6B551C35E1B6001C8FC9 /* String+URLEncodeTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+URLEncodeTest.swift"; sourceTree = ""; }; + 53BE6B5B1C35E55B001C8FC9 /* encodeUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = encodeUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 53BE6B5D1C35E55B001C8FC9 /* encodeUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = encodeUITests.swift; sourceTree = ""; }; + 53BE6B5F1C35E55B001C8FC9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 53DEA5051C36D455006334B0 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 53BE6B271C356461001C8FC9 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 53BE6B3B1C356462001C8FC9 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 53BE6B581C35E55B001C8FC9 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 53BE6B211C356461001C8FC9 = { + isa = PBXGroup; + children = ( + 53DEA5051C36D455006334B0 /* README.md */, + 53BE6B2C1C356461001C8FC9 /* encode */, + 53BE6B411C356462001C8FC9 /* encodeTests */, + 53BE6B5C1C35E55B001C8FC9 /* encodeUITests */, + 53BE6B2B1C356461001C8FC9 /* Products */, + ); + sourceTree = ""; + }; + 53BE6B2B1C356461001C8FC9 /* Products */ = { + isa = PBXGroup; + children = ( + 53BE6B2A1C356461001C8FC9 /* encode.app */, + 53BE6B3E1C356462001C8FC9 /* encodeTests.xctest */, + 53BE6B5B1C35E55B001C8FC9 /* encodeUITests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 53BE6B2C1C356461001C8FC9 /* encode */ = { + isa = PBXGroup; + children = ( + 53BE6B2D1C356461001C8FC9 /* AppDelegate.swift */, + 53BE6B2F1C356461001C8FC9 /* ViewController.swift */, + 53BE6B311C356461001C8FC9 /* Main.storyboard */, + 53BE6B341C356461001C8FC9 /* Assets.xcassets */, + 53BE6B361C356461001C8FC9 /* LaunchScreen.storyboard */, + 53BE6B391C356461001C8FC9 /* Info.plist */, + 53BE6B521C35E198001C8FC9 /* String+URLEncode.swift */, + ); + path = encode; + sourceTree = ""; + }; + 53BE6B411C356462001C8FC9 /* encodeTests */ = { + isa = PBXGroup; + children = ( + 53BE6B551C35E1B6001C8FC9 /* String+URLEncodeTest.swift */, + 53BE6B441C356462001C8FC9 /* Info.plist */, + ); + path = encodeTests; + sourceTree = ""; + }; + 53BE6B5C1C35E55B001C8FC9 /* encodeUITests */ = { + isa = PBXGroup; + children = ( + 53BE6B5D1C35E55B001C8FC9 /* encodeUITests.swift */, + 53BE6B5F1C35E55B001C8FC9 /* Info.plist */, + ); + path = encodeUITests; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 53BE6B291C356461001C8FC9 /* encode */ = { + isa = PBXNativeTarget; + buildConfigurationList = 53BE6B471C356462001C8FC9 /* Build configuration list for PBXNativeTarget "encode" */; + buildPhases = ( + 53BE6B261C356461001C8FC9 /* Sources */, + 53BE6B271C356461001C8FC9 /* Frameworks */, + 53BE6B281C356461001C8FC9 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = encode; + productName = encode; + productReference = 53BE6B2A1C356461001C8FC9 /* encode.app */; + productType = "com.apple.product-type.application"; + }; + 53BE6B3D1C356462001C8FC9 /* encodeTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 53BE6B4A1C356462001C8FC9 /* Build configuration list for PBXNativeTarget "encodeTests" */; + buildPhases = ( + 53BE6B3A1C356462001C8FC9 /* Sources */, + 53BE6B3B1C356462001C8FC9 /* Frameworks */, + 53BE6B3C1C356462001C8FC9 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 53BE6B401C356462001C8FC9 /* PBXTargetDependency */, + ); + name = encodeTests; + productName = encodeTests; + productReference = 53BE6B3E1C356462001C8FC9 /* encodeTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 53BE6B5A1C35E55B001C8FC9 /* encodeUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 53BE6B621C35E55B001C8FC9 /* Build configuration list for PBXNativeTarget "encodeUITests" */; + buildPhases = ( + 53BE6B571C35E55B001C8FC9 /* Sources */, + 53BE6B581C35E55B001C8FC9 /* Frameworks */, + 53BE6B591C35E55B001C8FC9 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 53BE6B611C35E55B001C8FC9 /* PBXTargetDependency */, + ); + name = encodeUITests; + productName = encodeUITests; + productReference = 53BE6B5B1C35E55B001C8FC9 /* encodeUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 53BE6B221C356461001C8FC9 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0720; + LastUpgradeCheck = 0720; + ORGANIZATIONNAME = "Keith Harrison"; + TargetAttributes = { + 53BE6B291C356461001C8FC9 = { + CreatedOnToolsVersion = 7.2; + }; + 53BE6B3D1C356462001C8FC9 = { + CreatedOnToolsVersion = 7.2; + TestTargetID = 53BE6B291C356461001C8FC9; + }; + 53BE6B5A1C35E55B001C8FC9 = { + CreatedOnToolsVersion = 7.2; + TestTargetID = 53BE6B291C356461001C8FC9; + }; + }; + }; + buildConfigurationList = 53BE6B251C356461001C8FC9 /* Build configuration list for PBXProject "encode" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 53BE6B211C356461001C8FC9; + productRefGroup = 53BE6B2B1C356461001C8FC9 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 53BE6B291C356461001C8FC9 /* encode */, + 53BE6B3D1C356462001C8FC9 /* encodeTests */, + 53BE6B5A1C35E55B001C8FC9 /* encodeUITests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 53BE6B281C356461001C8FC9 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 53BE6B381C356461001C8FC9 /* LaunchScreen.storyboard in Resources */, + 53BE6B351C356461001C8FC9 /* Assets.xcassets in Resources */, + 53BE6B331C356461001C8FC9 /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 53BE6B3C1C356462001C8FC9 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 53BE6B591C35E55B001C8FC9 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 53BE6B261C356461001C8FC9 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 53BE6B301C356461001C8FC9 /* ViewController.swift in Sources */, + 53BE6B531C35E198001C8FC9 /* String+URLEncode.swift in Sources */, + 53BE6B2E1C356461001C8FC9 /* AppDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 53BE6B3A1C356462001C8FC9 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 53BE6B561C35E1B6001C8FC9 /* String+URLEncodeTest.swift in Sources */, + 53BE6B541C35E198001C8FC9 /* String+URLEncode.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 53BE6B571C35E55B001C8FC9 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 53BE6B651C35E670001C8FC9 /* String+URLEncode.swift in Sources */, + 53BE6B5E1C35E55B001C8FC9 /* encodeUITests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 53BE6B401C356462001C8FC9 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 53BE6B291C356461001C8FC9 /* encode */; + targetProxy = 53BE6B3F1C356462001C8FC9 /* PBXContainerItemProxy */; + }; + 53BE6B611C35E55B001C8FC9 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 53BE6B291C356461001C8FC9 /* encode */; + targetProxy = 53BE6B601C35E55B001C8FC9 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 53BE6B311C356461001C8FC9 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 53BE6B321C356461001C8FC9 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 53BE6B361C356461001C8FC9 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 53BE6B371C356461001C8FC9 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 53BE6B451C356462001C8FC9 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.2; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 53BE6B461C356462001C8FC9 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.2; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 53BE6B481C356462001C8FC9 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = encode/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.useyourloaf.encode; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 53BE6B491C356462001C8FC9 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = encode/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.useyourloaf.encode; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + 53BE6B4B1C356462001C8FC9 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ENABLE_MODULES = YES; + INFOPLIST_FILE = encodeTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.useyourloaf.encodeTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/encode.app/encode"; + }; + name = Debug; + }; + 53BE6B4C1C356462001C8FC9 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ENABLE_MODULES = YES; + INFOPLIST_FILE = encodeTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.useyourloaf.encodeTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/encode.app/encode"; + }; + name = Release; + }; + 53BE6B631C35E55B001C8FC9 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + INFOPLIST_FILE = encodeUITests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.useyourloaf.encodeUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_TARGET_NAME = encode; + USES_XCTRUNNER = YES; + }; + name = Debug; + }; + 53BE6B641C35E55B001C8FC9 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + INFOPLIST_FILE = encodeUITests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.useyourloaf.encodeUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_TARGET_NAME = encode; + USES_XCTRUNNER = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 53BE6B251C356461001C8FC9 /* Build configuration list for PBXProject "encode" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 53BE6B451C356462001C8FC9 /* Debug */, + 53BE6B461C356462001C8FC9 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 53BE6B471C356462001C8FC9 /* Build configuration list for PBXNativeTarget "encode" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 53BE6B481C356462001C8FC9 /* Debug */, + 53BE6B491C356462001C8FC9 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 53BE6B4A1C356462001C8FC9 /* Build configuration list for PBXNativeTarget "encodeTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 53BE6B4B1C356462001C8FC9 /* Debug */, + 53BE6B4C1C356462001C8FC9 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 53BE6B621C35E55B001C8FC9 /* Build configuration list for PBXNativeTarget "encodeUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 53BE6B631C35E55B001C8FC9 /* Debug */, + 53BE6B641C35E55B001C8FC9 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 53BE6B221C356461001C8FC9 /* Project object */; +} diff --git a/Encode/encode/AppDelegate.swift b/Encode/encode/AppDelegate.swift new file mode 100644 index 0000000..7f2b252 --- /dev/null +++ b/Encode/encode/AppDelegate.swift @@ -0,0 +1,41 @@ +// +// AppDelegate.swift +// encode +// +// Created by Keith Harrison http://useyourloaf.com +// Copyright (c) 2016 Keith Harrison. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? +} + diff --git a/Encode/encode/Assets.xcassets/AppIcon.appiconset/Contents.json b/Encode/encode/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..eeea76c --- /dev/null +++ b/Encode/encode/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,73 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Encode/encode/Base.lproj/LaunchScreen.storyboard b/Encode/encode/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..331e43e --- /dev/null +++ b/Encode/encode/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Encode/encode/Base.lproj/Main.storyboard b/Encode/encode/Base.lproj/Main.storyboard new file mode 100644 index 0000000..7ec14df --- /dev/null +++ b/Encode/encode/Base.lproj/Main.storyboard @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Encode/encode/Info.plist b/Encode/encode/Info.plist new file mode 100644 index 0000000..40c6215 --- /dev/null +++ b/Encode/encode/Info.plist @@ -0,0 +1,47 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/Encode/encode/String+URLEncode.swift b/Encode/encode/String+URLEncode.swift new file mode 100644 index 0000000..2d9e9e2 --- /dev/null +++ b/Encode/encode/String+URLEncode.swift @@ -0,0 +1,99 @@ +// +// String+URLEncode.swift +// +// Created by Keith Harrison http://useyourloaf.com +// Copyright (c) 2016 Keith Harrison. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +import Foundation + +/** + A String extension that provides percent encoding of URL + strings following RFC 3986 or the W3C HTML specification. + */ + +extension String { + + /** + Returns a new string made from the receiver by replacing characters which are + reserved in a URI query with percent encoded characters. + + The following characters are not considered reserved in a URI query + by RFC 3986: + + - Alpha "a...z" "A...Z" + - Numberic "0...9" + - Unreserved "-._~" + + In addition the reserved characters "/" and "?" have no reserved purpose in the + query component of a URI so do not need to be percent escaped. + + - Returns: The encoded string, or nil if the transformation is not possible. + */ + + public func stringByAddingPercentEncodingForRFC3986() -> String? { + let unreserved = "-._~/?" + let allowedCharacterSet = NSMutableCharacterSet.alphanumericCharacterSet() + allowedCharacterSet.addCharactersInString(unreserved) + return stringByAddingPercentEncodingWithAllowedCharacters(allowedCharacterSet) + } + + /** + Returns a new string made from the receiver by replacing characters which are + reserved in HTML forms (media type application/x-www-form-urlencoded) with + percent encoded characters. + + The W3C HTML5 specification, section 4.10.22.5 URL-encoded form + data percent encodes all characters except the following: + + - Space (0x20) is replaced by a "+" (0x2B) + - Bytes in the range 0x2A, 0x2D, 0x2E, 0x30-0x39, 0x41-0x5A, 0x5F, 0x61-0x7A + (alphanumeric + "*-._") + + - Parameter plusForSpace: Boolean, when true replaces space with a '+' + otherwise uses percent encoding (%20). Default is false. + + - Returns: The encoded string, or nil if the transformation is not possible. + */ + + public func stringByAddingPercentEncodingForFormData(plusForSpace: Bool=false) -> String? { + let unreserved = "*-._" + let allowedCharacterSet = NSMutableCharacterSet.alphanumericCharacterSet() + allowedCharacterSet.addCharactersInString(unreserved) + + if plusForSpace { + allowedCharacterSet.addCharactersInString(" ") + } + + var encoded = stringByAddingPercentEncodingWithAllowedCharacters(allowedCharacterSet) + if plusForSpace { + encoded = encoded?.stringByReplacingOccurrencesOfString(" ", withString: "+") + } + return encoded + } +} diff --git a/Encode/encode/ViewController.swift b/Encode/encode/ViewController.swift new file mode 100644 index 0000000..720ca8b --- /dev/null +++ b/Encode/encode/ViewController.swift @@ -0,0 +1,61 @@ +// +// ViewController.swift +// encode +// +// Created by Keith Harrison http://useyourloaf.com +// Copyright (c) 2016 Keith Harrison. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +import UIKit + +class ViewController: UIViewController { + + @IBOutlet weak var rfc3986Output: UILabel! + @IBOutlet weak var formOutput: UILabel! + @IBOutlet weak var textInput: UITextField! + @IBOutlet weak var plusSwitch: UISwitch! + + @IBAction func plusValueChanged(sender: UISwitch) { + updateOutputLabels() + } + + func updateOutputLabels() { + let input = textInput.text + let usePlusForSpace = plusSwitch.on + rfc3986Output.text = input?.stringByAddingPercentEncodingForRFC3986() + formOutput.text = input?.stringByAddingPercentEncodingForFormData(usePlusForSpace) + } +} + +extension ViewController: UITextFieldDelegate { + func textFieldShouldReturn(textField: UITextField) -> Bool { + updateOutputLabels() + return true + } +} + diff --git a/Encode/encodeTests/Info.plist b/Encode/encodeTests/Info.plist new file mode 100644 index 0000000..ba72822 --- /dev/null +++ b/Encode/encodeTests/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/Encode/encodeTests/String+URLEncodeTest.swift b/Encode/encodeTests/String+URLEncodeTest.swift new file mode 100644 index 0000000..4da757f --- /dev/null +++ b/Encode/encodeTests/String+URLEncodeTest.swift @@ -0,0 +1,134 @@ +// +// String+URLEncodeTest.swift +// +// Created by Keith Harrison http://useyourloaf.com +// Copyright (c) 2016 Keith Harrison. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +import XCTest + +class URLEncodeTest: XCTestCase { + + override func setUp() { + super.setUp() + } + + override func tearDown() { + super.tearDown() + } + + func testRFC3986AlphaNumericNotEncoded() { + let input = "abcdefghijklmnopqrstuvwxyz" + + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + + "0123456789" + let output = input.stringByAddingPercentEncodingForRFC3986() + XCTAssertEqual(input, output) + } + + func testFormAlphaNumericNotEncoded() { + let input = "abcdefghijklmnopqrstuvwxyz" + + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + + "0123456789" + let output = input.stringByAddingPercentEncodingForFormData() + XCTAssertEqual(input, output) + } + + func testRFC3986UnreservedNotEncoded() { + let input = "-._~" + let output = input.stringByAddingPercentEncodingForRFC3986() + XCTAssertEqual(input, output) + } + + func testRFC3986SlashQuestionNotEncoded() { + let input = "/?" + let output = input.stringByAddingPercentEncodingForRFC3986() + XCTAssertEqual(input, output) + } + + func testFormUnreservedNotEncoded() { + let input = "*-._" + let output = input.stringByAddingPercentEncodingForFormData() + XCTAssertEqual(input, output) + } + + func testQuerySpacePercentEncoded() { + let input = "one two" + let output = input.stringByAddingPercentEncodingForRFC3986() + let expected = "one%20two" + XCTAssertEqual(expected, output) + } + + func testFormSpacePercentEncoded() { + let input = "one two" + let output = input.stringByAddingPercentEncodingForFormData() + let expected = "one%20two" + XCTAssertEqual(expected, output) + } + + func testFormSpacePlusEncoded() { + let input = "one two" + let output = input.stringByAddingPercentEncodingForFormData(true) + let expected = "one+two" + XCTAssertEqual(expected, output) + } + + func testFormPlusIsPercentEncoded() { + let input = "one+two" + let output = input.stringByAddingPercentEncodingForFormData(true) + let expected = "one%2Btwo" + XCTAssertEqual(expected, output) + } + + func testQueryPercentPercentEncoded() { + let input = "%" + let output = input.stringByAddingPercentEncodingForRFC3986() + let expected = "%25" + XCTAssertEqual(expected, output) + } + + func testFormPercentPercentEncoded() { + let input = "%" + let output = input.stringByAddingPercentEncodingForFormData() + let expected = "%25" + XCTAssertEqual(expected, output) + } + + func testQueryReservedPercentEncoded() { + let input = "!#$&'()*+,:;=@[]" + let output = input.stringByAddingPercentEncodingForRFC3986() + let expected = "%21%23%24%26%27%28%29%2A%2B%2C%3A%3B%3D%40%5B%5D" + XCTAssertEqual(expected, output) + } + + func testFormReservedPercentEncoded() { + let input = "!#$&'()+,/:;=?@[]" + let output = input.stringByAddingPercentEncodingForFormData() + let expected = "%21%23%24%26%27%28%29%2B%2C%2F%3A%3B%3D%3F%40%5B%5D" + XCTAssertEqual(expected, output) + } +} diff --git a/Encode/encodeUITests/Info.plist b/Encode/encodeUITests/Info.plist new file mode 100644 index 0000000..ba72822 --- /dev/null +++ b/Encode/encodeUITests/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/Encode/encodeUITests/encodeUITests.swift b/Encode/encodeUITests/encodeUITests.swift new file mode 100644 index 0000000..9a3a44a --- /dev/null +++ b/Encode/encodeUITests/encodeUITests.swift @@ -0,0 +1,86 @@ +// +// encodeUITests.swift +// encodeUITests +// +// Created by Keith Harrison http://useyourloaf.com +// Copyright (c) 2016 Keith Harrison. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +import XCTest + +class encodeUITests: XCTestCase { + + override func setUp() { + super.setUp() + continueAfterFailure = false + XCUIApplication().launch() + } + + func testOutputUpdated() { + let app = XCUIApplication() + let input = "one two" + let expectedRFC3986 = input.stringByAddingPercentEncodingForRFC3986() + let expectedForm = input.stringByAddingPercentEncodingForFormData() + + let textToEncodeTextField = app.textFields["InputText"] + textToEncodeTextField.tap() + textToEncodeTextField.typeText("\(input)\r") + + let outputRFC3986 = app.staticTexts["RFC3968Output"] + XCTAssertEqual(expectedRFC3986, outputRFC3986.label) + + let outputForm = app.staticTexts["FormOutput"] + XCTAssertEqual(expectedForm, outputForm.label) + } + + func testPlusForSpaceSwitch() { + let app = XCUIApplication() + let input = "one two" + let expectedRFC3986 = input.stringByAddingPercentEncodingForRFC3986() + let expectedForm = input.stringByAddingPercentEncodingForFormData() + let expectedPlusForm = input.stringByAddingPercentEncodingForFormData(true) + + let textToEncodeTextField = app.textFields["InputText"] + textToEncodeTextField.tap() + textToEncodeTextField.typeText("\(input)\r") + + let outputRFC3986 = app.staticTexts["RFC3968Output"] + XCTAssertEqual(expectedRFC3986, outputRFC3986.label) + + let outputForm = app.staticTexts["FormOutput"] + XCTAssertEqual(expectedForm, outputForm.label) + + app.switches["PlusSwitch"].tap() + XCTAssertEqual(expectedRFC3986, outputRFC3986.label) + XCTAssertEqual(expectedPlusForm, outputForm.label) + + app.switches["PlusSwitch"].tap() + XCTAssertEqual(expectedRFC3986, outputRFC3986.label) + XCTAssertEqual(expectedForm, outputForm.label) + } +} From c9f60cd52f628ce6f05e2fd08ca13b39eafd24fe Mon Sep 17 00:00:00 2001 From: Keith Harrison Date: Fri, 1 Jan 2016 15:51:11 +0000 Subject: [PATCH 011/184] Added Encode to index --- README | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README b/README index d351eef..e878b6b 100644 --- a/README +++ b/README @@ -3,7 +3,7 @@ See http://useyourloaf.com for details - This README last updated: 30 Nov 2015 + This README last updated: 01 Jan 2016 ======================================================================= + INDEX @@ -15,6 +15,7 @@ AnimatedConstraints - Animating Autolayout changes Collection - Simple collection view with popovers Designable - IBDESIGNABLE/IB_Inspectable custom views + Encode - Percent encoding of URL query string DynamicText - Supporting dynamic text size changes Huckleberry - Auto layout table view cells with varying row heights MasterSlide - Implementing a mail app style split view controller From 9a5bee7bb281d8ecdc6a8a8b9bdab843fd9a5d2a Mon Sep 17 00:00:00 2001 From: Keith Harrison Date: Mon, 25 Jan 2016 20:40:52 +0000 Subject: [PATCH 012/184] Add AutoLayout --- .../AutoLayout.xcodeproj/project.pbxproj | 298 +++++++++++++++++ AutoLayout/AutoLayout/AppDelegate.swift | 52 +++ .../AppIcon.appiconset/Contents.json | 73 +++++ .../AutoLayout/Assets.xcassets/Contents.json | 6 + .../Heart.imageset/Contents.json | 23 ++ .../Heart.imageset/heart100.png | Bin 0 -> 1135 bytes .../Heart.imageset/heart200.png | Bin 0 -> 1958 bytes .../Heart.imageset/heart300.png | Bin 0 -> 2655 bytes .../Star.imageset/Contents.json | 23 ++ .../Assets.xcassets/Star.imageset/star100.png | Bin 0 -> 1306 bytes .../Assets.xcassets/Star.imageset/star200.png | Bin 0 -> 2143 bytes .../Assets.xcassets/Star.imageset/star300.png | Bin 0 -> 2794 bytes .../Base.lproj/LaunchScreen.storyboard | 51 +++ .../AutoLayout/Base.lproj/Main.storyboard | 300 ++++++++++++++++++ .../CenterAlignViewController.swift | 101 ++++++ AutoLayout/AutoLayout/Info.plist | 57 ++++ .../AutoLayout/MasterViewController.swift | 49 +++ AutoLayout/README | 8 + README | 3 +- 19 files changed, 1043 insertions(+), 1 deletion(-) create mode 100644 AutoLayout/AutoLayout.xcodeproj/project.pbxproj create mode 100644 AutoLayout/AutoLayout/AppDelegate.swift create mode 100644 AutoLayout/AutoLayout/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 AutoLayout/AutoLayout/Assets.xcassets/Contents.json create mode 100644 AutoLayout/AutoLayout/Assets.xcassets/Heart.imageset/Contents.json create mode 100644 AutoLayout/AutoLayout/Assets.xcassets/Heart.imageset/heart100.png create mode 100644 AutoLayout/AutoLayout/Assets.xcassets/Heart.imageset/heart200.png create mode 100644 AutoLayout/AutoLayout/Assets.xcassets/Heart.imageset/heart300.png create mode 100644 AutoLayout/AutoLayout/Assets.xcassets/Star.imageset/Contents.json create mode 100644 AutoLayout/AutoLayout/Assets.xcassets/Star.imageset/star100.png create mode 100644 AutoLayout/AutoLayout/Assets.xcassets/Star.imageset/star200.png create mode 100644 AutoLayout/AutoLayout/Assets.xcassets/Star.imageset/star300.png create mode 100644 AutoLayout/AutoLayout/Base.lproj/LaunchScreen.storyboard create mode 100644 AutoLayout/AutoLayout/Base.lproj/Main.storyboard create mode 100644 AutoLayout/AutoLayout/CenterAlignViewController.swift create mode 100644 AutoLayout/AutoLayout/Info.plist create mode 100644 AutoLayout/AutoLayout/MasterViewController.swift create mode 100644 AutoLayout/README diff --git a/AutoLayout/AutoLayout.xcodeproj/project.pbxproj b/AutoLayout/AutoLayout.xcodeproj/project.pbxproj new file mode 100644 index 0000000..5642e4c --- /dev/null +++ b/AutoLayout/AutoLayout.xcodeproj/project.pbxproj @@ -0,0 +1,298 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 47; + objects = { + +/* Begin PBXBuildFile section */ + 5362E7F81C53F1B200DAA88B /* CenterAlignViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5362E7F71C53F1B200DAA88B /* CenterAlignViewController.swift */; }; + 53D6A5F11C53BF5200A81A14 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53D6A5F01C53BF5200A81A14 /* AppDelegate.swift */; }; + 53D6A5F31C53BF5200A81A14 /* MasterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53D6A5F21C53BF5200A81A14 /* MasterViewController.swift */; }; + 53D6A5F81C53BF5200A81A14 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 53D6A5F61C53BF5200A81A14 /* Main.storyboard */; }; + 53D6A5FA1C53BF5200A81A14 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 53D6A5F91C53BF5200A81A14 /* Assets.xcassets */; }; + 53D6A5FD1C53BF5200A81A14 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 53D6A5FB1C53BF5200A81A14 /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 5362E7F71C53F1B200DAA88B /* CenterAlignViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = CenterAlignViewController.swift; sourceTree = ""; tabWidth = 4; }; + 53D6A5ED1C53BF5200A81A14 /* AutoLayout.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AutoLayout.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 53D6A5F01C53BF5200A81A14 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 53D6A5F21C53BF5200A81A14 /* MasterViewController.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = MasterViewController.swift; sourceTree = ""; tabWidth = 4; }; + 53D6A5F71C53BF5200A81A14 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 53D6A5F91C53BF5200A81A14 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 53D6A5FC1C53BF5200A81A14 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 53D6A5FE1C53BF5200A81A14 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 53D6A5EA1C53BF5200A81A14 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 53D6A5E41C53BF5200A81A14 = { + isa = PBXGroup; + children = ( + 53D6A5EF1C53BF5200A81A14 /* AutoLayout */, + 53D6A5EE1C53BF5200A81A14 /* Products */, + ); + sourceTree = ""; + }; + 53D6A5EE1C53BF5200A81A14 /* Products */ = { + isa = PBXGroup; + children = ( + 53D6A5ED1C53BF5200A81A14 /* AutoLayout.app */, + ); + name = Products; + sourceTree = ""; + }; + 53D6A5EF1C53BF5200A81A14 /* AutoLayout */ = { + isa = PBXGroup; + children = ( + 53D6A5F01C53BF5200A81A14 /* AppDelegate.swift */, + 53D6A5F21C53BF5200A81A14 /* MasterViewController.swift */, + 5362E7F71C53F1B200DAA88B /* CenterAlignViewController.swift */, + 53D6A5F61C53BF5200A81A14 /* Main.storyboard */, + 53D6A5F91C53BF5200A81A14 /* Assets.xcassets */, + 53D6A5FB1C53BF5200A81A14 /* LaunchScreen.storyboard */, + 53D6A5FE1C53BF5200A81A14 /* Info.plist */, + ); + path = AutoLayout; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 53D6A5EC1C53BF5200A81A14 /* AutoLayout */ = { + isa = PBXNativeTarget; + buildConfigurationList = 53D6A6011C53BF5200A81A14 /* Build configuration list for PBXNativeTarget "AutoLayout" */; + buildPhases = ( + 53D6A5E91C53BF5200A81A14 /* Sources */, + 53D6A5EA1C53BF5200A81A14 /* Frameworks */, + 53D6A5EB1C53BF5200A81A14 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = AutoLayout; + productName = AutoLayout; + productReference = 53D6A5ED1C53BF5200A81A14 /* AutoLayout.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 53D6A5E51C53BF5200A81A14 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0720; + LastUpgradeCheck = 0720; + ORGANIZATIONNAME = "Keith Harrison"; + TargetAttributes = { + 53D6A5EC1C53BF5200A81A14 = { + CreatedOnToolsVersion = 7.2; + }; + }; + }; + buildConfigurationList = 53D6A5E81C53BF5200A81A14 /* Build configuration list for PBXProject "AutoLayout" */; + compatibilityVersion = "Xcode 6.3"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 53D6A5E41C53BF5200A81A14; + productRefGroup = 53D6A5EE1C53BF5200A81A14 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 53D6A5EC1C53BF5200A81A14 /* AutoLayout */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 53D6A5EB1C53BF5200A81A14 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 53D6A5FD1C53BF5200A81A14 /* LaunchScreen.storyboard in Resources */, + 53D6A5FA1C53BF5200A81A14 /* Assets.xcassets in Resources */, + 53D6A5F81C53BF5200A81A14 /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 53D6A5E91C53BF5200A81A14 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 53D6A5F31C53BF5200A81A14 /* MasterViewController.swift in Sources */, + 5362E7F81C53F1B200DAA88B /* CenterAlignViewController.swift in Sources */, + 53D6A5F11C53BF5200A81A14 /* AppDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 53D6A5F61C53BF5200A81A14 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 53D6A5F71C53BF5200A81A14 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 53D6A5FB1C53BF5200A81A14 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 53D6A5FC1C53BF5200A81A14 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 53D6A5FF1C53BF5200A81A14 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.2; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 53D6A6001C53BF5200A81A14 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.2; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 53D6A6021C53BF5200A81A14 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = AutoLayout/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.useyourloaf.AutoLayout; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 53D6A6031C53BF5200A81A14 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = AutoLayout/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.useyourloaf.AutoLayout; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 53D6A5E81C53BF5200A81A14 /* Build configuration list for PBXProject "AutoLayout" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 53D6A5FF1C53BF5200A81A14 /* Debug */, + 53D6A6001C53BF5200A81A14 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 53D6A6011C53BF5200A81A14 /* Build configuration list for PBXNativeTarget "AutoLayout" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 53D6A6021C53BF5200A81A14 /* Debug */, + 53D6A6031C53BF5200A81A14 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 53D6A5E51C53BF5200A81A14 /* Project object */; +} diff --git a/AutoLayout/AutoLayout/AppDelegate.swift b/AutoLayout/AutoLayout/AppDelegate.swift new file mode 100644 index 0000000..43e7fa3 --- /dev/null +++ b/AutoLayout/AutoLayout/AppDelegate.swift @@ -0,0 +1,52 @@ +// +// AppDelegate.swift +// AutoLayout +// +// Created by Keith Harrison http://useyourloaf.com +// Copyright (c) 2016 Keith Harrison. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDelegate { + + var window: UIWindow? + + + func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { + let splitViewController = self.window!.rootViewController as! UISplitViewController + splitViewController.delegate = self + return true + } + + func splitViewController(svc: UISplitViewController, shouldHideViewController vc: UIViewController, inOrientation orientation: UIInterfaceOrientation) -> Bool { + return false + } +} + diff --git a/AutoLayout/AutoLayout/Assets.xcassets/AppIcon.appiconset/Contents.json b/AutoLayout/AutoLayout/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..eeea76c --- /dev/null +++ b/AutoLayout/AutoLayout/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,73 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/AutoLayout/AutoLayout/Assets.xcassets/Contents.json b/AutoLayout/AutoLayout/Assets.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/AutoLayout/AutoLayout/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/AutoLayout/AutoLayout/Assets.xcassets/Heart.imageset/Contents.json b/AutoLayout/AutoLayout/Assets.xcassets/Heart.imageset/Contents.json new file mode 100644 index 0000000..581c8f1 --- /dev/null +++ b/AutoLayout/AutoLayout/Assets.xcassets/Heart.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "heart100.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "heart200.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "heart300.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/AutoLayout/AutoLayout/Assets.xcassets/Heart.imageset/heart100.png b/AutoLayout/AutoLayout/Assets.xcassets/Heart.imageset/heart100.png new file mode 100644 index 0000000000000000000000000000000000000000..d6be769605f6debffe0476e66459073bd52d11e3 GIT binary patch literal 1135 zcmV-#1d#iQP)OcbPJpk%I0^VyZ&8KF*+nB|{e&Sjj>OTSLK?K}zG0doE zz22I}zplVKM+wiLUc zVI#!EfyuXY=|u?OVkE-PjOs!K=|l$1t7+y>5yZ!Y)r?HN;GNE)VCP8-#=U#ighR!{ zfWyd!&!Atv+n3~5801wK$Gv;YtZUwFF1_EI<5(K#NDIHzk-^fB&7@+|l~ljhlE2lG z#=m{O+LzXbM%0T--fS()uWjl>2G@i|;A17d+?c}7i^jly$+L0Cx_HRAcEietzS)-C zaWdX&E5pf!#Kwc=R2IO~klS@O-e@Vtym{VjE#*-Yz2KbObT#8w8PaE(~?ofzI@h)N6W5k&7)%5b2HPBP|%uM+;cR}qG8&2ILoYR z*?T+5wR6~iK<7#f)Q?Z!W+&=E1I(>!#m0l?P7uP)isV)pz}A!6c{#g^KjHuY0$fQ% zK~#7F?bhR>1P2rZ;l8%n*S2ljwr$(7?Wc{o8Sb17=X-(rTlhz^*=#nOo4bdnm$#3v zpML-}1O^3%gocGjM0y0kq^RhaSpFg|J|Pi`l0uVH_>0uE^b8o6nMDWLIk|9@m!3}t z1%)uEsJMiGR$7*aKPa!Dg32lwQC&l2wRQL%-+C%)XoSAPRH|ue#;;nas-+b=+DfRd zy#qgspoUIp>q@1T(r)~yhngaxrMHjT`rQCvAeEX@2B9I8dWHZDk5I?x7*vg?Qr849 zNnJTmclUHIi*x(4_iTVBV`VCGIsiAuop}l)Mh7T)ZK0jJk%?Xw;j4 zctyV*laeN?K`6buTcK0J!b#_002ovPDHLkV1lPP BF;oBm literal 0 HcmV?d00001 diff --git a/AutoLayout/AutoLayout/Assets.xcassets/Heart.imageset/heart200.png b/AutoLayout/AutoLayout/Assets.xcassets/Heart.imageset/heart200.png new file mode 100644 index 0000000000000000000000000000000000000000..932019f94132479fa12c96df192546a77164dad4 GIT binary patch literal 1958 zcmV;X2U+-uP)LEdR9M z)r?HuZ7$_h7V1I;=tc?FghR=)aJ}4^$+vXWi%Z~PBkDi{!O)G%t7+MNJ?2jl-fS(* zsb{_3n%0O%=1mXHsAj$2o4wzh%CT?Ub2RBe1m{W&)`mvH&5FF?oy@Ik;$0upkx;(c zm(rG1*?vCaS{%}qRnw4A=tv9Kgha&0gym5a=1vgXb2GozlEBlD+;1?z)|0}|jLEce z$+B_hOAh5w62`oF$GdpalvC{ZJk6tG;9?`(a539*GtQ=D;$9%! zaWdX&E6TBO;$0rudOO3)hv-KM+ITqQSQ^}NGRv=R)rm;Rw{_rQBI!g1zt@w~l2P4n zFzQ1F!^nonwR7o32j6HY%dBhQWhUcX9pqFNVbGdc-e@VvyCRO*+L;y1Jda(1GNxVHnmeiB3J6iqh2YPKSD= z!lYQ|Oe*Nv3nl5@kP7Vp8qkQ$B>=FL zMl6E5@G_dQ96)YIno$wTYSWYzKn4w231zEj%IY?eH8iCK6s@H>>sHX1Stv$W;k>RN zD#Ms(QlMrgh1vuq^(fUEDA`Q0>On;ZinRqQwoP_v;X z_b^48-yDi|Q>1ZFRj)cFIs#>rC{T@&P_}{s?S;BFhbd15XlOujra;HhsN8;5z`H4j zW6*Mp!W@UDu@q%~UuZfJr6ebz?Gy!Bd>Y!$B+Hy(R9A~)Eb9#y$}Of8>*2~A3Xv6t zE8#e0I186nP=s@B;8q`*GuP@;&NJ~Yz<`|s+QJ+d5Sb^lx2W!7G85}u3}&=sLRH@n zLq=YbIpd)2#9}7SvxP8cGZUuzT$ps3iSjgz3SVJjtf>pLjwLe@k}JTpPE3TWFfR8R z{kJ><^LqE7->#pA7j7)3zoK*DjYkA;!-U`s5!WK{O73O)=jH-W!Xh|DWwN} zk_vC#T1;QuhSwgcP9NOKh35{?d-Fy@f!s>CdvExnE4`Jq9sbC@OE1m82fwr#OYdxK z3;)EH(<{~Q!%u||WUe2m>vf5qXb->jK1UA}z<;OD$qa|O7uLuOhq`hv3Rpc5m?kel zoxpNZeJ4;BLK0nv#Oi&CCJZ}*M0uE?5TSPr8!;q!*9qQ;a64HdGQe9dI^5pKNss@m04wXcALaK#S zkoh{~I#8WXO@9NDy}6Ojq+f(=^)u&_BH-$G!doMW*X=OI9PJzz1i?jcYL-)50kPeZ`O+OY^pRS`ImclbZ;Mk0uA^FQX~ zBADK-&VShW9s(;PlYcqC0|EB_GX5dG5(4dmf&5K=IRsq2MkG1*Nd#W+3FMv#%7DTT sH^Z_l%d#xXvMkH8EX%Si%d*t{3^75F>fvq9<^TWy07*qoM6N<$g8yOE9RL6T literal 0 HcmV?d00001 diff --git a/AutoLayout/AutoLayout/Assets.xcassets/Heart.imageset/heart300.png b/AutoLayout/AutoLayout/Assets.xcassets/Heart.imageset/heart300.png new file mode 100644 index 0000000000000000000000000000000000000000..c24ae626319c2f4962d507978be4bb655175cf46 GIT binary patch literal 2655 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 N002ovPDHLkV1h;)7%cz* literal 0 HcmV?d00001 diff --git a/AutoLayout/AutoLayout/Assets.xcassets/Star.imageset/Contents.json b/AutoLayout/AutoLayout/Assets.xcassets/Star.imageset/Contents.json new file mode 100644 index 0000000..9ee1843 --- /dev/null +++ b/AutoLayout/AutoLayout/Assets.xcassets/Star.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "star100.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "star200.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "star300.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/AutoLayout/AutoLayout/Assets.xcassets/Star.imageset/star100.png b/AutoLayout/AutoLayout/Assets.xcassets/Star.imageset/star100.png new file mode 100644 index 0000000000000000000000000000000000000000..4965255dc5c56ab3d62c08ab371db50421bcb727 GIT binary patch literal 1306 zcmV+#1?BpQP)LRY? zN?hBWG1ke`=_CWu_^ahjSlgj4*UZrACj!y=r{qyp+oUYk&-U#B&FCrt(fXz2R8rcf zDb~{U?gGl`Eu!OBPTH#`)z!%8FP`IDOxms_)z|dx1I6evo8w+d+OZ+k+VkxM!{;@a z;$TJDwI9{n^6dq`=Q)((WJ1`u9n{_N?FPK(JQdUBrsPv<-H$ES%?r}-5!2~4*Tq@f zpnl(b>FW;O>KeM|K9%BQao&hc*{mwo(FoD=4$|#;-+-OtT*BuzD=U;$OYyJL2mWvE@gN;cIH$kh$hR-Rc|B=_ak^ONiibW!;qt z((<(CL~GrSujNUq+B3Y*u6N{ z!gt<+=j#yU>=Q88%H!)5E!NK9>lj+xpCZ-SRNJN3>L4D~+)vu78`R$?)zpmPY`5k? zj^Sz()aO6gyW{H=Y21+()8v!kWkJ}vzUDfX;$p$)H%8gCaNdbX*|NpxGjiUCMA^2? z=qpUxtqjudc;12N>kr51FxKiJh2V7V>?_vL@a+dS*TkgbRYTaf#OE~5=qXd$rl{mlR@w@5Sq2yTn&#c)100N## zL_t(|0qxj>dNeoy1<+gF+P3-Iwr$(0ZQEJnm3-mirOOnTeZ~Lkwd*(hAmHY~Tet7ry+>7R!|y+MxOHO;9LPU?>d|BB zvHi)@XU|{2){B?a>J{ucS4e$QyqMims^M1tB$5S6c@`$EfOfJ%lw-O=B1&oTiGVW=J_ec`dDw z^gNZ4+Oi<6IGa))-++SlwUpr70VSQAD6Fd+ih8zFP;@VpU7(;osOzU7AE+CkpyyC` zkAeoFY>1*BLD?`xjX>Ebg}nwFcJQ1d<|d;leh)wJW%hp+C@j*C#zL0e z6%DlX88OLO~Yiti7`v4*m8!=llT$e{x3Z{ysR>^9N^a@`Su&oE8#=5%a6fB!?8Uoa%y#ERwS=H})$2?F+@vwNYd Qm;e9(07*qoM6N<$f-6757XSbN literal 0 HcmV?d00001 diff --git a/AutoLayout/AutoLayout/Assets.xcassets/Star.imageset/star200.png b/AutoLayout/AutoLayout/Assets.xcassets/Star.imageset/star200.png new file mode 100644 index 0000000000000000000000000000000000000000..49311bcd268c09f49e3bc6005fe674418cd397b1 GIT binary patch literal 2143 zcmV-l2%z_gP)kkjp>nhgK zd*6Pe<5uSE5f0MrC)L#T?gDt;fuQ7Blg^p^BvUP^6dp`-H(#uX5H!=2hsE!)Zg*# z258-qkK$?D>Kz8s^%>ORWZjn8=^q8s_7~IRVceP5>L3Kt_Z8FSUfZ08;B(aJBLmU+ z6V&JI>;s+ogQpeaz@90nz&B>kv@dsmbUr4btxA z>=I4ctaaan#^^Ch+OWjuG)LL9!RI##((**vwrkyvzUDgK>KZ`Ux<1*wV%(Uz=RR%S zj3(99IoQG0>LIb^M~&fYYTb}V*|b^QprYef#pp8W>kh5uONrrcX5ExR*tjv)$yM8= zdEbDY<6Oe$HqGcNJ=nc1*3D7csJ-Sp;p-QH;CNlzoGI4QOxmrL;$ykyKgH-Xq2yS7 z-+W2hv25Lqkm6{y=0h;o$|2R-McK7!+>wmoY}M)_(CH_)=0POY*I?Y5+Ug!3)!Q7@ z-b>oA$mlOY*ts;<#<=D{f2q%sXoNM}YS9FRpa8xBY&nUf27d3=@+c?En{SOjs! zd{qK*rF>OZ4rvv9R|#oVd{yoU}NKuGcm~!rXXjUq0G!e%-lRfT7Z~EhPJc} zDJ!cCV+~T)8P3KggluIntZfL%W_UZhkg=D;2=*Z(pOG9KLc~!aBRYnN6GnD=1_|dE zjN%d!DjChyH3ZyLGpbt%xMOtp*N^Sl|YHrIIi>E)Ug&hrnqOkyO+TPU}upJ>asU zMX0PN99Ep_dcj?#O45Vga8_A*(Fd;TTb`cugQNP>n*nfBwSn|#5L^=yZSauhL+MrH zD#LmXuLCDE9nmaxrtriJT)~l}Mvoa=tR*aIpI}_Z@e?Lemr89XO`bBfVmpY*Ic@rk znX@8kMy%Osnm3wL7bX>*JFk0*`3oqab$F(Qi$*ksUey*aDZX@BRmz*Ve8tLf3ECrA z4GOGUy=HCw+_WrTpP4hJugeMVTCU%)aZ{qrW$2Uz^Gg&8OJ54Ank?N?gYL>ub?Y{$ z8D5nx%e@_{itV7g3bui|osH?LGSlJ3t`>Atr7(C?cMDxqV>i5t-<9r(H5T5r+C$eQ z-itt_TTZv6n1jGX=|h)HjDtW$%%?j-_aSga_tO>m)*%>uqtXor(jz$44$=kX7b948 z($K!D1rWR@hiKc5V$h-GVcOOB2=r)olr|l!4qZZy)1FSTp-)H*+S0Wtv`R=j_Oya# zCuqa+bkME~<(-TI15Q!e>HIL_3}tOC1w+PA(%HT+rd|e$nN$r1HOoyQ=NiDMaRn*j zd=nUUp$r9FYzgBoRibev+Cf0i%QWmt2t;g%N25kV%AmwF=qltyPeEfALejO&G$b2j zCCx!2xFqrzSfdw0>-(6U6h5F@#1p{b6Y{<(&1MWdwG@39B z;x^EP`w(}ACd`7kqckCH2&65i5j!C)q&1D`31P)(Mj;4$Kr;?Q*eRM34q+>3My!^Q z^^k@Pho~ksXh;}DEv6~iAnF25$plduY04IedPGyo)PbbORM)55{W4TH7lJAjq^hn< z02DZ%fvP${P%0`KIj$Yvx}J!NQbSN1D!DVI0X}+ino5#GP%adsVkCFSq&_+wF>NVfrhKt zEf>|d6>#(UxiU+uBRF*`FW?s%LPjV*$&h^;f>-qKP=02re7e234n5NSlb9c^mQJ_R zBcM;iHI;+|?hN1QF&282o|#)T;EwW*w*Q7eyR2#WMv4$f=tZ*j_LeYWUre!p3u(;* ztE<77z5fXX-1><#^24ZZv;PkPP20n?b_HOH&*$^`d_JGg=kxh|KA+F$^Z9%}-z&Ga V#rHCL@ksyx002ovPDHLkV1iS=M+yJ{ literal 0 HcmV?d00001 diff --git a/AutoLayout/AutoLayout/Assets.xcassets/Star.imageset/star300.png b/AutoLayout/AutoLayout/Assets.xcassets/Star.imageset/star300.png new file mode 100644 index 0000000000000000000000000000000000000000..d46d41b635b7d094c17ba416aec8c1649adb66ac GIT binary patch literal 2794 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 z + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AutoLayout/AutoLayout/Base.lproj/Main.storyboard b/AutoLayout/AutoLayout/Base.lproj/Main.storyboard new file mode 100644 index 0000000..01fbc4a --- /dev/null +++ b/AutoLayout/AutoLayout/Base.lproj/Main.storyboard @@ -0,0 +1,300 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AutoLayout/AutoLayout/CenterAlignViewController.swift b/AutoLayout/AutoLayout/CenterAlignViewController.swift new file mode 100644 index 0000000..bfb9b17 --- /dev/null +++ b/AutoLayout/AutoLayout/CenterAlignViewController.swift @@ -0,0 +1,101 @@ +// +// CenterAlignViewController.swift +// AutoLayout +// +// Created by Keith Harrison http://useyourloaf.com +// Copyright (c) 2016 Keith Harrison. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +import UIKit + +class CenterAlignViewController: UIViewController { + + var heartTop: UIImageView? + var starTop: UIImageView? + var starBottomLeft: UIImageView? + var heartBottom: UIImageView? + var starBottomRight: UIImageView? + + override func viewDidLoad() { + super.viewDidLoad() + setupViews() + } + + func setupViews() { + heartTop = addImageViewForImageNamed("Heart") + starTop = addImageViewForImageNamed("Star") + starBottomLeft = addImageViewForImageNamed("Star") + heartBottom = addImageViewForImageNamed("Heart") + starBottomRight = addImageViewForImageNamed("Star") + setupConstraints() + } + + func setupConstraints() { + addConstraintFromView(heartTop, attribute: .CenterY, multiplier: 0.667, identifier: "heartTop center Y") + addConstraintFromView(starTop, attribute: .CenterY, multiplier: 0.667, identifier: "starTop center Y") + + addConstraintFromView(heartTop, attribute: .CenterX, multiplier: 0.5, identifier: "heartTop center X") + addConstraintFromView(starTop, attribute: .CenterX, multiplier: 1.5, identifier: "starTop center X") + + + addConstraintFromView(starBottomLeft, attribute: .CenterY, multiplier: 1.333, identifier: "startBottomLeft center Y") + addConstraintFromView(heartBottom, attribute: .CenterY, multiplier: 1.333, identifier: "heartBottom center Y") + addConstraintFromView(starBottomRight, attribute: .CenterY, multiplier: 1.333, identifier: "starBottomRight center Y") + + addConstraintFromView(starBottomLeft, attribute: .CenterX, multiplier: 0.333, identifier: "starBottomLeft center X") + addConstraintFromView(heartBottom, attribute: .CenterX, multiplier: 1.0, identifier: "heartBottom center X") + addConstraintFromView(starBottomRight, attribute: .CenterX, multiplier: 1.667, identifier: "starBottomRight center X") + } +} + +extension UIViewController { + + func addImageViewForImageNamed(name: String) -> UIImageView? { + if let image = UIImage.init(named: name) { + let imageView = UIImageView.init(image: image) + imageView.translatesAutoresizingMaskIntoConstraints = false + view.addSubview(imageView) + return imageView + } + return nil + } + + func addConstraintFromView(subview: UIView?, attribute: NSLayoutAttribute, multiplier: CGFloat, identifier: String) { + if let subview = subview { + let constraint = NSLayoutConstraint.init(item: subview, + attribute: attribute, + relatedBy: .Equal, + toItem: view, + attribute: attribute, + multiplier: multiplier, + constant: 0) + constraint.identifier = identifier + view.addConstraint(constraint) + } + } +} diff --git a/AutoLayout/AutoLayout/Info.plist b/AutoLayout/AutoLayout/Info.plist new file mode 100644 index 0000000..fd1ab7c --- /dev/null +++ b/AutoLayout/AutoLayout/Info.plist @@ -0,0 +1,57 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UIStatusBarTintParameters + + UINavigationBar + + Style + UIBarStyleDefault + Translucent + + + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/AutoLayout/AutoLayout/MasterViewController.swift b/AutoLayout/AutoLayout/MasterViewController.swift new file mode 100644 index 0000000..2f247cc --- /dev/null +++ b/AutoLayout/AutoLayout/MasterViewController.swift @@ -0,0 +1,49 @@ +// +// MasterViewController.swift +// AutoLayout +// +// Created by Keith Harrison http://useyourloaf.com +// Copyright (c) 2016 Keith Harrison. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +import UIKit + +class MasterViewController: UITableViewController { + + override func viewWillAppear(animated: Bool) { + self.clearsSelectionOnViewWillAppear = self.splitViewController!.collapsed + super.viewWillAppear(animated) + } + + override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { + if let controller = (segue.destinationViewController as! UINavigationController).topViewController { + controller.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem() + controller.navigationItem.leftItemsSupplementBackButton = true + } + } +} diff --git a/AutoLayout/README b/AutoLayout/README new file mode 100644 index 0000000..e77ac28 --- /dev/null +++ b/AutoLayout/README @@ -0,0 +1,8 @@ +### AutoLayout +Version 1.0 15 Jan 2016 Initial Version + +Example of how to use center constraints to proportionally +space views along horizontal or vertical axis. + +Solution is implemented with Interface Builder, in code and +with a stack view. \ No newline at end of file diff --git a/README b/README index e878b6b..5c0366e 100644 --- a/README +++ b/README @@ -3,7 +3,7 @@ See http://useyourloaf.com for details - This README last updated: 01 Jan 2016 + This README last updated: 25 Jan 2016 ======================================================================= + INDEX @@ -13,6 +13,7 @@ AlertView - Demo iOS 5 changes to UIAlertView AlertController - UIAlertController changes in iOS 8 AnimatedConstraints - Animating Autolayout changes + AutoLayout - AutoLayout examples Collection - Simple collection view with popovers Designable - IBDESIGNABLE/IB_Inspectable custom views Encode - Percent encoding of URL query string From 37c15f662d7ff42cd69f0f1088c46a6d14933432 Mon Sep 17 00:00:00 2001 From: Keith Harrison Date: Mon, 25 Jan 2016 21:00:20 +0000 Subject: [PATCH 013/184] Remove unnecessary init --- AutoLayout/AutoLayout/CenterAlignViewController.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AutoLayout/AutoLayout/CenterAlignViewController.swift b/AutoLayout/AutoLayout/CenterAlignViewController.swift index bfb9b17..e05c0ea 100644 --- a/AutoLayout/AutoLayout/CenterAlignViewController.swift +++ b/AutoLayout/AutoLayout/CenterAlignViewController.swift @@ -76,8 +76,8 @@ class CenterAlignViewController: UIViewController { extension UIViewController { func addImageViewForImageNamed(name: String) -> UIImageView? { - if let image = UIImage.init(named: name) { - let imageView = UIImageView.init(image: image) + if let image = UIImage(named: name) { + let imageView = UIImageView(image: image) imageView.translatesAutoresizingMaskIntoConstraints = false view.addSubview(imageView) return imageView From bc0f4c4e754debebe124da0470d6e29e2ecae42b Mon Sep 17 00:00:00 2001 From: Keith Harrison Date: Mon, 25 Jan 2016 21:08:53 +0000 Subject: [PATCH 014/184] Remove unnecessary init --- AutoLayout/AutoLayout/CenterAlignViewController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AutoLayout/AutoLayout/CenterAlignViewController.swift b/AutoLayout/AutoLayout/CenterAlignViewController.swift index e05c0ea..f52c49b 100644 --- a/AutoLayout/AutoLayout/CenterAlignViewController.swift +++ b/AutoLayout/AutoLayout/CenterAlignViewController.swift @@ -87,7 +87,7 @@ extension UIViewController { func addConstraintFromView(subview: UIView?, attribute: NSLayoutAttribute, multiplier: CGFloat, identifier: String) { if let subview = subview { - let constraint = NSLayoutConstraint.init(item: subview, + let constraint = NSLayoutConstraint(item: subview, attribute: attribute, relatedBy: .Equal, toItem: view, From 3db20760174dc0464c11cd721917968f5c7d92af Mon Sep 17 00:00:00 2001 From: Keith Harrison Date: Sun, 31 Jan 2016 20:15:58 +0000 Subject: [PATCH 015/184] Use larger images for regular:regular size classes Also some refactoring of the stack view implementation to add constraints in code --- .../AutoLayout.xcodeproj/project.pbxproj | 26 +++++ .../Heart.imageset/Contents.json | 91 +++++++++++++++ .../Heart.imageset/heart120.png | Bin 0 -> 2467 bytes .../Heart.imageset/heart240.png | Bin 0 -> 5334 bytes .../Heart.imageset/heart360.png | Bin 0 -> 9138 bytes .../Star.imageset/Contents.json | 51 +++++++++ .../Assets.xcassets/Star.imageset/star120.png | Bin 0 -> 1992 bytes .../Assets.xcassets/Star.imageset/star240.png | Bin 0 -> 5111 bytes .../Assets.xcassets/Star.imageset/star360.png | Bin 0 -> 9242 bytes .../AutoLayout/Base.lproj/Main.storyboard | 38 +------ .../CenterAlignViewController.swift | 29 +---- .../AutoLayout/StackViewController.swift | 105 ++++++++++++++++++ .../AutoLayout/UIStackView+addView.swift | 63 +++++++++++ .../UIViewController+Constraint.swift | 85 ++++++++++++++ .../AutoLayout/UIViewController+addView.swift | 68 ++++++++++++ AutoLayout/README | 11 +- 16 files changed, 500 insertions(+), 67 deletions(-) create mode 100644 AutoLayout/AutoLayout/Assets.xcassets/Heart.imageset/heart120.png create mode 100644 AutoLayout/AutoLayout/Assets.xcassets/Heart.imageset/heart240.png create mode 100644 AutoLayout/AutoLayout/Assets.xcassets/Heart.imageset/heart360.png create mode 100644 AutoLayout/AutoLayout/Assets.xcassets/Star.imageset/star120.png create mode 100644 AutoLayout/AutoLayout/Assets.xcassets/Star.imageset/star240.png create mode 100644 AutoLayout/AutoLayout/Assets.xcassets/Star.imageset/star360.png create mode 100644 AutoLayout/AutoLayout/StackViewController.swift create mode 100644 AutoLayout/AutoLayout/UIStackView+addView.swift create mode 100644 AutoLayout/AutoLayout/UIViewController+Constraint.swift create mode 100644 AutoLayout/AutoLayout/UIViewController+addView.swift diff --git a/AutoLayout/AutoLayout.xcodeproj/project.pbxproj b/AutoLayout/AutoLayout.xcodeproj/project.pbxproj index 5642e4c..4e084fc 100644 --- a/AutoLayout/AutoLayout.xcodeproj/project.pbxproj +++ b/AutoLayout/AutoLayout.xcodeproj/project.pbxproj @@ -13,6 +13,10 @@ 53D6A5F81C53BF5200A81A14 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 53D6A5F61C53BF5200A81A14 /* Main.storyboard */; }; 53D6A5FA1C53BF5200A81A14 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 53D6A5F91C53BF5200A81A14 /* Assets.xcassets */; }; 53D6A5FD1C53BF5200A81A14 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 53D6A5FB1C53BF5200A81A14 /* LaunchScreen.storyboard */; }; + 53EDF4081C5D5C78009F91EA /* StackViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53EDF4071C5D5C78009F91EA /* StackViewController.swift */; }; + 53EDF40A1C5D64C1009F91EA /* UIStackView+addView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53EDF4091C5D64C0009F91EA /* UIStackView+addView.swift */; }; + 53EDF40C1C5D6641009F91EA /* UIViewController+addView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53EDF40B1C5D6641009F91EA /* UIViewController+addView.swift */; }; + 53EDF40F1C5D66AA009F91EA /* UIViewController+Constraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53EDF40E1C5D66AA009F91EA /* UIViewController+Constraint.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -24,6 +28,11 @@ 53D6A5F91C53BF5200A81A14 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 53D6A5FC1C53BF5200A81A14 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 53D6A5FE1C53BF5200A81A14 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 53EDF4071C5D5C78009F91EA /* StackViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StackViewController.swift; sourceTree = ""; }; + 53EDF4091C5D64C0009F91EA /* UIStackView+addView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIStackView+addView.swift"; sourceTree = ""; }; + 53EDF40B1C5D6641009F91EA /* UIViewController+addView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIViewController+addView.swift"; sourceTree = ""; }; + 53EDF40E1C5D66AA009F91EA /* UIViewController+Constraint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIViewController+Constraint.swift"; sourceTree = ""; }; + 53EDF4101C5D77A7009F91EA /* README */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -40,6 +49,7 @@ 53D6A5E41C53BF5200A81A14 = { isa = PBXGroup; children = ( + 53EDF4101C5D77A7009F91EA /* README */, 53D6A5EF1C53BF5200A81A14 /* AutoLayout */, 53D6A5EE1C53BF5200A81A14 /* Products */, ); @@ -56,9 +66,11 @@ 53D6A5EF1C53BF5200A81A14 /* AutoLayout */ = { isa = PBXGroup; children = ( + 53EDF40D1C5D6653009F91EA /* Extensions */, 53D6A5F01C53BF5200A81A14 /* AppDelegate.swift */, 53D6A5F21C53BF5200A81A14 /* MasterViewController.swift */, 5362E7F71C53F1B200DAA88B /* CenterAlignViewController.swift */, + 53EDF4071C5D5C78009F91EA /* StackViewController.swift */, 53D6A5F61C53BF5200A81A14 /* Main.storyboard */, 53D6A5F91C53BF5200A81A14 /* Assets.xcassets */, 53D6A5FB1C53BF5200A81A14 /* LaunchScreen.storyboard */, @@ -67,6 +79,16 @@ path = AutoLayout; sourceTree = ""; }; + 53EDF40D1C5D6653009F91EA /* Extensions */ = { + isa = PBXGroup; + children = ( + 53EDF40B1C5D6641009F91EA /* UIViewController+addView.swift */, + 53EDF4091C5D64C0009F91EA /* UIStackView+addView.swift */, + 53EDF40E1C5D66AA009F91EA /* UIViewController+Constraint.swift */, + ); + name = Extensions; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -138,7 +160,11 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 53EDF40F1C5D66AA009F91EA /* UIViewController+Constraint.swift in Sources */, + 53EDF40A1C5D64C1009F91EA /* UIStackView+addView.swift in Sources */, 53D6A5F31C53BF5200A81A14 /* MasterViewController.swift in Sources */, + 53EDF4081C5D5C78009F91EA /* StackViewController.swift in Sources */, + 53EDF40C1C5D6641009F91EA /* UIViewController+addView.swift in Sources */, 5362E7F81C53F1B200DAA88B /* CenterAlignViewController.swift in Sources */, 53D6A5F11C53BF5200A81A14 /* AppDelegate.swift in Sources */, ); diff --git a/AutoLayout/AutoLayout/Assets.xcassets/Heart.imageset/Contents.json b/AutoLayout/AutoLayout/Assets.xcassets/Heart.imageset/Contents.json index 581c8f1..e338ea6 100644 --- a/AutoLayout/AutoLayout/Assets.xcassets/Heart.imageset/Contents.json +++ b/AutoLayout/AutoLayout/Assets.xcassets/Heart.imageset/Contents.json @@ -14,6 +14,97 @@ "idiom" : "universal", "filename" : "heart300.png", "scale" : "3x" + }, + { + "idiom" : "universal", + "scale" : "1x", + "height-class" : "regular" + }, + { + "idiom" : "universal", + "scale" : "2x", + "height-class" : "regular" + }, + { + "idiom" : "universal", + "scale" : "3x", + "height-class" : "regular" + }, + { + "idiom" : "universal", + "scale" : "1x", + "width-class" : "regular" + }, + { + "idiom" : "universal", + "scale" : "2x", + "width-class" : "regular" + }, + { + "idiom" : "universal", + "scale" : "3x", + "width-class" : "regular" + }, + { + "idiom" : "universal", + "filename" : "heart120.png", + "width-class" : "regular", + "height-class" : "regular", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "heart240.png", + "width-class" : "regular", + "height-class" : "regular", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "heart360.png", + "width-class" : "regular", + "height-class" : "regular", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "height-class" : "regular" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "height-class" : "regular" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "width-class" : "regular" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "width-class" : "regular" + }, + { + "idiom" : "ipad", + "width-class" : "regular", + "height-class" : "regular", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "width-class" : "regular", + "height-class" : "regular", + "scale" : "2x" } ], "info" : { diff --git a/AutoLayout/AutoLayout/Assets.xcassets/Heart.imageset/heart120.png b/AutoLayout/AutoLayout/Assets.xcassets/Heart.imageset/heart120.png new file mode 100644 index 0000000000000000000000000000000000000000..3dd922e844692a4ef9fa7c0ee736746c69bc6a1c GIT binary patch literal 2467 zcmV;U30(GxP)?!`qJup;so_d`{^6W|Zz%lL z{IZJ^Do%~6JTkQM`0%oe6Z8MDEF)`wV&hfcl~D=p`T>oBLF3fo<3-VP>o)hYOnlKf z-~o52pC`yq3Y5kBS9hqtW8lMWGv25=I5790>l2&2qlJz4hz<}u;G)Pm^&0{$6JBuk zyWbPyyLG_#1Zuc?-(#8la`pZJSqa4)K=)K@fU(PKda5%yY%JW_yv@qPn0%R`QOfrJs7b zqyxQnfZnw1^Nh^B^t?3$8m=(0<$A@*QLaAsV|ZjcT^5WJKcs2Nr|EeIG1QLgV2es@ zzE*jBgsb;`-PXaYfV2NYc)jp~Vsx4S9cPb9sW{!V^fMUa_lMWrp*n^Oc;?eq&%#-9o z^fyu}&cxdyRR9eWgHLQ;^05dz`cX*WUwmukk9`QdXL8-e)>&`Kl*O@bUhqLu{bk%y zY0wyU<XzRN&AQ= zeG{faf(Zi|P~VL9*^5fnG0!B` zT@n*V%k$x?FHP<}F#Lw~-^Sa8)uYKxV~d3pV0s4~J6~Wun)a-u7aXDe&Zc$H7(O;W z0EIx%#JUO5bf9_78Oa17D6!q3@Yf5aAoGI8@Bu+EXo%%ja_vRym}gB`^uQh(+lVJq zs?H(+6Al`JK)vF`NE1$p7&6wFC{IxWG(1#V@o~rCM?7WlbwuH$>jkO$8 z8MXHSz@{MVy;pGCE*W*f5gcA~hnU5jv3nM~w2-GL3Yv_%^5{@0z%=H({#$YyN`pq( zJEi=TjrPtO-JG$f5>+I1K$B6I+?Xx}8b8>m-5Eh;esw_8(shalU&KRa<7Vph#JuY$9#zeG(3^k~kdmnTztqIJ-; zrwPqU?8Be*1nbN-qpcZ=WomW_>q2CW4r}Oe@!kzab!@<$3~9HWFy+uZf1ymPs#zwYroW)_#N4PS~e4ilnoA=SR)NXt7s`mS--qK z03L%Vn*uZyL(n+LHtK0lkW!R41tX#-zicAVWY=gsZX5NC6eOH7O5`}pPLxdtn(R6= zbFY0Q>{?*%hqjMY3^4HrZ-{lq9@uq{v!dtb4f0cloK& zNR%kUX3+mPH;#F>XZ&>tK;MN$Dfv4(4Ep=5v-czB^8|LcijPf~MUk`d1HVU^L4Pm4 z&OO+jp8cTg>iYn`4<*-^8T6f5=O4CAc&Q7l;gp>NAIbg4T3W_t&>g6U6dh}y(z)+p zU?R1Rcq%Jtdq-`VK@)3y!=~dkRa}NbReJXRPObYfXkzXCW>M>$ccg$0d@5zuTd5_- zyRwGOplPiDjpkbxd`PVCM7e3nC%BAG05a&Vv&Pq|8kc|m6Rbn2Em|;IOj3to2Howi z%QaqYSo^KaT28O4_Yahd7!10{zT{W8HI!I;LLmNS=f`=|>mH=cpds`b;Of2orEky; z?cSoOxdIYD22J8)U4CwS-k(OGiVkD&7c`H3xj6R hEDV}KGia9X$#(1u2=v7 literal 0 HcmV?d00001 diff --git a/AutoLayout/AutoLayout/Assets.xcassets/Heart.imageset/heart240.png b/AutoLayout/AutoLayout/Assets.xcassets/Heart.imageset/heart240.png new file mode 100644 index 0000000000000000000000000000000000000000..1ecead5250f4ac7d2b46614ea54fb100a2c1bf9b GIT binary patch literal 5334 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 literal 0 HcmV?d00001 diff --git a/AutoLayout/AutoLayout/Assets.xcassets/Star.imageset/star360.png b/AutoLayout/AutoLayout/Assets.xcassets/Star.imageset/star360.png new file mode 100644 index 0000000000000000000000000000000000000000..dc8514c87f181cbc21d0791c715d42fb7dc6e319 GIT binary patch literal 9242 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 Ilr<0kUo&qlegFUf literal 0 HcmV?d00001 diff --git a/AutoLayout/AutoLayout/Base.lproj/Main.storyboard b/AutoLayout/AutoLayout/Base.lproj/Main.storyboard index 01fbc4a..07e3a18 100644 --- a/AutoLayout/AutoLayout/Base.lproj/Main.storyboard +++ b/AutoLayout/AutoLayout/Base.lproj/Main.storyboard @@ -3,7 +3,6 @@ - @@ -209,7 +208,7 @@ - + @@ -217,42 +216,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/AutoLayout/AutoLayout/CenterAlignViewController.swift b/AutoLayout/AutoLayout/CenterAlignViewController.swift index f52c49b..e164024 100644 --- a/AutoLayout/AutoLayout/CenterAlignViewController.swift +++ b/AutoLayout/AutoLayout/CenterAlignViewController.swift @@ -71,31 +71,4 @@ class CenterAlignViewController: UIViewController { addConstraintFromView(heartBottom, attribute: .CenterX, multiplier: 1.0, identifier: "heartBottom center X") addConstraintFromView(starBottomRight, attribute: .CenterX, multiplier: 1.667, identifier: "starBottomRight center X") } -} - -extension UIViewController { - - func addImageViewForImageNamed(name: String) -> UIImageView? { - if let image = UIImage(named: name) { - let imageView = UIImageView(image: image) - imageView.translatesAutoresizingMaskIntoConstraints = false - view.addSubview(imageView) - return imageView - } - return nil - } - - func addConstraintFromView(subview: UIView?, attribute: NSLayoutAttribute, multiplier: CGFloat, identifier: String) { - if let subview = subview { - let constraint = NSLayoutConstraint(item: subview, - attribute: attribute, - relatedBy: .Equal, - toItem: view, - attribute: attribute, - multiplier: multiplier, - constant: 0) - constraint.identifier = identifier - view.addConstraint(constraint) - } - } -} +} \ No newline at end of file diff --git a/AutoLayout/AutoLayout/StackViewController.swift b/AutoLayout/AutoLayout/StackViewController.swift new file mode 100644 index 0000000..9988033 --- /dev/null +++ b/AutoLayout/AutoLayout/StackViewController.swift @@ -0,0 +1,105 @@ +// +// StackViewController.swift +// AutoLayout +// +// Created by Keith Harrison http://useyourloaf.com +// Copyright (c) 2016 Keith Harrison. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +import UIKit + +class StackViewController: UIViewController { + + var heartTop: UIImageView? + var starTop: UIImageView? + var starBottomLeft: UIImageView? + var heartBottom: UIImageView? + var starBottomRight: UIImageView? + + let topStackView = UIStackView() + let bottomStackView = UIStackView() + + override func viewDidLoad() { + super.viewDidLoad() + setupViews() + } + + func setupViews() { + + topStackView.translatesAutoresizingMaskIntoConstraints = false + topStackView.distribution = .EqualSpacing + view.addSubview(topStackView) + + bottomStackView.translatesAutoresizingMaskIntoConstraints = false + bottomStackView.distribution = .EqualSpacing + view.addSubview(bottomStackView) + + heartTop = topStackView.addImageViewForImage("Heart") + starTop = topStackView.addImageViewForImage("Star") + + starBottomLeft = bottomStackView.addImageViewForImage("Star") + heartBottom = bottomStackView.addImageViewForImage("Heart") + starBottomRight = bottomStackView.addImageViewForImage("Star") + + // Set the vertical position of each stack view + addConstraintFromView(topStackView, attribute: .CenterY, multiplier: 0.667, identifier: "topSV center Y") + addConstraintFromView(bottomStackView, attribute: .CenterY, multiplier: 1.333, identifier: "bottomSV center Y") + + // Center both stack views + addConstraintFromView(topStackView, attribute: .CenterX, multiplier: 1.0, identifier: "topSv center X") + addConstraintFromView(bottomStackView, attribute: .CenterX, multiplier: 1.0, identifier: "bottomSV center X") + + // Set the widths of each stack view by constraining + // the leading edge + if let heartTop = heartTop { + let offset = heartTop.bounds.width/2 + let constraint = NSLayoutConstraint(item: topStackView, + attribute: .Leading, + relatedBy: .Equal, + toItem: view, + attribute: .CenterXWithinMargins, + multiplier: 0.5, + constant: -offset) + constraint.identifier = "TopStackLeading" + view.addConstraint(constraint) + } + + if let starBottomLeft = starBottomLeft { + let offset = starBottomLeft.bounds.width/2 + let constraint = NSLayoutConstraint(item: bottomStackView, + attribute: .Leading, + relatedBy: .Equal, + toItem: view, + attribute: .CenterXWithinMargins, + multiplier: 0.333, + constant: -offset) + constraint.identifier = "BottomStackLeading" + view.addConstraint(constraint) + } + } +} \ No newline at end of file diff --git a/AutoLayout/AutoLayout/UIStackView+addView.swift b/AutoLayout/AutoLayout/UIStackView+addView.swift new file mode 100644 index 0000000..e7815c9 --- /dev/null +++ b/AutoLayout/AutoLayout/UIStackView+addView.swift @@ -0,0 +1,63 @@ +// +// UIStackView+addImage.swift +// AutoLayout +// +// Created by Keith Harrison http://useyourloaf.com +// Copyright (c) 2016 Keith Harrison. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +import UIKit + +/** + A UIStackView extension for adding views to a stack view + */ + +extension UIStackView { + + /** + A convenience method to create a UIImageView and then + add it to the stack view. The UIImageView is created + from a UIImage using the name parameter. + + - Parameter name: A String which should match the name + of a valid image file in the application bundle or asset + catalog. + + - Returns: The newly created UIImageView or nil. + */ + + func addImageViewForImage(name: String) -> UIImageView? { + if let image = UIImage(named: name) { + let imageView = UIImageView(image: image) + imageView.translatesAutoresizingMaskIntoConstraints = false + addArrangedSubview(imageView) + return imageView + } + return nil + } +} diff --git a/AutoLayout/AutoLayout/UIViewController+Constraint.swift b/AutoLayout/AutoLayout/UIViewController+Constraint.swift new file mode 100644 index 0000000..1ed1330 --- /dev/null +++ b/AutoLayout/AutoLayout/UIViewController+Constraint.swift @@ -0,0 +1,85 @@ +// +// UIViewController+Constraint.swift +// AutoLayout +// +// Created by Keith Harrison http://useyourloaf.com +// Copyright (c) 2016 Keith Harrison. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +import UIKit + +/** + A UIViewController extension for adding constraints to the + view controller view. + */ + +extension UIViewController { + + /** + A convenience method to create and add a constraint + between a subview and the view of the view controller. + This method assumes the constraint uses the same + attribute for both views. + + For example, to horizontally center a view at + half way to the center of the view controller view: + + addConstraintFromView(myView, + attribute: .CenterX, + multiplier: 0.5, + identifier: "myView center X") + + - Note: This method does not add the subview to the + view hierarchy of the view controller. + + - Parameter subview: The subview of the view controller + from which to add the constraint. + + - Parameter attribute: The NSLayoutAttribute for both + the from and to item. + + - Parameter multipler: A CGFloat constant that multiplies + the attribute of the to item. + + - Parameter identifier: A String identifier to aid debugging. + */ + + func addConstraintFromView(subview: UIView?, attribute: NSLayoutAttribute, multiplier: CGFloat, identifier: String) { + if let subview = subview { + let constraint = NSLayoutConstraint(item: subview, + attribute: attribute, + relatedBy: .Equal, + toItem: view, + attribute: attribute, + multiplier: multiplier, + constant: 0) + constraint.identifier = identifier + view.addConstraint(constraint) + } + } +} diff --git a/AutoLayout/AutoLayout/UIViewController+addView.swift b/AutoLayout/AutoLayout/UIViewController+addView.swift new file mode 100644 index 0000000..cd8a17f --- /dev/null +++ b/AutoLayout/AutoLayout/UIViewController+addView.swift @@ -0,0 +1,68 @@ +// +// UIViewController+addView.swift +// AutoLayout +// +// Created by Keith Harrison http://useyourloaf.com +// Copyright (c) 2016 Keith Harrison. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +import UIKit + +/** + A UIViewController extension for adding views to a view controller + */ + +extension UIViewController { + + /** + A convenience method to create a UIImageView and then + add it to the view controller view. The UIImageView is + created from a UIImage using the name parameter. + + - Note: The translatesAutoresizingMaskIntoConstraints + flag is set to false for the UIImageView. Make sure you + add sufficient constraint to fix the position. + + - Parameter name: A String which should match the name + of a valid image file in the application bundle or asset + catalog. + + - Returns: The newly created UIImageView or nil. + */ + + func addImageViewForImageNamed(name: String) -> UIImageView? { + if let image = UIImage(named: name) { + let imageView = UIImageView(image: image) + imageView.translatesAutoresizingMaskIntoConstraints = false + view.addSubview(imageView) + return imageView + } + return nil + } +} + diff --git a/AutoLayout/README b/AutoLayout/README index e77ac28..7261693 100644 --- a/AutoLayout/README +++ b/AutoLayout/README @@ -1,8 +1,15 @@ -### AutoLayout +AutoLayout +========== + +Version 1.1 31 Jan 2016 Custom Asset catalog for size classes. Version 1.0 15 Jan 2016 Initial Version Example of how to use center constraints to proportionally space views along horizontal or vertical axis. Solution is implemented with Interface Builder, in code and -with a stack view. \ No newline at end of file +with a stack view. + +See this post for further details: + +http://useyourloaf.com/blog/proportional-spacing-with-auto-layout.html \ No newline at end of file From b27e63bc702db2205ee19a6bbccabcd1203e6353 Mon Sep 17 00:00:00 2001 From: Keith Harrison Date: Sun, 31 Jan 2016 20:30:12 +0000 Subject: [PATCH 016/184] Fix the width of the stack view by adding a constraint for the center of the left image. This has the benefit of not requiring knowledge of the width of the image. --- .../AutoLayout/StackViewController.swift | 31 +++---------------- 1 file changed, 4 insertions(+), 27 deletions(-) diff --git a/AutoLayout/AutoLayout/StackViewController.swift b/AutoLayout/AutoLayout/StackViewController.swift index 9988033..4f2e01c 100644 --- a/AutoLayout/AutoLayout/StackViewController.swift +++ b/AutoLayout/AutoLayout/StackViewController.swift @@ -73,33 +73,10 @@ class StackViewController: UIViewController { // Center both stack views addConstraintFromView(topStackView, attribute: .CenterX, multiplier: 1.0, identifier: "topSv center X") addConstraintFromView(bottomStackView, attribute: .CenterX, multiplier: 1.0, identifier: "bottomSV center X") - - // Set the widths of each stack view by constraining - // the leading edge - if let heartTop = heartTop { - let offset = heartTop.bounds.width/2 - let constraint = NSLayoutConstraint(item: topStackView, - attribute: .Leading, - relatedBy: .Equal, - toItem: view, - attribute: .CenterXWithinMargins, - multiplier: 0.5, - constant: -offset) - constraint.identifier = "TopStackLeading" - view.addConstraint(constraint) - } - if let starBottomLeft = starBottomLeft { - let offset = starBottomLeft.bounds.width/2 - let constraint = NSLayoutConstraint(item: bottomStackView, - attribute: .Leading, - relatedBy: .Equal, - toItem: view, - attribute: .CenterXWithinMargins, - multiplier: 0.333, - constant: -offset) - constraint.identifier = "BottomStackLeading" - view.addConstraint(constraint) - } + // Fix the width of the stack view by setting the center + // of the left image. + addConstraintFromView(heartTop, attribute: .CenterX, multiplier: 0.5, identifier: "heartTop center X") + addConstraintFromView(starBottomLeft, attribute: .CenterX, multiplier: 0.333, identifier: "starBottomLeft center X") } } \ No newline at end of file From 48a98ffcdcd01fd0ec45262cfa0496032594a495 Mon Sep 17 00:00:00 2001 From: Keith Harrison Date: Mon, 22 Feb 2016 11:31:56 +0000 Subject: [PATCH 017/184] Add example of layout anchors --- .../AutoLayout.xcodeproj/project.pbxproj | 4 + .../Base.lproj/LaunchScreen.storyboard | 50 +++++++- .../AutoLayout/Base.lproj/Main.storyboard | 64 +++++++++- .../AutoLayout/LayoutAnchorController.swift | 118 ++++++++++++++++++ AutoLayout/README | 18 +-- 5 files changed, 237 insertions(+), 17 deletions(-) create mode 100644 AutoLayout/AutoLayout/LayoutAnchorController.swift diff --git a/AutoLayout/AutoLayout.xcodeproj/project.pbxproj b/AutoLayout/AutoLayout.xcodeproj/project.pbxproj index 4e084fc..5f6ff42 100644 --- a/AutoLayout/AutoLayout.xcodeproj/project.pbxproj +++ b/AutoLayout/AutoLayout.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 5329F1161C79008300A35954 /* LayoutAnchorController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5329F1151C79008300A35954 /* LayoutAnchorController.swift */; }; 5362E7F81C53F1B200DAA88B /* CenterAlignViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5362E7F71C53F1B200DAA88B /* CenterAlignViewController.swift */; }; 53D6A5F11C53BF5200A81A14 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53D6A5F01C53BF5200A81A14 /* AppDelegate.swift */; }; 53D6A5F31C53BF5200A81A14 /* MasterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53D6A5F21C53BF5200A81A14 /* MasterViewController.swift */; }; @@ -20,6 +21,7 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 5329F1151C79008300A35954 /* LayoutAnchorController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LayoutAnchorController.swift; sourceTree = ""; }; 5362E7F71C53F1B200DAA88B /* CenterAlignViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = CenterAlignViewController.swift; sourceTree = ""; tabWidth = 4; }; 53D6A5ED1C53BF5200A81A14 /* AutoLayout.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AutoLayout.app; sourceTree = BUILT_PRODUCTS_DIR; }; 53D6A5F01C53BF5200A81A14 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -70,6 +72,7 @@ 53D6A5F01C53BF5200A81A14 /* AppDelegate.swift */, 53D6A5F21C53BF5200A81A14 /* MasterViewController.swift */, 5362E7F71C53F1B200DAA88B /* CenterAlignViewController.swift */, + 5329F1151C79008300A35954 /* LayoutAnchorController.swift */, 53EDF4071C5D5C78009F91EA /* StackViewController.swift */, 53D6A5F61C53BF5200A81A14 /* Main.storyboard */, 53D6A5F91C53BF5200A81A14 /* Assets.xcassets */, @@ -164,6 +167,7 @@ 53EDF40A1C5D64C1009F91EA /* UIStackView+addView.swift in Sources */, 53D6A5F31C53BF5200A81A14 /* MasterViewController.swift in Sources */, 53EDF4081C5D5C78009F91EA /* StackViewController.swift in Sources */, + 5329F1161C79008300A35954 /* LayoutAnchorController.swift in Sources */, 53EDF40C1C5D6641009F91EA /* UIViewController+addView.swift in Sources */, 5362E7F81C53F1B200DAA88B /* CenterAlignViewController.swift in Sources */, 53D6A5F11C53BF5200A81A14 /* AppDelegate.swift in Sources */, diff --git a/AutoLayout/AutoLayout/Base.lproj/LaunchScreen.storyboard b/AutoLayout/AutoLayout/Base.lproj/LaunchScreen.storyboard index 2702a55..1fb311e 100644 --- a/AutoLayout/AutoLayout/Base.lproj/LaunchScreen.storyboard +++ b/AutoLayout/AutoLayout/Base.lproj/LaunchScreen.storyboard @@ -1,21 +1,59 @@ - + - + - + - + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -23,7 +61,7 @@ - + diff --git a/AutoLayout/AutoLayout/Base.lproj/Main.storyboard b/AutoLayout/AutoLayout/Base.lproj/Main.storyboard index 07e3a18..072a09c 100644 --- a/AutoLayout/AutoLayout/Base.lproj/Main.storyboard +++ b/AutoLayout/AutoLayout/Base.lproj/Main.storyboard @@ -1,8 +1,8 @@ - + - + @@ -108,6 +108,27 @@ + + + + + + + + + + + + + + + @@ -120,7 +141,7 @@ - + @@ -240,6 +261,25 @@ + + + + + + + + + + + + + + + + + + + @@ -256,6 +296,24 @@ + + + + + + + + + + + + + + + + + + diff --git a/AutoLayout/AutoLayout/LayoutAnchorController.swift b/AutoLayout/AutoLayout/LayoutAnchorController.swift new file mode 100644 index 0000000..9ad2bea --- /dev/null +++ b/AutoLayout/AutoLayout/LayoutAnchorController.swift @@ -0,0 +1,118 @@ +// +// LayoutAnchorController.swift +// AutoLayout +// +// Created by Keith Harrison http://useyourloaf.com +// Copyright (c) 2016 Keith Harrison. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +import UIKit + +class LayoutAnchorController: UIViewController { + + let stackView = UIStackView() + + override func viewDidLoad() { + super.viewDidLoad() + setupViews() + } + + func setupViews() { + + stackView.translatesAutoresizingMaskIntoConstraints = false + stackView.distribution = .EqualSpacing + view.addSubview(stackView) + + stackView.addImageViewForImage("Heart") + stackView.addImageViewForImage("Star") + stackView.addImageViewForImage("Heart") + +// Constraints to pin the stack view to the leading and trailing +// margins of the superview and below the top layout guide. + +// The code below shows three different ways to achieve this: + +// 1. Using the NSLayoutConstraint class method to create +// each individual constraint. The most verbose of the +// three approaches. +// +// NSLayoutConstraint(item: stackView, +// attribute: .Leading, +// relatedBy: .Equal, +// toItem: view, +// attribute: .LeadingMargin, +// multiplier: 1, +// constant: 0).active = true +// +// NSLayoutConstraint(item: stackView, +// attribute: .Trailing, +// relatedBy: .Equal, +// toItem: view, +// attribute: .TrailingMargin, +// multiplier: 1, +// constant: 0).active = true +// +// NSLayoutConstraint(item: stackView, +// attribute: .Top, +// relatedBy: .Equal, +// toItem: topLayoutGuide, +// attribute: .Bottom, +// multiplier: 1, +// constant: 8.0).active = true + +// 2. Using the Visual Format Language to add both constraints +// in one go. Shorter but still not very readable. + +// let views: [String: AnyObject] = +// ["stackView" : stackView, +// "topLayoutGuide" : topLayoutGuide] +// +// let h = NSLayoutConstraint.constraintsWithVisualFormat( +// "|-[stackView]-|", +// options: [], +// metrics: nil, +// views: views) +// NSLayoutConstraint.activateConstraints(h) +// +// let v = NSLayoutConstraint.constraintsWithVisualFormat( +// "V:|[topLayoutGuide]-[stackView]", +// options: [], +// metrics: nil, +// views: views) +// NSLayoutConstraint.activateConstraints(v) + +// 3. Using layout anchors - by far the easiest method but requires +// iOS 9. + + let margins = view.layoutMarginsGuide + + stackView.leadingAnchor.constraintEqualToAnchor(margins.leadingAnchor).active = true + stackView.trailingAnchor.constraintEqualToAnchor(margins.trailingAnchor).active = true + stackView.topAnchor.constraintEqualToAnchor(topLayoutGuide.bottomAnchor, constant: 8.0).active = true + } +} diff --git a/AutoLayout/README b/AutoLayout/README index 7261693..fd2a564 100644 --- a/AutoLayout/README +++ b/AutoLayout/README @@ -1,15 +1,17 @@ -AutoLayout -========== +### AutoLayout +Version 1.2 22 Feb 2016 Example of using layout anchors Version 1.1 31 Jan 2016 Custom Asset catalog for size classes. Version 1.0 15 Jan 2016 Initial Version -Example of how to use center constraints to proportionally -space views along horizontal or vertical axis. +Auto Layout examples using Interface Builder and in code. -Solution is implemented with Interface Builder, in code and -with a stack view. ++ Proportional spacing using Interface Builder ++ Proportional spacing using code ++ Proportional spacing using a stack view ++ Layout Anchors -See this post for further details: +See the following posts further details: -http://useyourloaf.com/blog/proportional-spacing-with-auto-layout.html \ No newline at end of file ++ [Proportional Spacing with Auto Layout](http://useyourloaf.com/blog/proportional-spacing-with-auto-layout/) ++ [Adapting Images for Size Classes](http://useyourloaf.com/blog/adapting-images-for-size-classes/) \ No newline at end of file From 450b19388d095a6ae769f18da489678b1e293e1b Mon Sep 17 00:00:00 2001 From: Keith Harrison Date: Mon, 22 Feb 2016 11:41:18 +0000 Subject: [PATCH 018/184] use markdown for README --- AutoLayout/AutoLayout.xcodeproj/project.pbxproj | 4 ++-- AutoLayout/{README => README.md} | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename AutoLayout/{README => README.md} (100%) diff --git a/AutoLayout/AutoLayout.xcodeproj/project.pbxproj b/AutoLayout/AutoLayout.xcodeproj/project.pbxproj index 5f6ff42..e081145 100644 --- a/AutoLayout/AutoLayout.xcodeproj/project.pbxproj +++ b/AutoLayout/AutoLayout.xcodeproj/project.pbxproj @@ -34,7 +34,7 @@ 53EDF4091C5D64C0009F91EA /* UIStackView+addView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIStackView+addView.swift"; sourceTree = ""; }; 53EDF40B1C5D6641009F91EA /* UIViewController+addView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIViewController+addView.swift"; sourceTree = ""; }; 53EDF40E1C5D66AA009F91EA /* UIViewController+Constraint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIViewController+Constraint.swift"; sourceTree = ""; }; - 53EDF4101C5D77A7009F91EA /* README */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README; sourceTree = ""; }; + 53EDF4101C5D77A7009F91EA /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -51,7 +51,7 @@ 53D6A5E41C53BF5200A81A14 = { isa = PBXGroup; children = ( - 53EDF4101C5D77A7009F91EA /* README */, + 53EDF4101C5D77A7009F91EA /* README.md */, 53D6A5EF1C53BF5200A81A14 /* AutoLayout */, 53D6A5EE1C53BF5200A81A14 /* Products */, ); diff --git a/AutoLayout/README b/AutoLayout/README.md similarity index 100% rename from AutoLayout/README rename to AutoLayout/README.md From 98ef21bc50729e3c8b49280470976e32f4b0ff74 Mon Sep 17 00:00:00 2001 From: Keith Harrison Date: Mon, 22 Feb 2016 11:44:06 +0000 Subject: [PATCH 019/184] markdown formatting --- AutoLayout/README.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/AutoLayout/README.md b/AutoLayout/README.md index fd2a564..6b2680b 100644 --- a/AutoLayout/README.md +++ b/AutoLayout/README.md @@ -1,9 +1,5 @@ ### AutoLayout -Version 1.2 22 Feb 2016 Example of using layout anchors -Version 1.1 31 Jan 2016 Custom Asset catalog for size classes. -Version 1.0 15 Jan 2016 Initial Version - Auto Layout examples using Interface Builder and in code. + Proportional spacing using Interface Builder @@ -14,4 +10,10 @@ Auto Layout examples using Interface Builder and in code. See the following posts further details: + [Proportional Spacing with Auto Layout](http://useyourloaf.com/blog/proportional-spacing-with-auto-layout/) -+ [Adapting Images for Size Classes](http://useyourloaf.com/blog/adapting-images-for-size-classes/) \ No newline at end of file ++ [Adapting Images for Size Classes](http://useyourloaf.com/blog/adapting-images-for-size-classes/) + +#### Version History + ++ Version 1.2 22 Feb 2016 Example of using layout anchors ++ Version 1.1 31 Jan 2016 Custom Asset catalog for size classes. ++ Version 1.0 15 Jan 2016 Initial Version \ No newline at end of file From 7cbc84b7f08618ee6c8eafe515eef63e19630612 Mon Sep 17 00:00:00 2001 From: Keith Harrison Date: Sat, 27 Feb 2016 20:27:29 +0000 Subject: [PATCH 020/184] Add Layout Guide example --- .../AutoLayout.xcodeproj/project.pbxproj | 6 + .../Assets.xcassets/Buttons/Contents.json | 6 + .../greenButton.imageset/Contents.json | 23 +++ .../Buttons/greenButton.imageset/green.pdf | Bin 0 -> 12238 bytes .../Buttons/redButton.imageset/Contents.json | 23 +++ .../Buttons/redButton.imageset/red.pdf | Bin 0 -> 12248 bytes .../AutoLayout/Base.lproj/Main.storyboard | 70 +++++++++- .../AutoLayout/LayoutGuideController.swift | 131 ++++++++++++++++++ AutoLayout/README.md | 3 + 9 files changed, 256 insertions(+), 6 deletions(-) create mode 100644 AutoLayout/AutoLayout/Assets.xcassets/Buttons/Contents.json create mode 100644 AutoLayout/AutoLayout/Assets.xcassets/Buttons/greenButton.imageset/Contents.json create mode 100644 AutoLayout/AutoLayout/Assets.xcassets/Buttons/greenButton.imageset/green.pdf create mode 100644 AutoLayout/AutoLayout/Assets.xcassets/Buttons/redButton.imageset/Contents.json create mode 100644 AutoLayout/AutoLayout/Assets.xcassets/Buttons/redButton.imageset/red.pdf create mode 100644 AutoLayout/AutoLayout/LayoutGuideController.swift diff --git a/AutoLayout/AutoLayout.xcodeproj/project.pbxproj b/AutoLayout/AutoLayout.xcodeproj/project.pbxproj index e081145..464d35e 100644 --- a/AutoLayout/AutoLayout.xcodeproj/project.pbxproj +++ b/AutoLayout/AutoLayout.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ 5329F1161C79008300A35954 /* LayoutAnchorController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5329F1151C79008300A35954 /* LayoutAnchorController.swift */; }; + 53358D6F1C80F0B100C1842A /* LayoutGuideController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53358D6E1C80F0B100C1842A /* LayoutGuideController.swift */; }; 5362E7F81C53F1B200DAA88B /* CenterAlignViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5362E7F71C53F1B200DAA88B /* CenterAlignViewController.swift */; }; 53D6A5F11C53BF5200A81A14 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53D6A5F01C53BF5200A81A14 /* AppDelegate.swift */; }; 53D6A5F31C53BF5200A81A14 /* MasterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53D6A5F21C53BF5200A81A14 /* MasterViewController.swift */; }; @@ -22,6 +23,7 @@ /* Begin PBXFileReference section */ 5329F1151C79008300A35954 /* LayoutAnchorController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LayoutAnchorController.swift; sourceTree = ""; }; + 53358D6E1C80F0B100C1842A /* LayoutGuideController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LayoutGuideController.swift; sourceTree = ""; }; 5362E7F71C53F1B200DAA88B /* CenterAlignViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = CenterAlignViewController.swift; sourceTree = ""; tabWidth = 4; }; 53D6A5ED1C53BF5200A81A14 /* AutoLayout.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AutoLayout.app; sourceTree = BUILT_PRODUCTS_DIR; }; 53D6A5F01C53BF5200A81A14 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -55,7 +57,9 @@ 53D6A5EF1C53BF5200A81A14 /* AutoLayout */, 53D6A5EE1C53BF5200A81A14 /* Products */, ); + indentWidth = 4; sourceTree = ""; + tabWidth = 4; }; 53D6A5EE1C53BF5200A81A14 /* Products */ = { isa = PBXGroup; @@ -73,6 +77,7 @@ 53D6A5F21C53BF5200A81A14 /* MasterViewController.swift */, 5362E7F71C53F1B200DAA88B /* CenterAlignViewController.swift */, 5329F1151C79008300A35954 /* LayoutAnchorController.swift */, + 53358D6E1C80F0B100C1842A /* LayoutGuideController.swift */, 53EDF4071C5D5C78009F91EA /* StackViewController.swift */, 53D6A5F61C53BF5200A81A14 /* Main.storyboard */, 53D6A5F91C53BF5200A81A14 /* Assets.xcassets */, @@ -166,6 +171,7 @@ 53EDF40F1C5D66AA009F91EA /* UIViewController+Constraint.swift in Sources */, 53EDF40A1C5D64C1009F91EA /* UIStackView+addView.swift in Sources */, 53D6A5F31C53BF5200A81A14 /* MasterViewController.swift in Sources */, + 53358D6F1C80F0B100C1842A /* LayoutGuideController.swift in Sources */, 53EDF4081C5D5C78009F91EA /* StackViewController.swift in Sources */, 5329F1161C79008300A35954 /* LayoutAnchorController.swift in Sources */, 53EDF40C1C5D6641009F91EA /* UIViewController+addView.swift in Sources */, diff --git a/AutoLayout/AutoLayout/Assets.xcassets/Buttons/Contents.json b/AutoLayout/AutoLayout/Assets.xcassets/Buttons/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/AutoLayout/AutoLayout/Assets.xcassets/Buttons/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/AutoLayout/AutoLayout/Assets.xcassets/Buttons/greenButton.imageset/Contents.json b/AutoLayout/AutoLayout/Assets.xcassets/Buttons/greenButton.imageset/Contents.json new file mode 100644 index 0000000..30dee8d --- /dev/null +++ b/AutoLayout/AutoLayout/Assets.xcassets/Buttons/greenButton.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "green.pdf", + "resizing" : { + "mode" : "3-part-horizontal", + "center" : { + "mode" : "stretch", + "width" : 1 + }, + "cap-insets" : { + "right" : 20, + "left" : 20 + } + } + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/AutoLayout/AutoLayout/Assets.xcassets/Buttons/greenButton.imageset/green.pdf b/AutoLayout/AutoLayout/Assets.xcassets/Buttons/greenButton.imageset/green.pdf new file mode 100644 index 0000000000000000000000000000000000000000..b3fa12c24fd4dd1bf4fdc3bbfbe0807c05b03373 GIT binary patch literal 12238 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ð5<+&o`K4sm0qB5^p38KoN&T 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 LEGVd=tqT4hi~a(0 literal 0 HcmV?d00001 diff --git a/AutoLayout/AutoLayout/Base.lproj/Main.storyboard b/AutoLayout/AutoLayout/Base.lproj/Main.storyboard index 072a09c..c689bb2 100644 --- a/AutoLayout/AutoLayout/Base.lproj/Main.storyboard +++ b/AutoLayout/AutoLayout/Base.lproj/Main.storyboard @@ -19,7 +19,7 @@ - + @@ -32,7 +32,7 @@ - + @@ -129,6 +129,27 @@ + + + + + + + + + + + + + + + @@ -137,11 +158,11 @@ - + - + @@ -278,7 +299,7 @@ - + @@ -312,7 +333,44 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AutoLayout/AutoLayout/LayoutGuideController.swift b/AutoLayout/AutoLayout/LayoutGuideController.swift new file mode 100644 index 0000000..28ce349 --- /dev/null +++ b/AutoLayout/AutoLayout/LayoutGuideController.swift @@ -0,0 +1,131 @@ +// +// LayoutGuideController.swift +// AutoLayout +// +// Created by Keith Harrison http://useyourloaf.com +// Copyright (c) 2016 Keith Harrison. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +import UIKit + +class LayoutGuideController: UIViewController { + + // Use layout guides as an alternative to spacer views + // The two buttons will be placed between the view margins + // with equal spacing between the buttons and the margins + // so that the width of the three guides is equal. + + // = |+++++++++++************+++++++++++************+++++++++++| = + // = |+ leading +* no *+ middle +* yes *+ trailing+| = + // = |+ guide +* button *+ guide +* button *+ guide +| = + // = |+++++++++++************+++++++++++************+++++++++++| = + + let leadingGuide = UILayoutGuide() + let noButton = UIButton(type: .Custom) + let middleGuide = UILayoutGuide() + let yesButton = UIButton(type: .Custom) + let trailingGuide = UILayoutGuide() + + override func viewDidLoad() { + super.viewDidLoad() + setupViews() + setupConstraints() + } + + private func setupViews() { + + // Configure the buttons and add them to the superview + + noButton.translatesAutoresizingMaskIntoConstraints = false + noButton.setTitle("No", forState: .Normal) + let redImage = UIImage(named: "redButton") + noButton.setBackgroundImage(redImage, forState: .Normal) + noButton.contentEdgeInsets = UIEdgeInsetsMake(8, 16, 8, 16) + noButton.addTarget(self, action: "noThanks:", forControlEvents: .TouchUpInside) + + yesButton.translatesAutoresizingMaskIntoConstraints = false + yesButton.setTitle("Yes please!", forState: .Normal) + let greenImage = UIImage(named: "greenButton") + yesButton.setBackgroundImage(greenImage, forState: .Normal) + yesButton.contentEdgeInsets = UIEdgeInsetsMake(8, 16, 8, 16) + yesButton.addTarget(self, action: "yesPlease:", forControlEvents: .TouchUpInside) + + view.addSubview(noButton) + view.addSubview(yesButton) + + // Add the layout guides to the view + // Note that the guides are not part of the + // view hierarchy + + view.addLayoutGuide(leadingGuide) + view.addLayoutGuide(middleGuide) + view.addLayoutGuide(trailingGuide) + } + + private func setupConstraints() { + + // The views are spaced relative to the margings of + // the superview. To space the views relative to the + // edges of the super view replace "margings" with + // "view" in the constraints below + + let margins = view.layoutMarginsGuide + + // leading to trailing constraints + // working from left to right + + margins.leadingAnchor.constraintEqualToAnchor(leadingGuide.leadingAnchor).active = true + leadingGuide.trailingAnchor.constraintEqualToAnchor(noButton.leadingAnchor).active = true + noButton.trailingAnchor.constraintEqualToAnchor(middleGuide.leadingAnchor).active = true + middleGuide.trailingAnchor.constraintEqualToAnchor(yesButton.leadingAnchor).active = true + yesButton.trailingAnchor.constraintEqualToAnchor(trailingGuide.leadingAnchor).active = true + trailingGuide.trailingAnchor.constraintEqualToAnchor(margins.trailingAnchor).active = true + + // The buttons should have the same width + noButton.widthAnchor.constraintEqualToAnchor(yesButton.widthAnchor).active = true + + // The guides should have the same width + leadingGuide.widthAnchor.constraintEqualToAnchor(middleGuide.widthAnchor).active = true + leadingGuide.widthAnchor.constraintEqualToAnchor(trailingGuide.widthAnchor).active = true + + // Center everything vertically in the super view + leadingGuide.centerYAnchor.constraintEqualToAnchor(view.centerYAnchor).active = true + middleGuide.centerYAnchor.constraintEqualToAnchor(view.centerYAnchor).active = true + trailingGuide.centerYAnchor.constraintEqualToAnchor(view.centerYAnchor).active = true + noButton.centerYAnchor.constraintEqualToAnchor(view.centerYAnchor).active = true + yesButton.centerYAnchor.constraintEqualToAnchor(view.centerYAnchor).active = true + } + + func noThanks(sender: UIButton) { + print("No thanks!") + } + + func yesPlease(sender: UIButton) { + print("Yes please!") + } +} diff --git a/AutoLayout/README.md b/AutoLayout/README.md index 6b2680b..453c261 100644 --- a/AutoLayout/README.md +++ b/AutoLayout/README.md @@ -6,14 +6,17 @@ Auto Layout examples using Interface Builder and in code. + Proportional spacing using code + Proportional spacing using a stack view + Layout Anchors ++ Layout Guides as spacer views See the following posts further details: + [Proportional Spacing with Auto Layout](http://useyourloaf.com/blog/proportional-spacing-with-auto-layout/) + [Adapting Images for Size Classes](http://useyourloaf.com/blog/adapting-images-for-size-classes/) ++ [Pain Free Constraints with Layout Anchors](http://useyourloaf.com/blog/pain-free-constraints-with-layout-anchors/) #### Version History ++ Version 1.3 27 Feb 2016 Using layout guides to space views + Version 1.2 22 Feb 2016 Example of using layout anchors + Version 1.1 31 Jan 2016 Custom Asset catalog for size classes. + Version 1.0 15 Jan 2016 Initial Version \ No newline at end of file From 41cc7f5937d6549ea78e5a1d61245c375455eb67 Mon Sep 17 00:00:00 2001 From: Keith Harrison Date: Mon, 7 Mar 2016 20:48:39 +0000 Subject: [PATCH 021/184] Add Buttons project --- Buttons/Buttons.xcodeproj/project.pbxproj | 294 ++++++++++++++++++ Buttons/Buttons/AppDelegate.swift | 41 +++ .../AppIcon.appiconset/Contents.json | 73 +++++ .../Button Images - No Slicing/Contents.json | 6 + .../unslicedBorder.imageset/Contents.json | 12 + .../unslicedBorder.imageset/border.pdf | Bin 0 -> 4065 bytes .../unslicedFill.imageset/Contents.json | 12 + .../unslicedFill.imageset/filled.pdf | Bin 0 -> 4099 bytes .../Button Templates - Sliced/Contents.json | 6 + .../Contents.json | 29 ++ .../slicedBorderTemplate.imageset/border.pdf | Bin 0 -> 4065 bytes .../slicedFillTemplate.imageset/Contents.json | 29 ++ .../slicedFillTemplate.imageset/filled.pdf | Bin 0 -> 4099 bytes Buttons/Buttons/Assets.xcassets/Contents.json | 6 + .../Base.lproj/LaunchScreen.storyboard | 27 ++ Buttons/Buttons/Base.lproj/Main.storyboard | 75 +++++ Buttons/Buttons/Info.plist | 47 +++ Buttons/Buttons/ViewController.swift | 68 ++++ 18 files changed, 725 insertions(+) create mode 100644 Buttons/Buttons.xcodeproj/project.pbxproj create mode 100644 Buttons/Buttons/AppDelegate.swift create mode 100644 Buttons/Buttons/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 Buttons/Buttons/Assets.xcassets/Button Images - No Slicing/Contents.json create mode 100644 Buttons/Buttons/Assets.xcassets/Button Images - No Slicing/unslicedBorder.imageset/Contents.json create mode 100644 Buttons/Buttons/Assets.xcassets/Button Images - No Slicing/unslicedBorder.imageset/border.pdf create mode 100644 Buttons/Buttons/Assets.xcassets/Button Images - No Slicing/unslicedFill.imageset/Contents.json create mode 100644 Buttons/Buttons/Assets.xcassets/Button Images - No Slicing/unslicedFill.imageset/filled.pdf create mode 100644 Buttons/Buttons/Assets.xcassets/Button Templates - Sliced/Contents.json create mode 100644 Buttons/Buttons/Assets.xcassets/Button Templates - Sliced/slicedBorderTemplate.imageset/Contents.json create mode 100644 Buttons/Buttons/Assets.xcassets/Button Templates - Sliced/slicedBorderTemplate.imageset/border.pdf create mode 100644 Buttons/Buttons/Assets.xcassets/Button Templates - Sliced/slicedFillTemplate.imageset/Contents.json create mode 100644 Buttons/Buttons/Assets.xcassets/Button Templates - Sliced/slicedFillTemplate.imageset/filled.pdf create mode 100644 Buttons/Buttons/Assets.xcassets/Contents.json create mode 100644 Buttons/Buttons/Base.lproj/LaunchScreen.storyboard create mode 100644 Buttons/Buttons/Base.lproj/Main.storyboard create mode 100644 Buttons/Buttons/Info.plist create mode 100644 Buttons/Buttons/ViewController.swift diff --git a/Buttons/Buttons.xcodeproj/project.pbxproj b/Buttons/Buttons.xcodeproj/project.pbxproj new file mode 100644 index 0000000..468cb69 --- /dev/null +++ b/Buttons/Buttons.xcodeproj/project.pbxproj @@ -0,0 +1,294 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 5323B0B81C88D97300B183DD /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5323B0B71C88D97300B183DD /* AppDelegate.swift */; }; + 5323B0BA1C88D97300B183DD /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5323B0B91C88D97300B183DD /* ViewController.swift */; }; + 5323B0BD1C88D97300B183DD /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5323B0BB1C88D97300B183DD /* Main.storyboard */; }; + 5323B0BF1C88D97300B183DD /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5323B0BE1C88D97300B183DD /* Assets.xcassets */; }; + 5323B0C21C88D97300B183DD /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5323B0C01C88D97300B183DD /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 5323B0B41C88D97300B183DD /* Buttons.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Buttons.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 5323B0B71C88D97300B183DD /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 5323B0B91C88D97300B183DD /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + 5323B0BC1C88D97300B183DD /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 5323B0BE1C88D97300B183DD /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 5323B0C11C88D97300B183DD /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 5323B0C31C88D97300B183DD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 5323B0B11C88D97300B183DD /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 5323B0AB1C88D97300B183DD = { + isa = PBXGroup; + children = ( + 5323B0B61C88D97300B183DD /* Buttons */, + 5323B0B51C88D97300B183DD /* Products */, + ); + sourceTree = ""; + }; + 5323B0B51C88D97300B183DD /* Products */ = { + isa = PBXGroup; + children = ( + 5323B0B41C88D97300B183DD /* Buttons.app */, + ); + name = Products; + sourceTree = ""; + }; + 5323B0B61C88D97300B183DD /* Buttons */ = { + isa = PBXGroup; + children = ( + 5323B0B71C88D97300B183DD /* AppDelegate.swift */, + 5323B0B91C88D97300B183DD /* ViewController.swift */, + 5323B0BB1C88D97300B183DD /* Main.storyboard */, + 5323B0BE1C88D97300B183DD /* Assets.xcassets */, + 5323B0C01C88D97300B183DD /* LaunchScreen.storyboard */, + 5323B0C31C88D97300B183DD /* Info.plist */, + ); + path = Buttons; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 5323B0B31C88D97300B183DD /* Buttons */ = { + isa = PBXNativeTarget; + buildConfigurationList = 5323B0C61C88D97300B183DD /* Build configuration list for PBXNativeTarget "Buttons" */; + buildPhases = ( + 5323B0B01C88D97300B183DD /* Sources */, + 5323B0B11C88D97300B183DD /* Frameworks */, + 5323B0B21C88D97300B183DD /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Buttons; + productName = Buttons; + productReference = 5323B0B41C88D97300B183DD /* Buttons.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 5323B0AC1C88D97300B183DD /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0720; + LastUpgradeCheck = 0720; + ORGANIZATIONNAME = "Keith Harrison"; + TargetAttributes = { + 5323B0B31C88D97300B183DD = { + CreatedOnToolsVersion = 7.2.1; + }; + }; + }; + buildConfigurationList = 5323B0AF1C88D97300B183DD /* Build configuration list for PBXProject "Buttons" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 5323B0AB1C88D97300B183DD; + productRefGroup = 5323B0B51C88D97300B183DD /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 5323B0B31C88D97300B183DD /* Buttons */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 5323B0B21C88D97300B183DD /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 5323B0C21C88D97300B183DD /* LaunchScreen.storyboard in Resources */, + 5323B0BF1C88D97300B183DD /* Assets.xcassets in Resources */, + 5323B0BD1C88D97300B183DD /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 5323B0B01C88D97300B183DD /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 5323B0BA1C88D97300B183DD /* ViewController.swift in Sources */, + 5323B0B81C88D97300B183DD /* AppDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 5323B0BB1C88D97300B183DD /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 5323B0BC1C88D97300B183DD /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 5323B0C01C88D97300B183DD /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 5323B0C11C88D97300B183DD /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 5323B0C41C88D97300B183DD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.2; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 5323B0C51C88D97300B183DD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.2; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 5323B0C71C88D97300B183DD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = Buttons/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.useyourloaf.Buttons; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 5323B0C81C88D97300B183DD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = Buttons/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.useyourloaf.Buttons; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 5323B0AF1C88D97300B183DD /* Build configuration list for PBXProject "Buttons" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 5323B0C41C88D97300B183DD /* Debug */, + 5323B0C51C88D97300B183DD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 5323B0C61C88D97300B183DD /* Build configuration list for PBXNativeTarget "Buttons" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 5323B0C71C88D97300B183DD /* Debug */, + 5323B0C81C88D97300B183DD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 5323B0AC1C88D97300B183DD /* Project object */; +} diff --git a/Buttons/Buttons/AppDelegate.swift b/Buttons/Buttons/AppDelegate.swift new file mode 100644 index 0000000..8bee4c3 --- /dev/null +++ b/Buttons/Buttons/AppDelegate.swift @@ -0,0 +1,41 @@ +// +// AppDelegate.swift +// Buttons +// +// Created by Keith Harrison http://useyourloaf.com +// Copyright (c) 2016 Keith Harrison. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? +} + diff --git a/Buttons/Buttons/Assets.xcassets/AppIcon.appiconset/Contents.json b/Buttons/Buttons/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..eeea76c --- /dev/null +++ b/Buttons/Buttons/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,73 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Buttons/Buttons/Assets.xcassets/Button Images - No Slicing/Contents.json b/Buttons/Buttons/Assets.xcassets/Button Images - No Slicing/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/Buttons/Buttons/Assets.xcassets/Button Images - No Slicing/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Buttons/Buttons/Assets.xcassets/Button Images - No Slicing/unslicedBorder.imageset/Contents.json b/Buttons/Buttons/Assets.xcassets/Button Images - No Slicing/unslicedBorder.imageset/Contents.json new file mode 100644 index 0000000..70fd5a2 --- /dev/null +++ b/Buttons/Buttons/Assets.xcassets/Button Images - No Slicing/unslicedBorder.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "border.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Buttons/Buttons/Assets.xcassets/Button Images - No Slicing/unslicedBorder.imageset/border.pdf b/Buttons/Buttons/Assets.xcassets/Button Images - No Slicing/unslicedBorder.imageset/border.pdf new file mode 100644 index 0000000000000000000000000000000000000000..0b798decfeace0654604e327922b1ae9b1d0c7a3 GIT binary patch literal 4065 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 literal 0 HcmV?d00001 diff --git a/Buttons/Buttons/Assets.xcassets/Button Images - No Slicing/unslicedFill.imageset/Contents.json b/Buttons/Buttons/Assets.xcassets/Button Images - No Slicing/unslicedFill.imageset/Contents.json new file mode 100644 index 0000000..3168b55 --- /dev/null +++ b/Buttons/Buttons/Assets.xcassets/Button Images - No Slicing/unslicedFill.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "filled.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Buttons/Buttons/Assets.xcassets/Button Images - No Slicing/unslicedFill.imageset/filled.pdf b/Buttons/Buttons/Assets.xcassets/Button Images - No Slicing/unslicedFill.imageset/filled.pdf new file mode 100644 index 0000000000000000000000000000000000000000..bec308024007717309b0677d016f60129eb61f7c GIT binary patch literal 4099 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 literal 0 HcmV?d00001 diff --git a/Buttons/Buttons/Assets.xcassets/Button Templates - Sliced/Contents.json b/Buttons/Buttons/Assets.xcassets/Button Templates - Sliced/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/Buttons/Buttons/Assets.xcassets/Button Templates - Sliced/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Buttons/Buttons/Assets.xcassets/Button Templates - Sliced/slicedBorderTemplate.imageset/Contents.json b/Buttons/Buttons/Assets.xcassets/Button Templates - Sliced/slicedBorderTemplate.imageset/Contents.json new file mode 100644 index 0000000..132fee5 --- /dev/null +++ b/Buttons/Buttons/Assets.xcassets/Button Templates - Sliced/slicedBorderTemplate.imageset/Contents.json @@ -0,0 +1,29 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "border.pdf", + "resizing" : { + "mode" : "9-part", + "center" : { + "mode" : "stretch", + "width" : 1, + "height" : 1 + }, + "cap-insets" : { + "bottom" : 10, + "top" : 10, + "right" : 10, + "left" : 10 + } + } + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "template-rendering-intent" : "template" + } +} \ No newline at end of file diff --git a/Buttons/Buttons/Assets.xcassets/Button Templates - Sliced/slicedBorderTemplate.imageset/border.pdf b/Buttons/Buttons/Assets.xcassets/Button Templates - Sliced/slicedBorderTemplate.imageset/border.pdf new file mode 100644 index 0000000000000000000000000000000000000000..0b798decfeace0654604e327922b1ae9b1d0c7a3 GIT binary patch literal 4065 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 literal 0 HcmV?d00001 diff --git a/Buttons/Buttons/Assets.xcassets/Button Templates - Sliced/slicedFillTemplate.imageset/Contents.json b/Buttons/Buttons/Assets.xcassets/Button Templates - Sliced/slicedFillTemplate.imageset/Contents.json new file mode 100644 index 0000000..b2cf9b2 --- /dev/null +++ b/Buttons/Buttons/Assets.xcassets/Button Templates - Sliced/slicedFillTemplate.imageset/Contents.json @@ -0,0 +1,29 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "filled.pdf", + "resizing" : { + "mode" : "9-part", + "center" : { + "mode" : "stretch", + "width" : 1, + "height" : 1 + }, + "cap-insets" : { + "bottom" : 10, + "top" : 10, + "right" : 10, + "left" : 10 + } + } + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "template-rendering-intent" : "template" + } +} \ No newline at end of file diff --git a/Buttons/Buttons/Assets.xcassets/Button Templates - Sliced/slicedFillTemplate.imageset/filled.pdf b/Buttons/Buttons/Assets.xcassets/Button Templates - Sliced/slicedFillTemplate.imageset/filled.pdf new file mode 100644 index 0000000000000000000000000000000000000000..bec308024007717309b0677d016f60129eb61f7c GIT binary patch literal 4099 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 literal 0 HcmV?d00001 diff --git a/Buttons/Buttons/Assets.xcassets/Contents.json b/Buttons/Buttons/Assets.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/Buttons/Buttons/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Buttons/Buttons/Base.lproj/LaunchScreen.storyboard b/Buttons/Buttons/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..90d6157 --- /dev/null +++ b/Buttons/Buttons/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Buttons/Buttons/Base.lproj/Main.storyboard b/Buttons/Buttons/Base.lproj/Main.storyboard new file mode 100644 index 0000000..15ac78d --- /dev/null +++ b/Buttons/Buttons/Base.lproj/Main.storyboard @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Buttons/Buttons/Info.plist b/Buttons/Buttons/Info.plist new file mode 100644 index 0000000..40c6215 --- /dev/null +++ b/Buttons/Buttons/Info.plist @@ -0,0 +1,47 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/Buttons/Buttons/ViewController.swift b/Buttons/Buttons/ViewController.swift new file mode 100644 index 0000000..c3b7cc9 --- /dev/null +++ b/Buttons/Buttons/ViewController.swift @@ -0,0 +1,68 @@ +// +// ViewController.swift +// Buttons +// +// Created by Keith Harrison http://useyourloaf.com +// Copyright (c) 2016 Keith Harrison. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +import UIKit + +class ViewController: UIViewController { + + @IBOutlet weak var stackView: UIStackView! + + override func viewDidLoad() { + super.viewDidLoad() + + // Create a custom button and set title label style + + let orangeButton = UIButton(type: .Custom) + orangeButton.setTitle("Orange", forState: .Normal) + orangeButton.setTitleColor(.orangeColor(), forState: .Normal) + orangeButton.setTitleColor(.whiteColor(), forState: .Highlighted) + orangeButton.titleLabel?.font = UIFont.systemFontOfSize(14) + orangeButton.contentEdgeInsets = UIEdgeInsetsMake(8, 8, 8, 8) + + // Get the pre-sliced template images direct from the + // asset catalog for the default and hightlighted states + + let slicedBorderTemplate = UIImage(named: "slicedBorderTemplate") + let slicedFillTemplate = UIImage(named: "slicedFillTemplate") + + orangeButton.setBackgroundImage(slicedBorderTemplate, forState: .Normal) + orangeButton.setBackgroundImage(slicedFillTemplate, forState: .Highlighted) + + // The tintColor controls the colour used by the template images + // Defaults to inherited value + + orangeButton.tintColor = .orangeColor() + + stackView.addArrangedSubview(orangeButton) + } +} \ No newline at end of file From f5e2ca073399c0f04eba8525b9920b6802229c2d Mon Sep 17 00:00:00 2001 From: Keith Harrison Date: Sat, 12 Mar 2016 21:44:15 +0000 Subject: [PATCH 022/184] Add scrolling stack view --- Stacks/README | 29 ---- Stacks/README.md | 47 ++++++ Stacks/Stacks.xcodeproj/project.pbxproj | 12 +- .../AppIcon.appiconset/Contents.json | 6 + .../AppIcon.appiconset/icon-167.png | Bin 0 -> 11276 bytes Stacks/Stacks/Base.lproj/Main.storyboard | 153 +++++++++++++++--- Stacks/Stacks/Info.plist | 4 +- .../Stacks/ScrollingStackViewController.swift | 45 ++++++ 8 files changed, 236 insertions(+), 60 deletions(-) delete mode 100644 Stacks/README create mode 100644 Stacks/README.md create mode 100644 Stacks/Stacks/Assets.xcassets/AppIcon.appiconset/icon-167.png create mode 100644 Stacks/Stacks/ScrollingStackViewController.swift diff --git a/Stacks/README b/Stacks/README deleted file mode 100644 index d4bb551..0000000 --- a/Stacks/README +++ /dev/null @@ -1,29 +0,0 @@ -======================================================================= -Stacks - Using stack views (requires iOS 9) - -Version 1.3 05 Oct 2015 Add static quick actions -Version 1.2 27 Sep 2015 Hide a view for compact height -Version 1.1 06 July 2015 Adapting axis to view size classes -Version 1.0 22 June 2015 Initial version. -======================================================================= - -Example of how to use UIStackView to simplify layout of horizontal and -vertical arrangements of views. See the following post for details: - -http://useyourloaf.com/blog/easier-autolayout-with-stackviews.html - -Also see this post for details on how to have the stack view automatically -adapt to size class changes: - -http://useyourloaf.com/blog/adapting-stack-views-with-size-classes.html - -The third view controller - HiddenViewController is an example of how to -use size classes to hide a view contained in a stack view. See this post -for details: - -http://useyourloaf.com/blog/using-size-classes-to-hide-stack-view-contents.html - -Quick actions allow the user to launch the app directly to one of the three tabs. -See this post for details: - -http://useyourloaf.com/blog/adding-3d-touch-quick-actions.html \ No newline at end of file diff --git a/Stacks/README.md b/Stacks/README.md new file mode 100644 index 0000000..1386fb1 --- /dev/null +++ b/Stacks/README.md @@ -0,0 +1,47 @@ +### Stacks + +Examples of using and sometime abusing stack views (requires iOS 9) + +#### Basic Stack View Usage + +Example of how to use UIStackView to simplify layout of horizontal and +vertical arrangements of views. See the following post for details: + ++ [Easier Auto Layout with Stack Views](http://useyourloaf.com/blog/easier-autolayout-with-stackviews/) + +#### Using Stack Views with Size Classes + +Also see this post for details on how to have the stack view automatically +adapt to size class changes: + ++ [Adapting Stack Views with Size Classes](http://useyourloaf.com/blog/adapting-stack-views-with-size-classes/) + +#### Hiding Views in a Stack View + +The third view controller - HiddenViewController is an example of how to +use size classes to hide a view contained in a stack view. See this post +for details: + ++ [Using Size Classes to Hide Stack View Contents](http://useyourloaf.com/blog/using-size-classes-to-hide-stack-view-contents/) + +#### 3D Touch Quick Actions + +Quick actions allow the user to launch the app directly to one of the three tabs. +See this post for details: + ++ [Adding 3D Touch Quick Actions](http://useyourloaf.com/blog/adding-3d-touch-quick-actions/) + +#### Scrolling Stack View + +The ScrollingStackViewController provides an example of how to embed a +stack view in a scroll view. See this post for details: + ++ [Scroll Stack Views](http://useyourloaf.com/blog/scrolling-stack-views/) + +#### Version History + ++ Version 1.4 12 Mar 2016 Add a scrolling stack view ++ Version 1.3 05 Oct 2015 Add static quick actions ++ Version 1.2 27 Sep 2015 Hide a view for compact height ++ Version 1.1 06 July 2015 Adapting axis to view size classes ++ Version 1.0 22 June 2015 Initial version. diff --git a/Stacks/Stacks.xcodeproj/project.pbxproj b/Stacks/Stacks.xcodeproj/project.pbxproj index 2f707eb..fadae18 100644 --- a/Stacks/Stacks.xcodeproj/project.pbxproj +++ b/Stacks/Stacks.xcodeproj/project.pbxproj @@ -8,8 +8,8 @@ /* Begin PBXBuildFile section */ 5378799F1B49BA6A004CBDC1 /* SizeClassViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5378799E1B49BA6A004CBDC1 /* SizeClassViewController.swift */; }; - 5395B1371BB48B4700CF54F7 /* HiddenViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5395B1361BB48B4700CF54F7 /* HiddenViewController.swift */; settings = {ASSET_TAGS = (); }; }; - 5395B1381BB48B4700CF54F7 /* HiddenViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5395B1361BB48B4700CF54F7 /* HiddenViewController.swift */; settings = {ASSET_TAGS = (); }; }; + 5395B1371BB48B4700CF54F7 /* HiddenViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5395B1361BB48B4700CF54F7 /* HiddenViewController.swift */; }; + 5395B1381BB48B4700CF54F7 /* HiddenViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5395B1361BB48B4700CF54F7 /* HiddenViewController.swift */; }; 53B55B951BC1B89A0071291E /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 53B55B981BC1B89A0071291E /* InfoPlist.strings */; }; 53B55B961BC1B89A0071291E /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 53B55B981BC1B89A0071291E /* InfoPlist.strings */; }; 53DA13EE1B32184600FEEE79 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53DA13ED1B32184600FEEE79 /* AppDelegate.swift */; }; @@ -19,6 +19,7 @@ 53DA13F81B32184600FEEE79 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 53DA13F61B32184600FEEE79 /* LaunchScreen.storyboard */; }; 53DA14031B32184600FEEE79 /* StacksTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53DA14021B32184600FEEE79 /* StacksTests.swift */; }; 53DA140E1B32184600FEEE79 /* StacksUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53DA140D1B32184600FEEE79 /* StacksUITests.swift */; }; + 53E9DC1B1C90E0FB00C9A89C /* ScrollingStackViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53E9DC1A1C90E0FB00C9A89C /* ScrollingStackViewController.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -42,6 +43,7 @@ 5378799E1B49BA6A004CBDC1 /* SizeClassViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SizeClassViewController.swift; sourceTree = ""; }; 5395B1361BB48B4700CF54F7 /* HiddenViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HiddenViewController.swift; sourceTree = ""; }; 53B55B971BC1B89A0071291E /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/InfoPlist.strings; sourceTree = ""; }; + 53BB4E981C94C22D0019483F /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 53DA13EA1B32184600FEEE79 /* Stacks.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Stacks.app; sourceTree = BUILT_PRODUCTS_DIR; }; 53DA13ED1B32184600FEEE79 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 53DA13EF1B32184600FEEE79 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; @@ -55,7 +57,7 @@ 53DA14091B32184600FEEE79 /* StacksUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = StacksUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 53DA140D1B32184600FEEE79 /* StacksUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StacksUITests.swift; sourceTree = ""; }; 53DA140F1B32184600FEEE79 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 53F4745D1B38B6C100560817 /* README */ = {isa = PBXFileReference; lastKnownFileType = text; path = README; sourceTree = ""; }; + 53E9DC1A1C90E0FB00C9A89C /* ScrollingStackViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScrollingStackViewController.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -86,7 +88,7 @@ 53DA13E11B32184500FEEE79 = { isa = PBXGroup; children = ( - 53F4745D1B38B6C100560817 /* README */, + 53BB4E981C94C22D0019483F /* README.md */, 53DA13EC1B32184600FEEE79 /* Stacks */, 53DA14011B32184600FEEE79 /* StacksTests */, 53DA140C1B32184600FEEE79 /* StacksUITests */, @@ -110,6 +112,7 @@ 53DA13ED1B32184600FEEE79 /* AppDelegate.swift */, 53DA13EF1B32184600FEEE79 /* ViewController.swift */, 53DA13F11B32184600FEEE79 /* Main.storyboard */, + 53E9DC1A1C90E0FB00C9A89C /* ScrollingStackViewController.swift */, 5378799E1B49BA6A004CBDC1 /* SizeClassViewController.swift */, 5395B1361BB48B4700CF54F7 /* HiddenViewController.swift */, 53DA13F41B32184600FEEE79 /* Assets.xcassets */, @@ -271,6 +274,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 53E9DC1B1C90E0FB00C9A89C /* ScrollingStackViewController.swift in Sources */, 5395B1371BB48B4700CF54F7 /* HiddenViewController.swift in Sources */, 53DA13F01B32184600FEEE79 /* ViewController.swift in Sources */, 53DA13EE1B32184600FEEE79 /* AppDelegate.swift in Sources */, diff --git a/Stacks/Stacks/Assets.xcassets/AppIcon.appiconset/Contents.json b/Stacks/Stacks/Assets.xcassets/AppIcon.appiconset/Contents.json index 056c0aa..adeb159 100644 --- a/Stacks/Stacks/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/Stacks/Stacks/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -71,6 +71,12 @@ "idiom" : "ipad", "filename" : "icon-76@2x.png", "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "icon-167.png", + "scale" : "2x" } ], "info" : { diff --git a/Stacks/Stacks/Assets.xcassets/AppIcon.appiconset/icon-167.png b/Stacks/Stacks/Assets.xcassets/AppIcon.appiconset/icon-167.png new file mode 100644 index 0000000000000000000000000000000000000000..0a5ec66a8746d77c19afa4db06b0e9e27145e92d GIT binary patch literal 11276 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 - + - + - + @@ -22,28 +22,23 @@ - - - - - - + @@ -72,47 +67,39 @@ - - - - - - - - + @@ -122,6 +109,125 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -139,7 +245,6 @@ - @@ -152,7 +257,6 @@ - @@ -164,11 +268,9 @@ - - @@ -177,14 +279,14 @@ - + - + @@ -194,13 +296,13 @@ - + @@ -209,6 +311,7 @@ + diff --git a/Stacks/Stacks/Info.plist b/Stacks/Stacks/Info.plist index 52811b9..1f7589c 100644 --- a/Stacks/Stacks/Info.plist +++ b/Stacks/Stacks/Info.plist @@ -15,11 +15,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.3 + 1.4 CFBundleSignature ???? CFBundleVersion - 3 + 4 LSRequiresIPhoneOS UIApplicationShortcutItems diff --git a/Stacks/Stacks/ScrollingStackViewController.swift b/Stacks/Stacks/ScrollingStackViewController.swift new file mode 100644 index 0000000..f8c5fcd --- /dev/null +++ b/Stacks/Stacks/ScrollingStackViewController.swift @@ -0,0 +1,45 @@ +// +// ScrollingStackViewController.swift +// Stacks +// +// Created by Keith Harrison on 09/03/2016. +// Copyright © 2016 Keith Harrison. All rights reserved. +// + +import UIKit + +class ScrollingStackViewController: UIViewController { + + @IBOutlet weak var scrollView: UIScrollView! + @IBOutlet weak var stackView: UIStackView! + + @IBAction func singleTap(sender: UITapGestureRecognizer) { + let heartImage = UIImage(named: "Heart") + let heartImageView = UIImageView(image: heartImage) + self.stackView.addArrangedSubview(heartImageView) + self.scrollToEnd(heartImageView) + } + + @IBAction func twoFingerTap(sender: UITapGestureRecognizer) { + let starImage = UIImage(named: "Star") + let starImageView = UIImageView(image: starImage) + self.stackView.addArrangedSubview(starImageView) + self.scrollToEnd(starImageView) + } + + @IBAction func threeFingerTap(sender: UITapGestureRecognizer) { + let views = stackView.arrangedSubviews + for entry in views { + stackView.removeArrangedSubview(entry) + entry.removeFromSuperview() + } + } + + private func scrollToEnd(addedView: UIView) { + let contentViewHeight = scrollView.contentSize.height + addedView.bounds.height + stackView.spacing + let offsetY = contentViewHeight - scrollView.bounds.height + if (offsetY > 0) { + scrollView.setContentOffset(CGPoint(x: scrollView.contentOffset.x, y: offsetY), animated: true) + } + } +} From 4d3691707660c6f3d1ce8354d9d6b52c95d1a5cc Mon Sep 17 00:00:00 2001 From: Keith Harrison Date: Sat, 12 Mar 2016 22:25:54 +0000 Subject: [PATCH 023/184] Removed surplus self --- Stacks/Stacks/ScrollingStackViewController.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Stacks/Stacks/ScrollingStackViewController.swift b/Stacks/Stacks/ScrollingStackViewController.swift index f8c5fcd..a460be1 100644 --- a/Stacks/Stacks/ScrollingStackViewController.swift +++ b/Stacks/Stacks/ScrollingStackViewController.swift @@ -16,15 +16,15 @@ class ScrollingStackViewController: UIViewController { @IBAction func singleTap(sender: UITapGestureRecognizer) { let heartImage = UIImage(named: "Heart") let heartImageView = UIImageView(image: heartImage) - self.stackView.addArrangedSubview(heartImageView) - self.scrollToEnd(heartImageView) + stackView.addArrangedSubview(heartImageView) + scrollToEnd(heartImageView) } @IBAction func twoFingerTap(sender: UITapGestureRecognizer) { let starImage = UIImage(named: "Star") let starImageView = UIImageView(image: starImage) - self.stackView.addArrangedSubview(starImageView) - self.scrollToEnd(starImageView) + stackView.addArrangedSubview(starImageView) + scrollToEnd(starImageView) } @IBAction func threeFingerTap(sender: UITapGestureRecognizer) { From 56d8a1d503965b7629b6c105ec13328b324616a7 Mon Sep 17 00:00:00 2001 From: Keith Harrison Date: Sat, 12 Mar 2016 22:27:38 +0000 Subject: [PATCH 024/184] Update copyright --- .../Stacks/ScrollingStackViewController.swift | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/Stacks/Stacks/ScrollingStackViewController.swift b/Stacks/Stacks/ScrollingStackViewController.swift index a460be1..554b609 100644 --- a/Stacks/Stacks/ScrollingStackViewController.swift +++ b/Stacks/Stacks/ScrollingStackViewController.swift @@ -2,9 +2,34 @@ // ScrollingStackViewController.swift // Stacks // -// Created by Keith Harrison on 09/03/2016. -// Copyright © 2016 Keith Harrison. All rights reserved. +// Created by Keith Harrison http://useyourloaf.com +// Copyright (c) 2016 Keith Harrison. All rights reserved. // +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. import UIKit From 3433bf705747394fe7ea036fcf9cad74732f4fa0 Mon Sep 17 00:00:00 2001 From: Keith Harrison Date: Sun, 13 Mar 2016 20:49:06 +0000 Subject: [PATCH 025/184] Use a content stack view --- Stacks/Stacks/Base.lproj/Main.storyboard | 135 ++++++++++++----------- 1 file changed, 69 insertions(+), 66 deletions(-) diff --git a/Stacks/Stacks/Base.lproj/Main.storyboard b/Stacks/Stacks/Base.lproj/Main.storyboard index 969631f..d119908 100644 --- a/Stacks/Stacks/Base.lproj/Main.storyboard +++ b/Stacks/Stacks/Base.lproj/Main.storyboard @@ -121,81 +121,84 @@ - - - - - - - - - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - + - - - - - - + + + + From 38dc9208231920d8a1f6b436b788a83563a60976 Mon Sep 17 00:00:00 2001 From: Keith Harrison Date: Fri, 25 Mar 2016 11:29:54 +0000 Subject: [PATCH 026/184] Adopt Swift 2.2 selector syntax --- AutoLayout/AutoLayout/LayoutGuideController.swift | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/AutoLayout/AutoLayout/LayoutGuideController.swift b/AutoLayout/AutoLayout/LayoutGuideController.swift index 28ce349..caaee87 100644 --- a/AutoLayout/AutoLayout/LayoutGuideController.swift +++ b/AutoLayout/AutoLayout/LayoutGuideController.swift @@ -66,14 +66,16 @@ class LayoutGuideController: UIViewController { let redImage = UIImage(named: "redButton") noButton.setBackgroundImage(redImage, forState: .Normal) noButton.contentEdgeInsets = UIEdgeInsetsMake(8, 16, 8, 16) - noButton.addTarget(self, action: "noThanks:", forControlEvents: .TouchUpInside) + let noThanksAction = #selector(LayoutGuideController.noThanks(_:)) + noButton.addTarget(self, action: noThanksAction, forControlEvents: .TouchUpInside) yesButton.translatesAutoresizingMaskIntoConstraints = false yesButton.setTitle("Yes please!", forState: .Normal) let greenImage = UIImage(named: "greenButton") yesButton.setBackgroundImage(greenImage, forState: .Normal) yesButton.contentEdgeInsets = UIEdgeInsetsMake(8, 16, 8, 16) - yesButton.addTarget(self, action: "yesPlease:", forControlEvents: .TouchUpInside) + let yesPleaseAction = #selector(LayoutGuideController.yesPlease(_:)) + yesButton.addTarget(self, action: yesPleaseAction, forControlEvents: .TouchUpInside) view.addSubview(noButton) view.addSubview(yesButton) From 14d4ad26732034cecd565861652159125309fe42 Mon Sep 17 00:00:00 2001 From: Keith Harrison Date: Fri, 25 Mar 2016 11:52:12 +0000 Subject: [PATCH 027/184] Updated by Xcode 7.3 --- AutoLayout/AutoLayout/Base.lproj/Main.storyboard | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AutoLayout/AutoLayout/Base.lproj/Main.storyboard b/AutoLayout/AutoLayout/Base.lproj/Main.storyboard index c689bb2..88f4933 100644 --- a/AutoLayout/AutoLayout/Base.lproj/Main.storyboard +++ b/AutoLayout/AutoLayout/Base.lproj/Main.storyboard @@ -1,8 +1,8 @@ - + - + From 94fa50dc8bb59c0ec1b497d1cf95ef205571e4d4 Mon Sep 17 00:00:00 2001 From: Keith Harrison Date: Fri, 25 Mar 2016 13:47:37 +0000 Subject: [PATCH 028/184] Layout Guides --- AutoLayout/AutoLayout/Base.lproj/Main.storyboard | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AutoLayout/AutoLayout/Base.lproj/Main.storyboard b/AutoLayout/AutoLayout/Base.lproj/Main.storyboard index 88f4933..f7a2421 100644 --- a/AutoLayout/AutoLayout/Base.lproj/Main.storyboard +++ b/AutoLayout/AutoLayout/Base.lproj/Main.storyboard @@ -136,7 +136,7 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -178,7 +221,7 @@ - + @@ -226,7 +269,7 @@ - + @@ -245,7 +288,7 @@ - + @@ -264,7 +307,7 @@ - + @@ -280,7 +323,7 @@ - + @@ -299,7 +342,7 @@ - + @@ -315,7 +358,7 @@ - + @@ -333,7 +376,7 @@ - + @@ -352,7 +395,7 @@ - + @@ -370,11 +413,141 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AutoLayout/AutoLayout/Info.plist b/AutoLayout/AutoLayout/Info.plist index fd1ab7c..3c0e24f 100644 --- a/AutoLayout/AutoLayout/Info.plist +++ b/AutoLayout/AutoLayout/Info.plist @@ -15,11 +15,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.0 + 1.4 CFBundleSignature ???? CFBundleVersion - 1 + 4 LSRequiresIPhoneOS UILaunchStoryboardName diff --git a/AutoLayout/README.md b/AutoLayout/README.md index 453c261..af9336a 100644 --- a/AutoLayout/README.md +++ b/AutoLayout/README.md @@ -7,15 +7,19 @@ Auto Layout examples using Interface Builder and in code. + Proportional spacing using a stack view + Layout Anchors + Layout Guides as spacer views ++ Adaptive constraints using Interface Builder and code See the following posts further details: + [Proportional Spacing with Auto Layout](http://useyourloaf.com/blog/proportional-spacing-with-auto-layout/) + [Adapting Images for Size Classes](http://useyourloaf.com/blog/adapting-images-for-size-classes/) + [Pain Free Constraints with Layout Anchors](http://useyourloaf.com/blog/pain-free-constraints-with-layout-anchors/) ++ [Goodbye Spacer Views Hello Layout Guides](http://useyourloaf.com/blog/goodbye-spacer-views-hello-layout-guides/) ++ [Adapting AutoLayout Without Interface Builder](http://useyourloaf.com/blog/adapting-auto-layout-without-interface-builder/) #### Version History ++ Version 1.4 28 Mar 2016 Adapting constraints to size classes + Version 1.3 27 Feb 2016 Using layout guides to space views + Version 1.2 22 Feb 2016 Example of using layout anchors + Version 1.1 31 Jan 2016 Custom Asset catalog for size classes. From 4d65e6ffe0163328cedbc85915591b57e51b523e Mon Sep 17 00:00:00 2001 From: Keith Harrison Date: Mon, 4 Apr 2016 21:51:25 +0100 Subject: [PATCH 030/184] Add contentMode playground --- .../Bottom.xcplaygroundpage/Contents.swift | 15 +++++++++ .../timeline.xctimeline | 11 +++++++ .../Contents.swift | 14 ++++++++ .../timeline.xctimeline | 11 +++++++ .../Contents.swift | 14 ++++++++ .../timeline.xctimeline | 11 +++++++ .../Center.xcplaygroundpage/Contents.swift | 15 +++++++++ .../timeline.xctimeline | 21 ++++++++++++ .../Left.xcplaygroundpage/Contents.swift | 14 ++++++++ .../Left.xcplaygroundpage/timeline.xctimeline | 11 +++++++ .../Redraw.xcplaygroundpage/Contents.swift | 18 +++++++++++ .../timeline.xctimeline | 21 ++++++++++++ .../Right.xcplaygroundpage/Contents.swift | 14 ++++++++ .../timeline.xctimeline | 11 +++++++ .../Contents.swift | 30 ++++++++++++++++++ .../timeline.xctimeline | 16 ++++++++++ .../Contents.swift | 14 ++++++++ .../timeline.xctimeline | 11 +++++++ .../Contents.swift | 17 ++++++++++ .../timeline.xctimeline | 11 +++++++ .../Pages/Top.xcplaygroundpage/Contents.swift | 15 +++++++++ .../Top.xcplaygroundpage/timeline.xctimeline | 11 +++++++ .../TopLeft.xcplaygroundpage/Contents.swift | 14 ++++++++ .../timeline.xctimeline | 11 +++++++ .../TopRight.xcplaygroundpage/Contents.swift | 14 ++++++++ .../timeline.xctimeline | 11 +++++++ .../Contents.swift | 29 +++++++++++++++++ .../Resources/star100.png | Bin 0 -> 1306 bytes .../Sources/CircleView.swift | 22 +++++++++++++ .../Sources/StarView.swift | 21 ++++++++++++ .../contents.xcplayground | 19 +++++++++++ .../contents.xcworkspacedata | 7 ++++ 32 files changed, 474 insertions(+) create mode 100644 Playgrounds/ContentMode.playground/Pages/Bottom.xcplaygroundpage/Contents.swift create mode 100644 Playgrounds/ContentMode.playground/Pages/Bottom.xcplaygroundpage/timeline.xctimeline create mode 100644 Playgrounds/ContentMode.playground/Pages/BottomLeft.xcplaygroundpage/Contents.swift create mode 100644 Playgrounds/ContentMode.playground/Pages/BottomLeft.xcplaygroundpage/timeline.xctimeline create mode 100644 Playgrounds/ContentMode.playground/Pages/BottomRight.xcplaygroundpage/Contents.swift create mode 100644 Playgrounds/ContentMode.playground/Pages/BottomRight.xcplaygroundpage/timeline.xctimeline create mode 100644 Playgrounds/ContentMode.playground/Pages/Center.xcplaygroundpage/Contents.swift create mode 100644 Playgrounds/ContentMode.playground/Pages/Center.xcplaygroundpage/timeline.xctimeline create mode 100644 Playgrounds/ContentMode.playground/Pages/Left.xcplaygroundpage/Contents.swift create mode 100644 Playgrounds/ContentMode.playground/Pages/Left.xcplaygroundpage/timeline.xctimeline create mode 100644 Playgrounds/ContentMode.playground/Pages/Redraw.xcplaygroundpage/Contents.swift create mode 100644 Playgrounds/ContentMode.playground/Pages/Redraw.xcplaygroundpage/timeline.xctimeline create mode 100644 Playgrounds/ContentMode.playground/Pages/Right.xcplaygroundpage/Contents.swift create mode 100644 Playgrounds/ContentMode.playground/Pages/Right.xcplaygroundpage/timeline.xctimeline create mode 100644 Playgrounds/ContentMode.playground/Pages/ScaleAspectFill.xcplaygroundpage/Contents.swift create mode 100644 Playgrounds/ContentMode.playground/Pages/ScaleAspectFill.xcplaygroundpage/timeline.xctimeline create mode 100644 Playgrounds/ContentMode.playground/Pages/ScaleAspectFit.xcplaygroundpage/Contents.swift create mode 100644 Playgrounds/ContentMode.playground/Pages/ScaleAspectFit.xcplaygroundpage/timeline.xctimeline create mode 100644 Playgrounds/ContentMode.playground/Pages/ScaleToFill.xcplaygroundpage/Contents.swift create mode 100644 Playgrounds/ContentMode.playground/Pages/ScaleToFill.xcplaygroundpage/timeline.xctimeline create mode 100644 Playgrounds/ContentMode.playground/Pages/Top.xcplaygroundpage/Contents.swift create mode 100644 Playgrounds/ContentMode.playground/Pages/Top.xcplaygroundpage/timeline.xctimeline create mode 100644 Playgrounds/ContentMode.playground/Pages/TopLeft.xcplaygroundpage/Contents.swift create mode 100644 Playgrounds/ContentMode.playground/Pages/TopLeft.xcplaygroundpage/timeline.xctimeline create mode 100644 Playgrounds/ContentMode.playground/Pages/TopRight.xcplaygroundpage/Contents.swift create mode 100644 Playgrounds/ContentMode.playground/Pages/TopRight.xcplaygroundpage/timeline.xctimeline create mode 100644 Playgrounds/ContentMode.playground/Pages/contentMode.xcplaygroundpage/Contents.swift create mode 100644 Playgrounds/ContentMode.playground/Resources/star100.png create mode 100644 Playgrounds/ContentMode.playground/Sources/CircleView.swift create mode 100644 Playgrounds/ContentMode.playground/Sources/StarView.swift create mode 100644 Playgrounds/ContentMode.playground/contents.xcplayground create mode 100644 Playgrounds/ContentMode.playground/playground.xcworkspace/contents.xcworkspacedata diff --git a/Playgrounds/ContentMode.playground/Pages/Bottom.xcplaygroundpage/Contents.swift b/Playgrounds/ContentMode.playground/Pages/Bottom.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..24abc4e --- /dev/null +++ b/Playgrounds/ContentMode.playground/Pages/Bottom.xcplaygroundpage/Contents.swift @@ -0,0 +1,15 @@ +import UIKit +import XCPlayground +/*: + ### Bottom + + Fix the position of the content. Does not scale + or stretch the content. + */ +let myView = StarView(frame: CGRect(x: 0, y: 0, width:200, height:350)) +myView.starImageView.contentMode = .Bottom +myView +XCPlaygroundPage.currentPage.liveView = myView +//: [Previous](@previous) +//: [Index](contentMode) +//: [Next](@next) diff --git a/Playgrounds/ContentMode.playground/Pages/Bottom.xcplaygroundpage/timeline.xctimeline b/Playgrounds/ContentMode.playground/Pages/Bottom.xcplaygroundpage/timeline.xctimeline new file mode 100644 index 0000000..f4e4340 --- /dev/null +++ b/Playgrounds/ContentMode.playground/Pages/Bottom.xcplaygroundpage/timeline.xctimeline @@ -0,0 +1,11 @@ + + + + + + + diff --git a/Playgrounds/ContentMode.playground/Pages/BottomLeft.xcplaygroundpage/Contents.swift b/Playgrounds/ContentMode.playground/Pages/BottomLeft.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..1bb2c89 --- /dev/null +++ b/Playgrounds/ContentMode.playground/Pages/BottomLeft.xcplaygroundpage/Contents.swift @@ -0,0 +1,14 @@ +import UIKit +import XCPlayground +/*: + ### Bottom Left + Fix the position of the content. Does not scale + or stretch the content. + */ +let myView = StarView(frame: CGRect(x: 0, y: 0, width:200, height:350)) +myView.starImageView.contentMode = .BottomLeft +myView +XCPlaygroundPage.currentPage.liveView = myView +//: [Previous](@previous) +//: [Index](contentMode) +//: [Next](@next) diff --git a/Playgrounds/ContentMode.playground/Pages/BottomLeft.xcplaygroundpage/timeline.xctimeline b/Playgrounds/ContentMode.playground/Pages/BottomLeft.xcplaygroundpage/timeline.xctimeline new file mode 100644 index 0000000..750a3a3 --- /dev/null +++ b/Playgrounds/ContentMode.playground/Pages/BottomLeft.xcplaygroundpage/timeline.xctimeline @@ -0,0 +1,11 @@ + + + + + + + diff --git a/Playgrounds/ContentMode.playground/Pages/BottomRight.xcplaygroundpage/Contents.swift b/Playgrounds/ContentMode.playground/Pages/BottomRight.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..f3442e5 --- /dev/null +++ b/Playgrounds/ContentMode.playground/Pages/BottomRight.xcplaygroundpage/Contents.swift @@ -0,0 +1,14 @@ +import UIKit +import XCPlayground +/*: + ### Bottom Right + Fix the position of the content. Does not scale + or stretch the content. + */ +let myView = StarView(frame: CGRect(x: 0, y: 0, width:200, height:350)) +myView.starImageView.contentMode = .BottomRight +myView +XCPlaygroundPage.currentPage.liveView = myView +//: [Previous](@previous) +//: [Index](contentMode) +//: [Next](@next) diff --git a/Playgrounds/ContentMode.playground/Pages/BottomRight.xcplaygroundpage/timeline.xctimeline b/Playgrounds/ContentMode.playground/Pages/BottomRight.xcplaygroundpage/timeline.xctimeline new file mode 100644 index 0000000..e2347be --- /dev/null +++ b/Playgrounds/ContentMode.playground/Pages/BottomRight.xcplaygroundpage/timeline.xctimeline @@ -0,0 +1,11 @@ + + + + + + + diff --git a/Playgrounds/ContentMode.playground/Pages/Center.xcplaygroundpage/Contents.swift b/Playgrounds/ContentMode.playground/Pages/Center.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..a7cbc6e --- /dev/null +++ b/Playgrounds/ContentMode.playground/Pages/Center.xcplaygroundpage/Contents.swift @@ -0,0 +1,15 @@ +import UIKit +import XCPlayground +/*: + ### Center + + Fix the position of the content. Does not scale + or stretch the content. + */ +let myView = StarView(frame: CGRect(x: 0, y: 0, width:200, height:350)) +myView.starImageView.contentMode = .Center +myView +XCPlaygroundPage.currentPage.liveView = myView +//: [Previous](@previous) +//: [Index](contentMode) +//: [Next](@next) diff --git a/Playgrounds/ContentMode.playground/Pages/Center.xcplaygroundpage/timeline.xctimeline b/Playgrounds/ContentMode.playground/Pages/Center.xcplaygroundpage/timeline.xctimeline new file mode 100644 index 0000000..5e04c90 --- /dev/null +++ b/Playgrounds/ContentMode.playground/Pages/Center.xcplaygroundpage/timeline.xctimeline @@ -0,0 +1,21 @@ + + + + + + + + + + + diff --git a/Playgrounds/ContentMode.playground/Pages/Left.xcplaygroundpage/Contents.swift b/Playgrounds/ContentMode.playground/Pages/Left.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..4844811 --- /dev/null +++ b/Playgrounds/ContentMode.playground/Pages/Left.xcplaygroundpage/Contents.swift @@ -0,0 +1,14 @@ +import UIKit +import XCPlayground +/*: + ### Left + Fix the position of the content. Does not scale + or stretch the content. + */ +let myView = StarView(frame: CGRect(x: 0, y: 0, width:200, height:350)) +myView.starImageView.contentMode = .Left +myView +XCPlaygroundPage.currentPage.liveView = myView +//: [Previous](@previous) +//: [Index](contentMode) +//: [Next](@next) diff --git a/Playgrounds/ContentMode.playground/Pages/Left.xcplaygroundpage/timeline.xctimeline b/Playgrounds/ContentMode.playground/Pages/Left.xcplaygroundpage/timeline.xctimeline new file mode 100644 index 0000000..da653f9 --- /dev/null +++ b/Playgrounds/ContentMode.playground/Pages/Left.xcplaygroundpage/timeline.xctimeline @@ -0,0 +1,11 @@ + + + + + + + diff --git a/Playgrounds/ContentMode.playground/Pages/Redraw.xcplaygroundpage/Contents.swift b/Playgrounds/ContentMode.playground/Pages/Redraw.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..8486cfd --- /dev/null +++ b/Playgrounds/ContentMode.playground/Pages/Redraw.xcplaygroundpage/Contents.swift @@ -0,0 +1,18 @@ +import UIKit +import XCPlayground +/*: + ### Redraw + + Use this mode when you have a custom view that implements drawRect. + + When the bounds of the view change the view will be redrawn by + calling drawRect. + */ +let myView = CircleView(frame: CGRect(x: 0, y: 0, width:200, height:350)) +myView.backgroundColor = .whiteColor() +myView.contentMode = .Redraw +myView +XCPlaygroundPage.currentPage.liveView = myView +//: [Previous](@previous) +//: [Index](contentMode) +//: [Next](@next) \ No newline at end of file diff --git a/Playgrounds/ContentMode.playground/Pages/Redraw.xcplaygroundpage/timeline.xctimeline b/Playgrounds/ContentMode.playground/Pages/Redraw.xcplaygroundpage/timeline.xctimeline new file mode 100644 index 0000000..c8b56ae --- /dev/null +++ b/Playgrounds/ContentMode.playground/Pages/Redraw.xcplaygroundpage/timeline.xctimeline @@ -0,0 +1,21 @@ + + + + + + + + + + + diff --git a/Playgrounds/ContentMode.playground/Pages/Right.xcplaygroundpage/Contents.swift b/Playgrounds/ContentMode.playground/Pages/Right.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..e87045f --- /dev/null +++ b/Playgrounds/ContentMode.playground/Pages/Right.xcplaygroundpage/Contents.swift @@ -0,0 +1,14 @@ +import UIKit +import XCPlayground +/*: + ### Right + Fix the position of the content. Does not scale + or stretch the content. + */ +let myView = StarView(frame: CGRect(x: 0, y: 0, width:200, height:350)) +myView.starImageView.contentMode = .Right +myView +XCPlaygroundPage.currentPage.liveView = myView +//: [Previous](@previous) +//: [Index](contentMode) +//: [Next](@next) diff --git a/Playgrounds/ContentMode.playground/Pages/Right.xcplaygroundpage/timeline.xctimeline b/Playgrounds/ContentMode.playground/Pages/Right.xcplaygroundpage/timeline.xctimeline new file mode 100644 index 0000000..52f2979 --- /dev/null +++ b/Playgrounds/ContentMode.playground/Pages/Right.xcplaygroundpage/timeline.xctimeline @@ -0,0 +1,11 @@ + + + + + + + diff --git a/Playgrounds/ContentMode.playground/Pages/ScaleAspectFill.xcplaygroundpage/Contents.swift b/Playgrounds/ContentMode.playground/Pages/ScaleAspectFill.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..8788d5e --- /dev/null +++ b/Playgrounds/ContentMode.playground/Pages/ScaleAspectFill.xcplaygroundpage/Contents.swift @@ -0,0 +1,30 @@ +import UIKit +import XCPlayground +/*: + ### Scale Aspect Fill + `ScaleAspectFill` scales the content to totally fill the view maintaining the aspect ratio. This can result in the content being larger than the bounds of the view. + + */ +let containerView = UIView(frame: CGRect(x: 0, y: 0, width: 400, height: 400)) +containerView.backgroundColor = .redColor() + +let myView = StarView(frame: CGRect(x: 0, y: 0, width:200, height:350)) +containerView.addSubview(myView) +myView.center = CGPoint(x: 200, y: 200) + +myView.starImageView.contentMode = .ScaleAspectFill +/*: + #### clipToBounds + You will most likely want to have the superview set to clip subviews + to bounds to prevent the scaled view from being visible outside the + super view. + + Try changing the following setting to see the difference. + */ +myView.clipsToBounds = true + +containerView +XCPlaygroundPage.currentPage.liveView = containerView +//: [Previous](@previous) +//: [Index](contentMode) +//: [Next](@next) diff --git a/Playgrounds/ContentMode.playground/Pages/ScaleAspectFill.xcplaygroundpage/timeline.xctimeline b/Playgrounds/ContentMode.playground/Pages/ScaleAspectFill.xcplaygroundpage/timeline.xctimeline new file mode 100644 index 0000000..ec83354 --- /dev/null +++ b/Playgrounds/ContentMode.playground/Pages/ScaleAspectFill.xcplaygroundpage/timeline.xctimeline @@ -0,0 +1,16 @@ + + + + + + + + + diff --git a/Playgrounds/ContentMode.playground/Pages/ScaleAspectFit.xcplaygroundpage/Contents.swift b/Playgrounds/ContentMode.playground/Pages/ScaleAspectFit.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..daae1d1 --- /dev/null +++ b/Playgrounds/ContentMode.playground/Pages/ScaleAspectFit.xcplaygroundpage/Contents.swift @@ -0,0 +1,14 @@ +import UIKit +import XCPlayground +/*: + ### Scale Aspect Fit + + `ScaleAspectFit` scales the content to fit the view but maintains the aspect ratio. Any part of the view bounds that is not filled with content is transparent. + */ +let myView = StarView(frame: CGRect(x: 0, y: 0, width:200, height:350)) +myView.starImageView.contentMode = .ScaleAspectFit +myView +XCPlaygroundPage.currentPage.liveView = myView +//: [Previous](@previous) +//: [Index](contentMode) +//: [Next](@next) diff --git a/Playgrounds/ContentMode.playground/Pages/ScaleAspectFit.xcplaygroundpage/timeline.xctimeline b/Playgrounds/ContentMode.playground/Pages/ScaleAspectFit.xcplaygroundpage/timeline.xctimeline new file mode 100644 index 0000000..61b3e15 --- /dev/null +++ b/Playgrounds/ContentMode.playground/Pages/ScaleAspectFit.xcplaygroundpage/timeline.xctimeline @@ -0,0 +1,11 @@ + + + + + + + diff --git a/Playgrounds/ContentMode.playground/Pages/ScaleToFill.xcplaygroundpage/Contents.swift b/Playgrounds/ContentMode.playground/Pages/ScaleToFill.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..5e6608e --- /dev/null +++ b/Playgrounds/ContentMode.playground/Pages/ScaleToFill.xcplaygroundpage/Contents.swift @@ -0,0 +1,17 @@ +import UIKit +import XCPlayground +/*: + ### Scale To Fill + + This is the default mode. + + The image is stretched in both dimensions to fill the frame. The + aspect ratio of the image is not maintained. + */ +let myView = StarView(frame: CGRect(x: 0, y: 0, width:200, height:350)) +myView.starImageView.contentMode = .ScaleToFill +myView +XCPlaygroundPage.currentPage.liveView = myView +//: [Previous](@previous) +//: [Index](contentMode) +//: [Next](@next) diff --git a/Playgrounds/ContentMode.playground/Pages/ScaleToFill.xcplaygroundpage/timeline.xctimeline b/Playgrounds/ContentMode.playground/Pages/ScaleToFill.xcplaygroundpage/timeline.xctimeline new file mode 100644 index 0000000..07c1389 --- /dev/null +++ b/Playgrounds/ContentMode.playground/Pages/ScaleToFill.xcplaygroundpage/timeline.xctimeline @@ -0,0 +1,11 @@ + + + + + + + diff --git a/Playgrounds/ContentMode.playground/Pages/Top.xcplaygroundpage/Contents.swift b/Playgrounds/ContentMode.playground/Pages/Top.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..e96706e --- /dev/null +++ b/Playgrounds/ContentMode.playground/Pages/Top.xcplaygroundpage/Contents.swift @@ -0,0 +1,15 @@ +import UIKit +import XCPlayground +/*: + ### Top + + Fix the position of the content. Does not scale + or stretch the content. + */ +let myView = StarView(frame: CGRect(x: 0, y: 0, width:200, height:350)) +myView.starImageView.contentMode = .Top +myView +XCPlaygroundPage.currentPage.liveView = myView +//: [Previous](@previous) +//: [Index](contentMode) +//: [Next](@next) diff --git a/Playgrounds/ContentMode.playground/Pages/Top.xcplaygroundpage/timeline.xctimeline b/Playgrounds/ContentMode.playground/Pages/Top.xcplaygroundpage/timeline.xctimeline new file mode 100644 index 0000000..0e86698 --- /dev/null +++ b/Playgrounds/ContentMode.playground/Pages/Top.xcplaygroundpage/timeline.xctimeline @@ -0,0 +1,11 @@ + + + + + + + diff --git a/Playgrounds/ContentMode.playground/Pages/TopLeft.xcplaygroundpage/Contents.swift b/Playgrounds/ContentMode.playground/Pages/TopLeft.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..5fdbfd4 --- /dev/null +++ b/Playgrounds/ContentMode.playground/Pages/TopLeft.xcplaygroundpage/Contents.swift @@ -0,0 +1,14 @@ +import UIKit +import XCPlayground +/*: + ### Top Left + Fix the position of the content. Does not scale + or stretch the content. + */ +let myView = StarView(frame: CGRect(x: 0, y: 0, width:200, height:350)) +myView.starImageView.contentMode = .TopLeft +myView +XCPlaygroundPage.currentPage.liveView = myView +//: [Previous](@previous) +//: [Index](contentMode) +//: [Next](@next) diff --git a/Playgrounds/ContentMode.playground/Pages/TopLeft.xcplaygroundpage/timeline.xctimeline b/Playgrounds/ContentMode.playground/Pages/TopLeft.xcplaygroundpage/timeline.xctimeline new file mode 100644 index 0000000..bfa3c3c --- /dev/null +++ b/Playgrounds/ContentMode.playground/Pages/TopLeft.xcplaygroundpage/timeline.xctimeline @@ -0,0 +1,11 @@ + + + + + + + diff --git a/Playgrounds/ContentMode.playground/Pages/TopRight.xcplaygroundpage/Contents.swift b/Playgrounds/ContentMode.playground/Pages/TopRight.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..330506c --- /dev/null +++ b/Playgrounds/ContentMode.playground/Pages/TopRight.xcplaygroundpage/Contents.swift @@ -0,0 +1,14 @@ +import UIKit +import XCPlayground +/*: + ### Top Right + Fix the position of the content. Does not scale + or stretch the content. + */ +let myView = StarView(frame: CGRect(x: 0, y: 0, width:200, height:350)) +myView.starImageView.contentMode = .TopRight +myView +XCPlaygroundPage.currentPage.liveView = myView +//: [Previous](@previous) +//: [Index](contentMode) +//: [Next](@next) diff --git a/Playgrounds/ContentMode.playground/Pages/TopRight.xcplaygroundpage/timeline.xctimeline b/Playgrounds/ContentMode.playground/Pages/TopRight.xcplaygroundpage/timeline.xctimeline new file mode 100644 index 0000000..5490561 --- /dev/null +++ b/Playgrounds/ContentMode.playground/Pages/TopRight.xcplaygroundpage/timeline.xctimeline @@ -0,0 +1,11 @@ + + + + + + + diff --git a/Playgrounds/ContentMode.playground/Pages/contentMode.xcplaygroundpage/Contents.swift b/Playgrounds/ContentMode.playground/Pages/contentMode.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..04412dc --- /dev/null +++ b/Playgrounds/ContentMode.playground/Pages/contentMode.xcplaygroundpage/Contents.swift @@ -0,0 +1,29 @@ +/*: + ### UIView contentMode Quick Guide + +The `contentMode` property of UIView allows you to control how to layout a view when the view's bounds change. The system will not, by default, redraw a view each time the bounds change. That would be wasteful. Instead, depending on the content mode, it can scale, stretch or pin the contents to a fixed position. + + There are thirteen different content modes but it is easiest to think of three main groups based on the effect: + + #### Scaling the View (with or without maintaining the aspect ratio) + ++ [Scale To Fill](ScaleToFill) ++ [Scale Aspect Fit](ScaleAspectFit) ++ [Scale Aspect Fill](ScaleAspectFill) + + #### Redrawing the View + ++ [Redraw](Redraw) + + #### Positioning the View + ++ [Center](Center) ++ [Top](Top) ++ [Bottom](Bottom) ++ [Left](Left) ++ [Right](Right) ++ [TopLeft](TopLeft) ++ [TopRight](TopRight) ++ [BottomLeft](BottomLeft) ++ [BottomRight](BottomRight) +*/ diff --git a/Playgrounds/ContentMode.playground/Resources/star100.png b/Playgrounds/ContentMode.playground/Resources/star100.png new file mode 100644 index 0000000000000000000000000000000000000000..4965255dc5c56ab3d62c08ab371db50421bcb727 GIT binary patch literal 1306 zcmV+#1?BpQP)LRY? zN?hBWG1ke`=_CWu_^ahjSlgj4*UZrACj!y=r{qyp+oUYk&-U#B&FCrt(fXz2R8rcf zDb~{U?gGl`Eu!OBPTH#`)z!%8FP`IDOxms_)z|dx1I6evo8w+d+OZ+k+VkxM!{;@a z;$TJDwI9{n^6dq`=Q)((WJ1`u9n{_N?FPK(JQdUBrsPv<-H$ES%?r}-5!2~4*Tq@f zpnl(b>FW;O>KeM|K9%BQao&hc*{mwo(FoD=4$|#;-+-OtT*BuzD=U;$OYyJL2mWvE@gN;cIH$kh$hR-Rc|B=_ak^ONiibW!;qt z((<(CL~GrSujNUq+B3Y*u6N{ z!gt<+=j#yU>=Q88%H!)5E!NK9>lj+xpCZ-SRNJN3>L4D~+)vu78`R$?)zpmPY`5k? zj^Sz()aO6gyW{H=Y21+()8v!kWkJ}vzUDfX;$p$)H%8gCaNdbX*|NpxGjiUCMA^2? z=qpUxtqjudc;12N>kr51FxKiJh2V7V>?_vL@a+dS*TkgbRYTaf#OE~5=qXd$rl{mlR@w@5Sq2yTn&#c)100N## zL_t(|0qxj>dNeoy1<+gF+P3-Iwr$(0ZQEJnm3-mirOOnTeZ~Lkwd*(hAmHY~Tet7ry+>7R!|y+MxOHO;9LPU?>d|BB zvHi)@XU|{2){B?a>J{ucS4e$QyqMims^M1tB$5S6c@`$EfOfJ%lw-O=B1&oTiGVW=J_ec`dDw z^gNZ4+Oi<6IGa))-++SlwUpr70VSQAD6Fd+ih8zFP;@VpU7(;osOzU7AE+CkpyyC` zkAeoFY>1*BLD?`xjX>Ebg}nwFcJQ1d<|d;leh)wJW%hp+C@j*C#zL0e z6%DlX88OLO~Yiti7`v4*m8!=llT$e{x3Z{ysR>^9N^a@`Su&oE8#=5%a6fB!?8Uoa%y#ERwS=H})$2?F+@vwNYd Qm;e9(07*qoM6N<$f-6757XSbN literal 0 HcmV?d00001 diff --git a/Playgrounds/ContentMode.playground/Sources/CircleView.swift b/Playgrounds/ContentMode.playground/Sources/CircleView.swift new file mode 100644 index 0000000..9ca1165 --- /dev/null +++ b/Playgrounds/ContentMode.playground/Sources/CircleView.swift @@ -0,0 +1,22 @@ +import UIKit + +public class CircleView: UIView { + + var lineWidth: CGFloat = 5 { + didSet { setNeedsDisplay() } + } + + var color: UIColor = .redColor() { + didSet { setNeedsDisplay() } + } + + public override func drawRect(rect: CGRect) { + + let circleCenter = convertPoint(center, fromView: superview) + let circleRadius = min(bounds.size.width,bounds.size.height)/2 * 0.80 + let circlePath = UIBezierPath(arcCenter: circleCenter, radius: circleRadius, startAngle: 0, endAngle: CGFloat(2*M_PI), clockwise: true) + circlePath.lineWidth = lineWidth + color.set() + circlePath.stroke() + } +} diff --git a/Playgrounds/ContentMode.playground/Sources/StarView.swift b/Playgrounds/ContentMode.playground/Sources/StarView.swift new file mode 100644 index 0000000..d1bc0fe --- /dev/null +++ b/Playgrounds/ContentMode.playground/Sources/StarView.swift @@ -0,0 +1,21 @@ +import UIKit + +public class StarView: UIView { + + public let starImageView: UIImageView + + public override init(frame: CGRect) { + let starImage = UIImage(named: "star100") + starImageView = UIImageView(image: starImage) + + super.init(frame: frame) + addSubview(starImageView) + backgroundColor = .greenColor() + starImageView.frame = bounds + } + + public required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + diff --git a/Playgrounds/ContentMode.playground/contents.xcplayground b/Playgrounds/ContentMode.playground/contents.xcplayground new file mode 100644 index 0000000..e759d69 --- /dev/null +++ b/Playgrounds/ContentMode.playground/contents.xcplayground @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Playgrounds/ContentMode.playground/playground.xcworkspace/contents.xcworkspacedata b/Playgrounds/ContentMode.playground/playground.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..5cbf448 --- /dev/null +++ b/Playgrounds/ContentMode.playground/playground.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + From 4b6942f45f69c3fb18ba8408239f92f449b4e595 Mon Sep 17 00:00:00 2001 From: Keith Harrison Date: Mon, 4 Apr 2016 21:58:00 +0100 Subject: [PATCH 031/184] Zip playground files --- Playgrounds/ContentMode.zip | Bin 0 -> 36587 bytes Playgrounds/String.zip | Bin 0 -> 11986 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 Playgrounds/ContentMode.zip create mode 100644 Playgrounds/String.zip diff --git a/Playgrounds/ContentMode.zip b/Playgrounds/ContentMode.zip new file mode 100644 index 0000000000000000000000000000000000000000..464fa32bdb5ba5d172567b7e5da8e828571f64de GIT binary patch literal 36587 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 literal 0 HcmV?d00001 diff --git a/Playgrounds/String.zip b/Playgrounds/String.zip new file mode 100644 index 0000000000000000000000000000000000000000..64cb3fcb5cb958a2e6a3d24431bc195f015fc6b2 GIT binary patch literal 11986 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 Date: Mon, 11 Apr 2016 16:53:21 +0100 Subject: [PATCH 032/184] Add example of right-to-left language --- .../AutoLayout.xcodeproj/project.pbxproj | 8 ++ .../AutoLayout/Base.lproj/Main.storyboard | 120 +++++++++++++++--- AutoLayout/AutoLayout/Info.plist | 4 +- AutoLayout/AutoLayout/RTLViewController.swift | 69 ++++++++++ .../AutoLayout/ar.lproj/LaunchScreen.strings | 3 + AutoLayout/AutoLayout/ar.lproj/Main.strings | 78 ++++++++++++ AutoLayout/README.md | 3 + 7 files changed, 266 insertions(+), 19 deletions(-) create mode 100644 AutoLayout/AutoLayout/RTLViewController.swift create mode 100644 AutoLayout/AutoLayout/ar.lproj/LaunchScreen.strings create mode 100644 AutoLayout/AutoLayout/ar.lproj/Main.strings diff --git a/AutoLayout/AutoLayout.xcodeproj/project.pbxproj b/AutoLayout/AutoLayout.xcodeproj/project.pbxproj index e4e68bd..c2d60e9 100644 --- a/AutoLayout/AutoLayout.xcodeproj/project.pbxproj +++ b/AutoLayout/AutoLayout.xcodeproj/project.pbxproj @@ -10,6 +10,7 @@ 5319D0191CA6E8C1009E4AFA /* AdaptiveViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5319D0181CA6E8C1009E4AFA /* AdaptiveViewController.swift */; }; 5329F1161C79008300A35954 /* LayoutAnchorController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5329F1151C79008300A35954 /* LayoutAnchorController.swift */; }; 53358D6F1C80F0B100C1842A /* LayoutGuideController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53358D6E1C80F0B100C1842A /* LayoutGuideController.swift */; }; + 535078741CBAD4D400700816 /* RTLViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 535078731CBAD4D400700816 /* RTLViewController.swift */; }; 5362E7F81C53F1B200DAA88B /* CenterAlignViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5362E7F71C53F1B200DAA88B /* CenterAlignViewController.swift */; }; 53D6A5F11C53BF5200A81A14 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53D6A5F01C53BF5200A81A14 /* AppDelegate.swift */; }; 53D6A5F31C53BF5200A81A14 /* MasterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53D6A5F21C53BF5200A81A14 /* MasterViewController.swift */; }; @@ -26,6 +27,9 @@ 5319D0181CA6E8C1009E4AFA /* AdaptiveViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AdaptiveViewController.swift; sourceTree = ""; }; 5329F1151C79008300A35954 /* LayoutAnchorController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LayoutAnchorController.swift; sourceTree = ""; }; 53358D6E1C80F0B100C1842A /* LayoutGuideController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LayoutGuideController.swift; sourceTree = ""; }; + 535078731CBAD4D400700816 /* RTLViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RTLViewController.swift; sourceTree = ""; }; + 535078751CBADA0C00700816 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/Main.strings; sourceTree = ""; }; + 535078761CBADA0C00700816 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/LaunchScreen.strings; sourceTree = ""; }; 5362E7F71C53F1B200DAA88B /* CenterAlignViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = CenterAlignViewController.swift; sourceTree = ""; tabWidth = 4; }; 53D6A5ED1C53BF5200A81A14 /* AutoLayout.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AutoLayout.app; sourceTree = BUILT_PRODUCTS_DIR; }; 53D6A5F01C53BF5200A81A14 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -78,6 +82,7 @@ 53D6A5F01C53BF5200A81A14 /* AppDelegate.swift */, 53D6A5F21C53BF5200A81A14 /* MasterViewController.swift */, 5362E7F71C53F1B200DAA88B /* CenterAlignViewController.swift */, + 535078731CBAD4D400700816 /* RTLViewController.swift */, 5329F1151C79008300A35954 /* LayoutAnchorController.swift */, 53358D6E1C80F0B100C1842A /* LayoutGuideController.swift */, 53EDF4071C5D5C78009F91EA /* StackViewController.swift */, @@ -177,6 +182,7 @@ 53358D6F1C80F0B100C1842A /* LayoutGuideController.swift in Sources */, 5319D0191CA6E8C1009E4AFA /* AdaptiveViewController.swift in Sources */, 53EDF4081C5D5C78009F91EA /* StackViewController.swift in Sources */, + 535078741CBAD4D400700816 /* RTLViewController.swift in Sources */, 5329F1161C79008300A35954 /* LayoutAnchorController.swift in Sources */, 53EDF40C1C5D6641009F91EA /* UIViewController+addView.swift in Sources */, 5362E7F81C53F1B200DAA88B /* CenterAlignViewController.swift in Sources */, @@ -191,6 +197,7 @@ isa = PBXVariantGroup; children = ( 53D6A5F71C53BF5200A81A14 /* Base */, + 535078751CBADA0C00700816 /* ar */, ); name = Main.storyboard; sourceTree = ""; @@ -199,6 +206,7 @@ isa = PBXVariantGroup; children = ( 53D6A5FC1C53BF5200A81A14 /* Base */, + 535078761CBADA0C00700816 /* ar */, ); name = LaunchScreen.storyboard; sourceTree = ""; diff --git a/AutoLayout/AutoLayout/Base.lproj/Main.storyboard b/AutoLayout/AutoLayout/Base.lproj/Main.storyboard index 456e2a2..64d65bb 100644 --- a/AutoLayout/AutoLayout/Base.lproj/Main.storyboard +++ b/AutoLayout/AutoLayout/Base.lproj/Main.storyboard @@ -53,7 +53,7 @@ -