init project

This commit is contained in:
caiyuanpeng 2017-06-08 14:25:00 +08:00
parent 43ce37dc6e
commit 0eb21b5a86
86 changed files with 9137 additions and 0 deletions

View File

@ -0,0 +1,420 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
841B2C721E7BC5900084B37C /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 841B2C711E7BC5900084B37C /* AppDelegate.swift */; };
841B2C741E7BC5900084B37C /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 841B2C731E7BC5900084B37C /* ViewController.swift */; };
841B2C771E7BC5900084B37C /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 841B2C751E7BC5900084B37C /* Main.storyboard */; };
841B2C791E7BC5900084B37C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 841B2C781E7BC5900084B37C /* Assets.xcassets */; };
841B2C7C1E7BC5900084B37C /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 841B2C7A1E7BC5900084B37C /* LaunchScreen.storyboard */; };
8429690F1EE900EC0060C61D /* __Privates__Implementation__.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842968FD1EE900EC0060C61D /* __Privates__Implementation__.swift */; };
842969101EE900EC0060C61D /* Alert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842968FE1EE900EC0060C61D /* Alert.swift */; };
842969111EE900EC0060C61D /* AttStr.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842968FF1EE900EC0060C61D /* AttStr.swift */; };
842969121EE900EC0060C61D /* Button.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842969001EE900EC0060C61D /* Button.swift */; };
842969131EE900EC0060C61D /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842969011EE900EC0060C61D /* Color.swift */; };
842969141EE900EC0060C61D /* Cons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842969021EE900EC0060C61D /* Cons.swift */; };
842969151EE900EC0060C61D /* CPKStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842969031EE900EC0060C61D /* CPKStackView.swift */; };
842969161EE900EC0060C61D /* Font.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842969041EE900EC0060C61D /* Font.swift */; };
842969171EE900EC0060C61D /* ImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842969051EE900EC0060C61D /* ImageView.swift */; };
842969181EE900EC0060C61D /* Img.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842969061EE900EC0060C61D /* Img.swift */; };
842969191EE900EC0060C61D /* Label.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842969071EE900EC0060C61D /* Label.swift */; };
8429691A1EE900EC0060C61D /* Stack.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842969081EE900EC0060C61D /* Stack.swift */; };
8429691B1EE900EC0060C61D /* StaticTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842969091EE900EC0060C61D /* StaticTable.swift */; };
8429691C1EE900EC0060C61D /* Str.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8429690A1EE900EC0060C61D /* Str.swift */; };
8429691D1EE900EC0060C61D /* Styles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8429690B1EE900EC0060C61D /* Styles.swift */; };
8429691E1EE900EC0060C61D /* TextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8429690C1EE900EC0060C61D /* TextField.swift */; };
8429691F1EE900EC0060C61D /* TextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8429690D1EE900EC0060C61D /* TextView.swift */; };
842969201EE900EC0060C61D /* View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8429690E1EE900EC0060C61D /* View.swift */; };
842ADB421EB9C53D0031B56C /* ExamplesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842ADB411EB9C53D0031B56C /* ExamplesViewController.swift */; };
84B599B31EB1FF56005ADDFB /* BasicViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84B599B21EB1FF56005ADDFB /* BasicViewController.swift */; };
84B599B51EB1FF6C005ADDFB /* EnhancementViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84B599B41EB1FF6C005ADDFB /* EnhancementViewController.swift */; };
84B599B71EB32A9C005ADDFB /* StackViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84B599B61EB32A9C005ADDFB /* StackViewController.swift */; };
84B599B91EB32CE3005ADDFB /* appList.plist in Resources */ = {isa = PBXBuildFile; fileRef = 84B599B81EB32CE3005ADDFB /* appList.plist */; };
84B599BB1EB334EA005ADDFB /* StaticViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84B599BA1EB334EA005ADDFB /* StaticViewController.swift */; };
84CC7DF81EC94EC300817678 /* AppStoreViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CC7DF71EC94EC300817678 /* AppStoreViewController.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
841B2C6E1E7BC5900084B37C /* Cupcake-Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Cupcake-Demo.app"; sourceTree = BUILT_PRODUCTS_DIR; };
841B2C711E7BC5900084B37C /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
841B2C731E7BC5900084B37C /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
841B2C761E7BC5900084B37C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
841B2C781E7BC5900084B37C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
841B2C7B1E7BC5900084B37C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
841B2C7D1E7BC5900084B37C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
842968FD1EE900EC0060C61D /* __Privates__Implementation__.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = __Privates__Implementation__.swift; sourceTree = "<group>"; };
842968FE1EE900EC0060C61D /* Alert.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Alert.swift; sourceTree = "<group>"; };
842968FF1EE900EC0060C61D /* AttStr.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AttStr.swift; sourceTree = "<group>"; };
842969001EE900EC0060C61D /* Button.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Button.swift; sourceTree = "<group>"; };
842969011EE900EC0060C61D /* Color.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Color.swift; sourceTree = "<group>"; };
842969021EE900EC0060C61D /* Cons.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Cons.swift; sourceTree = "<group>"; };
842969031EE900EC0060C61D /* CPKStackView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CPKStackView.swift; sourceTree = "<group>"; };
842969041EE900EC0060C61D /* Font.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Font.swift; sourceTree = "<group>"; };
842969051EE900EC0060C61D /* ImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageView.swift; sourceTree = "<group>"; };
842969061EE900EC0060C61D /* Img.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Img.swift; sourceTree = "<group>"; };
842969071EE900EC0060C61D /* Label.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Label.swift; sourceTree = "<group>"; };
842969081EE900EC0060C61D /* Stack.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Stack.swift; sourceTree = "<group>"; };
842969091EE900EC0060C61D /* StaticTable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StaticTable.swift; sourceTree = "<group>"; };
8429690A1EE900EC0060C61D /* Str.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Str.swift; sourceTree = "<group>"; };
8429690B1EE900EC0060C61D /* Styles.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Styles.swift; sourceTree = "<group>"; };
8429690C1EE900EC0060C61D /* TextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextField.swift; sourceTree = "<group>"; };
8429690D1EE900EC0060C61D /* TextView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextView.swift; sourceTree = "<group>"; };
8429690E1EE900EC0060C61D /* View.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = View.swift; sourceTree = "<group>"; };
842ADB411EB9C53D0031B56C /* ExamplesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExamplesViewController.swift; sourceTree = "<group>"; };
84B599B21EB1FF56005ADDFB /* BasicViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BasicViewController.swift; sourceTree = "<group>"; };
84B599B41EB1FF6C005ADDFB /* EnhancementViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EnhancementViewController.swift; sourceTree = "<group>"; };
84B599B61EB32A9C005ADDFB /* StackViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StackViewController.swift; sourceTree = "<group>"; };
84B599B81EB32CE3005ADDFB /* appList.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = appList.plist; sourceTree = "<group>"; };
84B599BA1EB334EA005ADDFB /* StaticViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StaticViewController.swift; sourceTree = "<group>"; };
84CC7DF71EC94EC300817678 /* AppStoreViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppStoreViewController.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
841B2C6B1E7BC5900084B37C /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
841B2C651E7BC5900084B37C = {
isa = PBXGroup;
children = (
841B2C701E7BC5900084B37C /* Cupcake-Demo */,
841B2C6F1E7BC5900084B37C /* Products */,
);
sourceTree = "<group>";
};
841B2C6F1E7BC5900084B37C /* Products */ = {
isa = PBXGroup;
children = (
841B2C6E1E7BC5900084B37C /* Cupcake-Demo.app */,
);
name = Products;
sourceTree = "<group>";
};
841B2C701E7BC5900084B37C /* Cupcake-Demo */ = {
isa = PBXGroup;
children = (
842968FC1EE900EC0060C61D /* Cupcake */,
841B2C711E7BC5900084B37C /* AppDelegate.swift */,
841B2C731E7BC5900084B37C /* ViewController.swift */,
84B599B21EB1FF56005ADDFB /* BasicViewController.swift */,
84B599B41EB1FF6C005ADDFB /* EnhancementViewController.swift */,
84B599B61EB32A9C005ADDFB /* StackViewController.swift */,
84B599BA1EB334EA005ADDFB /* StaticViewController.swift */,
842ADB411EB9C53D0031B56C /* ExamplesViewController.swift */,
84CC7DF71EC94EC300817678 /* AppStoreViewController.swift */,
841B2C751E7BC5900084B37C /* Main.storyboard */,
841B2C781E7BC5900084B37C /* Assets.xcassets */,
841B2C7A1E7BC5900084B37C /* LaunchScreen.storyboard */,
84B599B81EB32CE3005ADDFB /* appList.plist */,
841B2C7D1E7BC5900084B37C /* Info.plist */,
);
path = "Cupcake-Demo";
sourceTree = "<group>";
};
842968FC1EE900EC0060C61D /* Cupcake */ = {
isa = PBXGroup;
children = (
8429690A1EE900EC0060C61D /* Str.swift */,
842968FF1EE900EC0060C61D /* AttStr.swift */,
842969041EE900EC0060C61D /* Font.swift */,
842969061EE900EC0060C61D /* Img.swift */,
842969011EE900EC0060C61D /* Color.swift */,
8429690E1EE900EC0060C61D /* View.swift */,
842969071EE900EC0060C61D /* Label.swift */,
842969051EE900EC0060C61D /* ImageView.swift */,
842969001EE900EC0060C61D /* Button.swift */,
8429690C1EE900EC0060C61D /* TextField.swift */,
8429690D1EE900EC0060C61D /* TextView.swift */,
842969091EE900EC0060C61D /* StaticTable.swift */,
842968FE1EE900EC0060C61D /* Alert.swift */,
842969081EE900EC0060C61D /* Stack.swift */,
842969021EE900EC0060C61D /* Cons.swift */,
8429690B1EE900EC0060C61D /* Styles.swift */,
842969031EE900EC0060C61D /* CPKStackView.swift */,
842968FD1EE900EC0060C61D /* __Privates__Implementation__.swift */,
);
name = Cupcake;
path = ../../Cupcake;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
841B2C6D1E7BC5900084B37C /* Cupcake-Demo */ = {
isa = PBXNativeTarget;
buildConfigurationList = 841B2C801E7BC5900084B37C /* Build configuration list for PBXNativeTarget "Cupcake-Demo" */;
buildPhases = (
841B2C6A1E7BC5900084B37C /* Sources */,
841B2C6B1E7BC5900084B37C /* Frameworks */,
841B2C6C1E7BC5900084B37C /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = "Cupcake-Demo";
productName = "Cupcake-Demo";
productReference = 841B2C6E1E7BC5900084B37C /* Cupcake-Demo.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
841B2C661E7BC5900084B37C /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0820;
LastUpgradeCheck = 0820;
ORGANIZATIONNAME = nerdycat;
TargetAttributes = {
841B2C6D1E7BC5900084B37C = {
CreatedOnToolsVersion = 8.2.1;
ProvisioningStyle = Automatic;
};
};
};
buildConfigurationList = 841B2C691E7BC5900084B37C /* Build configuration list for PBXProject "Cupcake-Demo" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 841B2C651E7BC5900084B37C;
productRefGroup = 841B2C6F1E7BC5900084B37C /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
841B2C6D1E7BC5900084B37C /* Cupcake-Demo */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
841B2C6C1E7BC5900084B37C /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
84B599B91EB32CE3005ADDFB /* appList.plist in Resources */,
841B2C7C1E7BC5900084B37C /* LaunchScreen.storyboard in Resources */,
841B2C791E7BC5900084B37C /* Assets.xcassets in Resources */,
841B2C771E7BC5900084B37C /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
841B2C6A1E7BC5900084B37C /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
842969201EE900EC0060C61D /* View.swift in Sources */,
842969141EE900EC0060C61D /* Cons.swift in Sources */,
84B599B51EB1FF6C005ADDFB /* EnhancementViewController.swift in Sources */,
84B599B31EB1FF56005ADDFB /* BasicViewController.swift in Sources */,
8429691E1EE900EC0060C61D /* TextField.swift in Sources */,
8429691C1EE900EC0060C61D /* Str.swift in Sources */,
842969171EE900EC0060C61D /* ImageView.swift in Sources */,
8429691B1EE900EC0060C61D /* StaticTable.swift in Sources */,
841B2C741E7BC5900084B37C /* ViewController.swift in Sources */,
842ADB421EB9C53D0031B56C /* ExamplesViewController.swift in Sources */,
8429691D1EE900EC0060C61D /* Styles.swift in Sources */,
842969151EE900EC0060C61D /* CPKStackView.swift in Sources */,
842969131EE900EC0060C61D /* Color.swift in Sources */,
84CC7DF81EC94EC300817678 /* AppStoreViewController.swift in Sources */,
842969191EE900EC0060C61D /* Label.swift in Sources */,
8429691F1EE900EC0060C61D /* TextView.swift in Sources */,
841B2C721E7BC5900084B37C /* AppDelegate.swift in Sources */,
84B599B71EB32A9C005ADDFB /* StackViewController.swift in Sources */,
842969121EE900EC0060C61D /* Button.swift in Sources */,
842969111EE900EC0060C61D /* AttStr.swift in Sources */,
842969161EE900EC0060C61D /* Font.swift in Sources */,
8429690F1EE900EC0060C61D /* __Privates__Implementation__.swift in Sources */,
8429691A1EE900EC0060C61D /* Stack.swift in Sources */,
842969181EE900EC0060C61D /* Img.swift in Sources */,
842969101EE900EC0060C61D /* Alert.swift in Sources */,
84B599BB1EB334EA005ADDFB /* StaticViewController.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
841B2C751E7BC5900084B37C /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
841B2C761E7BC5900084B37C /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
841B2C7A1E7BC5900084B37C /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
841B2C7B1E7BC5900084B37C /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
841B2C7E1E7BC5900084B37C /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
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_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
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 = 10.2;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
};
841B2C7F1E7BC5900084B37C /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
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_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
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 = 10.2;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
841B2C811E7BC5900084B37C /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
DEVELOPMENT_TEAM = "";
INFOPLIST_FILE = "Cupcake-Demo/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "nerdycat.Cupcake-Demo";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 3.0;
TARGETED_DEVICE_FAMILY = 1;
};
name = Debug;
};
841B2C821E7BC5900084B37C /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
DEVELOPMENT_TEAM = "";
INFOPLIST_FILE = "Cupcake-Demo/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "nerdycat.Cupcake-Demo";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 3.0;
TARGETED_DEVICE_FAMILY = 1;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
841B2C691E7BC5900084B37C /* Build configuration list for PBXProject "Cupcake-Demo" */ = {
isa = XCConfigurationList;
buildConfigurations = (
841B2C7E1E7BC5900084B37C /* Debug */,
841B2C7F1E7BC5900084B37C /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
841B2C801E7BC5900084B37C /* Build configuration list for PBXNativeTarget "Cupcake-Demo" */ = {
isa = XCConfigurationList;
buildConfigurations = (
841B2C811E7BC5900084B37C /* Debug */,
841B2C821E7BC5900084B37C /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 841B2C661E7BC5900084B37C /* Project object */;
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:Cupcake-Demo.xcodeproj">
</FileRef>
</Workspace>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<Bucket
type = "1"
version = "2.0">
</Bucket>

View File

@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0820"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "841B2C6D1E7BC5900084B37C"
BuildableName = "Cupcake-Demo.app"
BlueprintName = "Cupcake-Demo"
ReferencedContainer = "container:Cupcake-Demo.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "841B2C6D1E7BC5900084B37C"
BuildableName = "Cupcake-Demo.app"
BlueprintName = "Cupcake-Demo"
ReferencedContainer = "container:Cupcake-Demo.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "841B2C6D1E7BC5900084B37C"
BuildableName = "Cupcake-Demo.app"
BlueprintName = "Cupcake-Demo"
ReferencedContainer = "container:Cupcake-Demo.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "841B2C6D1E7BC5900084B37C"
BuildableName = "Cupcake-Demo.app"
BlueprintName = "Cupcake-Demo"
ReferencedContainer = "container:Cupcake-Demo.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict>
<key>Cupcake-Demo.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>841B2C6D1E7BC5900084B37C</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>

View File

@ -0,0 +1,44 @@
//
// AppDelegate.swift
// Cupcake-Demo
//
// Created by nerdycat on 2017/3/17.
// Copyright © 2017 nerdycat. All rights reserved.
//
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
func applicationWillResignActive(_ application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
}
func applicationDidEnterBackground(_ application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(_ application: UIApplication) {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
}

View File

@ -0,0 +1,100 @@
//
// AppStoreViewController.swift
// Cupcake-Demo
//
// Created by nerdycat on 2017/5/15.
// Copyright © 2017 nerdycat. All rights reserved.
//
import UIKit
class AppStoreCell: UITableViewCell {
var iconView: UIImageView!
var actionButton: UIButton!
var indexLabel, titleLabel, categoryLabel: UILabel!
var ratingLabel, countLabel, iapLabel: UILabel!
func update(app: Dictionary<String, Any>, index: Int) {
indexLabel.str(index + 1)
iconView.img(app["iconName"] as! String)
titleLabel.text = app["title"] as? String
categoryLabel.text = app["category"] as? String
countLabel.text = Str("(%@)", app["commentCount"] as! NSNumber)
iapLabel.isHidden = !(app["iap"] as! Bool)
let rating = (app["rating"] as! NSNumber).intValue
var result = ""
for i in 0..<5 { result = result + (i < rating ? "" : "") }
ratingLabel.text = result
let price = app["price"] as! String
actionButton.str( price.characters.count > 0 ? "$" + price : "GET")
}
func setupUI() {
indexLabel = Label.font(17).color("darkGray").align(.center).pin(.w(44))
iconView = ImageView.pin(64, 64).radius(10).border(1.0 / UIScreen.main.scale, "#CCC")
titleLabel = Label.font(15).lines(2)
categoryLabel = Label.font(13).color("darkGray")
ratingLabel = Label.font(11).color("orange")
countLabel = Label.font(11).color("darkGray")
actionButton = Button.font("15").color("#0065F7").border(1, "#0065F7").radius(3)
actionButton.highColor("white").highBg("#0065F7").padding(5, 10)
iapLabel = Label.font(9).color("darkGray").lines(2).str("In-App\nPurchases").align(.center)
let ratingStack = HStack(ratingLabel, countLabel).gap(5)
let midStack = VStack(titleLabel, categoryLabel, ratingStack).gap(4)
let actionStack = VStack(actionButton, iapLabel).gap(4).align(.center)
HStack(indexLabel, iconView, 10, midStack, "<-->", 10, actionStack).embedIn(self.contentView, 10, 0, 10, 15)
}
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupUI()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class AppStoreViewController: UITableViewController {
var appList: Array<Dictionary<String, Any>>!
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return appList.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! AppStoreCell
let app = self.appList[indexPath.row]
cell.update(app: app, index: indexPath.row)
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.estimatedRowHeight = 84
self.tableView.register(AppStoreCell.self, forCellReuseIdentifier: "cell")
let path = Bundle.main.path(forResource: "appList", ofType: "plist")
appList = NSArray(contentsOfFile: path!) as? Array<Dictionary<String, Any>>
for _ in 1..<5 { appList.append(contentsOf: appList) }
}
}

View File

@ -0,0 +1,48 @@
{
"images" : [
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "3x"
},
{
"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"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Bitmoji@2x.jpeg",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "fivehights@2x.jpeg",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "mario@2x.jpeg",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "messenger@2x.jpeg",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "minecraft@2x.jpeg",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "snapchat@2x.jpeg",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "youtube@2x.jpeg",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -0,0 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "customers.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "dashboard.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "entities.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 891 B

View File

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "jobsites.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "requests.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "shubox.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

View File

@ -0,0 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "airplane.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "display.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "disturb.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "general.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "wlan.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "arrow@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 323 B

View File

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "candle@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "cat.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11134" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11106"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/>
<viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11762" systemVersion="16A323" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="1XR-RW-jgm">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="ViewController" customModule="Cupcake_Demo" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="64" width="375" height="603"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</view>
<navigationItem key="navigationItem" id="EtQ-cN-G4e"/>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1028" y="-12"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="gG6-fc-595">
<objects>
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="1XR-RW-jgm" sceneMemberID="viewController">
<toolbarItems/>
<navigationBar key="navigationBar" contentMode="scaleToFill" misplaced="YES" translucent="NO" id="c6y-kC-3WF">
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<nil name="viewControllers"/>
<toolbar key="toolbar" opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="qfR-vg-40l">
<rect key="frame" x="0.0" y="623" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
</toolbar>
<connections>
<segue destination="BYZ-38-t0r" kind="relationship" relationship="rootViewController" id="rfl-gU-ekM"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="gi2-Rh-gnb" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="236" y="-12"/>
</scene>
</scenes>
</document>

View File

@ -0,0 +1,60 @@
//
// BasicViewController.swift
// Cupcake-Demo
//
// Created by nerdycat on 2017/4/27.
// Copyright © 2017 nerdycat. All rights reserved.
//
import UIKit
class BasicViewController: BaseViewController {
override func setupUI() {
//View
let box = View.bg("red").pin(20, 20, 50, 50).border(4).onClick({ _ in
print("box")
})
let circle = View.bg("blue").makeCons({
$0.left.top.equal(box).right.top.offset(20, 0)
$0.size.equal(50, 50)
}).radius(-1).shadow(0.7)
//Label
let label = Label.str("This is a normal Label.").font(17).color("66,66,66").pin(.xy(20, 100))
var att = AttStr("This is an attributed Label.").font(17).color("#3A3A3A")
att.select("attributed Label").underline().select(.range(8, 2)).color("red")
let attLabel = Label.str(att).pin(.xy(20, 130))
//ImageView
let candle = ImageView.img("candle").pin(.xy(20, 180))
let tintedCandle = ImageView.img("$candle").tint("#86BD5B").pin(.xy(70, 180))
//Button
let done = Button.str("Done").padding(5, 10).bg("red").highBg("blue").pin(.xy(20, 260)).radius(6)
att = AttStr("Friends\n1024").font(13).select(.number).font("17")
let friends = Button.str(att).border(1).highBg("darkGray,0.2").makeCons({
$0.left.centerY.equal(done).right.centerY.offset(20, 0)
}).padding(10).lines()
let more = Button.str("More").img("arrow").color("black").gap(5).reversed().makeCons({ make in
make.left.centerY.equal(friends).right.centerY.offset(20, 0)
}).onClick({ _ in
Alert.title("Alert").message("You just clicked the button.").action("OK").show()
}).touchInsets(-20)
self.view.addSubviews(box, circle, label, attLabel, candle, tintedCandle, done, friends, more)
}
}

View File

@ -0,0 +1,67 @@
//
// EnhancementViewController.swift
// Cupcake-Demo
//
// Created by nerdycat on 2017/4/27.
// Copyright © 2017 nerdycat. All rights reserved.
//
import UIKit
class EnhancementViewController: BaseViewController {
override func setupUI() {
//Label with lineSpacing and link
let str = "With #Cupcake, @Label now support lineSpacing and Link handing. Additionally, @TextField and @TextView both have the abilities to set max length and placeholder. "
let attStr = AttStr(str).select("Link handing", .hashTag, .nameTag).link()
let label = Label.str(attStr).lineGap(10).lines().onLink({ text in
print(text)
}).embedIn(self.view, 15, 15, 15)
//TextField with maxLength
let nameField = TextField.pin(40).padding(0, 8).border(1).maxLength(5).hint("normal")
let codeField = TextField.pin(40).padding(0, 8).border(1).maxLength(4).hint("secure").keyboard(.numberPad).secure()
nameField.makeCons({ make in
make.top.equal(label).bottom.offset(20)
make.left.offset(15)
}).onFinish({ _ in
codeField.becomeFirstResponder()
})
codeField.makeCons({ make in
make.top.equal(nameField)
make.left.equal(nameField).right.offset(10)
make.right.offset(-15)
make.width.equal(nameField)
}).onChange({ [unowned self] codeField in
if codeField.text?.characters.count == 4 {
self.view.viewWithTag(101)?.becomeFirstResponder()
}
})
//TextView with placeholder and maxLength
let textView = TextView.padding(8).maxLength(40).border(1).makeCons({ make in
make.left.right.offset(15, -15)
make.top.equal(nameField).bottom.offset(10)
make.height.equal(100)
}).hint("comment")
textView.tag = 101
self.view.addSubviews(nameField, codeField, textView)
self.view.onClick({ view in
view.endEditing(true)
})
}
}

View File

@ -0,0 +1,183 @@
//
// ExamplesViewController.swift
// Cupcake-Demo
//
// Created by nerdycat on 2017/5/3.
// Copyright © 2017 nerdycat. All rights reserved.
//
import UIKit
class ExamplesViewController: BaseViewController {
override func setupUI() {
let titles = ["Signup", "Shubox", "Dashboard", "AppStore"]
PlainTable(titles).embedIn(self.view).onClick({ [unowned self] row in
var target: UIViewController!
switch row.indexPath.row {
case 0: target = SignupViewController()
case 1: target = ShuboxViewController()
case 2: target = DashboardViewController()
case 3: target = AppStoreViewController()
default: break
}
target.title = row.cell.textLabel?.text
self.push(target)
})
}
}
class SignupViewController: BaseViewController {
override func setupUI() {
//https://dribbble.com/shots/3346069-001-Log-in-Sing-up
self.view.bg("#184367")
let iphone5 = UIScreen.main.bounds.width == 320
let card = View.bg("white").radius(4).embedIn(self.view, iphone5 ? 40 : 60, 30)
let inputStyle = Styles.pin(40, .lowHugging).font(14)
Styles("ruler").bg("#C7C7CD").pin(1)
let name = Label.str("FULL NAME").font(17)
let nameField = TextField.hint("Enter your full name").maxLength(15).styles(inputStyle)
let line1 = View.styles("ruler")
let email = Label.str("E-MAIL").font(17)
let emailField = TextField.hint("Your E-mail goes here").keyboard(.emailAddress).styles(inputStyle)
let line2 = View.styles("ruler")
let pw = Label.str("Password").font(17)
let pwField = TextField.hint("Enter your password").maxLength(10).secure().styles(inputStyle)
let line3 = View.styles("ruler")
let statement = Label.str("☑️ I agree all statements in").color("lightGray").font(12)
let term = Button.str( AttStr("Terms of service").color("#8DD6E5").font("12").underline() ).margin(0, -22)
let login = Button.str("LOG IN").font(15).bg("#4A96E3").pin(44, .lowHugging).radius(4)
VStack(name, nameField, line1, 30, email, emailField, line2, 30,
pw, pwField, line3, 30, statement , term, "<-->", login).embedIn(card, iphone5 ? 30: 50, 30, 30, 30)
Button.str("X").font(13).color("#D2E0E8").bg("#EDF2F5").radius(-1).pin(16, 16, .maxX(-10), .y(10)).addTo(card)
Button.str("HELP").font(13).padding(10).pin(.centerX(0), .maxY(-5)).addTo(self.view)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
}
class ShuboxViewController: BaseViewController {
override func setupUI() {
//https://dribbble.com/shots/3462307-Responsive-Shubox
let logo = Label.str("Shubox").color("#FC6560").font("30")
let navStyle = Styles.color("darkGray").highColor("red").font(15)
Styles("btn").color("#FC6560").highColor("white").highBg("#FC6560").font("15").padding(12, 30).border(3, "#FC6560").radius(-1)
let pricing = Button.str("Pricing").styles(navStyle)
let docs = Button.str("Docs").styles(navStyle)
let demos = Button.str("Demos").styles(navStyle)
let blog = Button.str("Blog").styles(navStyle)
let signIn = Button.str("Sign In").styles(navStyle).color("#FC6560")
let nav = HStack(pricing, docs, demos, blog, signIn).gap(15)
let simpleFast = Label.str("Simple. Fast. \nCustomizable.").color("#7C60CE").font("30").lines().align(.center)
let upload = Label.str("Upload images from your web app directly to Amazon S3.").color("#BE9FDE").font(15).lines().align(.center)
let startTrial = Button.str("Start Your Free Trial").styles("btn")
let image = ImageView.img("shubox").pin(.ratio)
let items: [Any] = [logo, 15, nav, 45, simpleFast, 15, upload, 30, startTrial, "<-->", image]
VStack(items).align(.center).embedIn(self.view, 10, 15, 0, 15)
}
}
class DashboardViewController: BaseViewController {
class DashButton: UIButton {
let subtitle: String!
override func setTitle(_ title: String?, for state: UIControlState) {
if state == .normal {
let att = AttStr(
AttStr(title).font("18").color("#181D42"), "\n",
AttStr(subtitle).font(11).color("darkGray")
).lineGap(3).align(.center)
self.str(att)
} else {
super.setTitle(title, for: state)
}
}
init(_ subtitle: String) {
self.subtitle = subtitle
super.init(frame: CGRect.zero)
self.pin(.lowHugging, 80).lines().gap(15).highBg("lightGray,0.2")
self.adjustsImageWhenHighlighted = false
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
override func setupUI() {
//https://dribbble.com/shots/3441336-Mobile-dashboard-items-list-light-design
let logo = ImageView.img("dashboard")
let profile = Button.img("customers")
let header = HStack(logo, "<-->", profile)
let welcome = Label.str("Welcome back,\nAndrew").font(18).color("darkGray").lines(2)
let number = Label.str(83).font("AvenirNext-Bold,90").color("#181D42")
let dash = View.pin(12, 3).bg("#DE2F43").radius(-1)
let jobsites = Label.str("Jobsites\nrequests").font(13).color("darkGray").lines(2)
let requests = HStack(number, 20, VStack(dash, 3, jobsites)).align(.baseline)
let jobBtn = DashButton("Jobsites").img("jobsites").str(346)
let requestsBtn = DashButton("Requests").img("requests").str(83)
let customersBtn = DashButton("Customers").img("customers").str(12)
let entitiesBtn = DashButton("Entities").img("entities").str(22).makeCons({
$0.width.equal(jobBtn)
$0.width.equal(requestsBtn)
$0.width.equal(customersBtn)
})
let tiles = View.margin(0, 10)
VStack(HStack(jobBtn, requestsBtn), HStack(customersBtn, entitiesBtn)).embedIn(tiles)
VStack(header, 30, welcome, 15, requests, 40, tiles).embedIn(self.view, 30, 30, 30)
let horLine = View.bg("lightGray,0.4").pin(1).makeCons({
$0.left.bottom.equal(jobBtn)
$0.width.equal(jobBtn).multiply(2)
})
let verLine = View.bg("lightGray,0.4").pin(.w(1)).makeCons({
$0.top.right.equal(jobBtn)
$0.height.equal(jobBtn).multiply(2)
})
self.view.addSubviews(horLine, verLine)
}
}

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
</array>
</dict>
</plist>

View File

@ -0,0 +1,63 @@
//
// StackViewController.swift
// Cupcake-Demo
//
// Created by nerdycat on 2017/4/28.
// Copyright © 2017 nerdycat. All rights reserved.
//
import UIKit
class StackViewController: BaseViewController {
var stack: CPKStackView!
func randomView() -> UIView {
let width = CGFloat(arc4random_uniform(30) + 20)
let height = CGFloat(arc4random_uniform(30) + 20)
let view = View.bg("random").pin(.wh(width, height)).onClick({
$0.removeFromSuperview()
})
return view
}
func setupButtons() {
let s = Styles.padding(3, 5).bg("red").font(15)
let addView = Button.styles(s).str("add").onClick({[unowned self] _ in
self.stack.addArrangedSubview(item: self.randomView())
})
let attachGap = Button.styles(s).str("attachGap").onClick({[unowned self] _ in
self.stack.addArrangedSubview(item: arc4random_uniform(30))
})
let gap = Button.styles(s).str("gap").onClick({[unowned self] _ in
self.stack.spacing = CGFloat(arc4random_uniform(30))
})
let align = Button.styles(s).str("align").onClick({[unowned self] _ in
var next = self.stack.alignment.rawValue + 1
if next > 2 { next = 0 }
let alignment = CPKStackAlignment(rawValue: next)!
self.stack.alignment = alignment
})
let axis = Button.styles(s).str("axis").onClick({[unowned self] _ in
self.stack.axis = self.stack.axis == .vertical ? .horizontal : .vertical
})
HStack("<-->", addView, attachGap, gap, align, axis, "<-->").gap(8).embedIn(self.view, nil, 0, 0, 0)
}
override func setupUI() {
super.setupUI()
Label.str("click view to remove").font(15).color("lightGray").align(.center).embedIn(self.view, 10, 0, 0)
stack = VStack(randomView()).border(1).pin(.center).addTo(self.view)
setupButtons()
}
}

View File

@ -0,0 +1,282 @@
//
// StaticViewController.swift
// Cupcake-Demo
//
// Created by nerdycat on 2017/4/28.
// Copyright © 2017 nerdycat. All rights reserved.
//
import UIKit
class StaticViewController: BaseViewController {
override func setupUI() {
weak var weakSelf = self
GroupTable(
Section(
Row.img("airplane").str("Airplane Mode").switchOn(false).onChange({ row in
print(row.switchView.isOn)
}),
Row.img("wlan").str("WLAN").detail("Not connected").arrow().onClick({ _ in
weakSelf?.push(WLANViewController())
}),
Row.img("disturb").str("Do Not Disturb").arrow().onClick({ _ in
weakSelf?.push(DisturbViewController())
})
),
Section(
Row.img("general").str("General").arrow().custom({ row in
let badge = Button.pin(22, 22, .maxX(-5), .centerY(0)).radius(-1).str("1").font(14).bg("red")
badge.isUserInteractionEnabled = false
row.cell.contentView.addSubview(badge)
}).onClick({ _ in
weakSelf?.push(GeneralViewController())
}),
Row.img("display").str("Display & Brightness").arrow().onClick({ _ in
}).onClick({ _ in
weakSelf?.push(DisplayViewController())
})
)
).embedIn(self.view)
}
}
class WLANViewController: BaseViewController {
override func setupUI() {
let footer = "Known networks will be joined automatically. If no known networks are available, you will have to manually select a network."
GroupTable(
Section(
Row.str("WLAN").switchOn().onChange({ row in
print(row.switchView.isOn)
})
),
Section(
Row.str("Wireless 1").detail("\u{0001F512} \u{268C}").accessory(.detailButton).onButton({ _ in
Alert.title("Wireless 1").message("detail button tapped").action("OK").show()
}).onClick({ _ in
print("Wireless 1")
}),
Row.str("Wireless 2").detail("\u{0001F513} \u{2630}").accessory(.detailButton).onButton({ _ in
Alert.title("Wireless 2").message("detail button tapped").action("OK", {
print("OK")
}).cancel("Cancel").show()
}).onClick({ _ in
print("Wireless 2")
})
).header("CHOOSE A NETWORK..."),
Section(
Row.str("Ask to Join Networks").switchOn(false).onChange({ row in
print(row.switchView.isOn)
})
).footer(footer)
).embedIn(self.view)
}
}
class DisturbViewController: BaseViewController {
override func setupUI() {
let footer1 = "When Do Not Disturb is enabled calls and alerts that arrive while locked will be silenced, and a moon icon will appear in the status bar."
let footer2 = "Incoming calls and notifications will be silenced while iPhone is either locked or unlocked."
GroupTable(
Section(
Row.str("Manual").switchOn(false).onChange({ row in
print(row.switchView.isOn)
})
).footer(footer1),
Section(
Row.str("Always").check().onClick({ _ in
print("always")
}),
Row.str("Only while iPhone is locked").onClick({ _ in
print("only locked")
})
).singleCheck().header("SILENCE:").footer(footer2)
).embedIn(self.view)
}
}
class GeneralViewController: BaseViewController {
var table: StaticTableView!
override func setupUI() {
weak var weakSelf = self
table = GroupTable(
Section(
Row.str("Name").arrow().onClick({ _ in
weakSelf?.push(NameViewController())
})
),
Section(
Row.str("Software Update").arrow().onClick({ _ in
weakSelf?.push(UpdateViewController())
})
)
).embedIn(self.view)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
table.update(detail: NameViewController.name, at: IndexPath(row: 0, section: 0))
}
}
class DisplayViewController: BaseViewController {
var autoLockRow: StaticRow!
override func setupUI() {
autoLockRow = Row.str("Auto-Lock").detail("").arrow().onClick({ [unowned self] _ in
self.push(AutoLockViewController())
})
GroupTable(
Section(
Row.custom({ row in
let slider = UISlider().embedIn(row.cell.contentView, 0, 15)
slider.minimumValueImage = Img("$candle").resize(0.5)
slider.maximumValueImage = Img("$candle").resize(0.7)
})
).header("BRIGHTNESS"),
Section(autoLockRow)
).embedIn(self.view)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
autoLockRow.detail(AutoLockViewController.selectedOption())
}
}
class NameViewController: BaseViewController {
static var name = "My-iPhone"
var textField: UITextField!
override func setupUI() {
let name = NameViewController.name
GroupTable(
Row.custom({ row in
self.textField = TextField.str(name).hint(name).clearMode(.whileEditing).onFinish({ [unowned self] textField in
NameViewController.name = textField.text ?? ""
self.navigationController!.popViewController(animated: true)
}).embedIn(row.cell.contentView, 0, 15)
})
).embedIn(self.view)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
textField.becomeFirstResponder()
}
}
class UpdateViewController: BaseViewController {
override func setupUI() {
let desc = "iOS 10.3.1 introduces new features including the ability to locate AirPods using Find my iPhone and more ways to use Siri with payment, ride booking and automaker apps.\n\nFor information on the security content of Apple software update, please visit this website: https://support.apple.com/kb/HT201222"
let attDesc = AttStr(desc).font(15).select(.url).link()
GroupTable(
Section(
Row.custom({ row in
let icon = ImageView.pin(60, 60).img("general")
let title = Label.str("iOS 10.3.1").font("15")
let cops = Label.str("Apple Inc.").font(13)
let status = Label.str("Downloaded").font(13)
let desc = Label.str(attDesc).lines().onLink({ text in
print(text)
})
VStack(
HStack( icon, VStack(title, cops, status).gap(2) ).gap(10),
12,
desc
).embedIn(row.cell.contentView, 10, 15)
}).height(-1),
Row.str("Learn More").arrow().onClick({ _ in
print("learn more")
})
),
Section(
Row.str(AttStr("Install Now").color("#157EFB")).onClick({ _ in
print("install")
})
)
).embedIn(self.view)
}
}
class AutoLockViewController: BaseViewController {
static let options = ["30 Seconds", "1 Minute", "2 Minutes", "3 Minutes", "4 Minutes", "Never"]
static var optionIndex = 1
var tableView: StaticTableView!
class func selectedOption() -> String {
return options[optionIndex]
}
override func setupUI() {
tableView = GroupTable(
Section(AutoLockViewController.options).singleCheck()
).custom({ row in
if row.indexPath.row == AutoLockViewController.optionIndex {
row.check()
}
}).embedIn(self.view)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillAppear(animated)
AutoLockViewController.optionIndex = tableView.checkedIndexPaths.first!.row
}
}
class BaseViewController: UIViewController {
func push(_ vc: UIViewController) {
self.navigationController?.pushViewController(vc, animated: true)
}
func setupUI() {
}
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .white
setupUI()
}
}

View File

@ -0,0 +1,40 @@
//
// ViewController.swift
// Cupcake-Demo
//
// Created by nerdycat on 2017/3/17.
// Copyright © 2017 nerdycat. All rights reserved.
//
import UIKit
class ViewController: BaseViewController {
override func setupUI() {
self.title = "Cupcake"
let titles = ["Basic", "Enhancement", "Stack", "StaticTable", "Examples"]
PlainTable(titles).embedIn(self.view).onClick({ [unowned self] row in
var target: UIViewController!
switch row.indexPath.row {
case 0: target = BasicViewController()
case 1: target = EnhancementViewController()
case 2: target = StackViewController()
case 3: target = StaticViewController()
case 4: target = ExamplesViewController()
default: break
}
target.title = row.cell.textLabel?.text
self.push(target)
})
}
}

View File

@ -0,0 +1,118 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
<dict>
<key>iconName</key>
<string>mario.jpeg</string>
<key>title</key>
<string>Super Mario Run</string>
<key>category</key>
<string>Games</string>
<key>rating</key>
<integer>3</integer>
<key>commentCount</key>
<integer>6053</integer>
<key>price</key>
<string></string>
<key>iap</key>
<true/>
</dict>
<dict>
<key>iconName</key>
<string>bitmoji.jpeg</string>
<key>title</key>
<string>Bitmoji - Your Personal Emoji</string>
<key>category</key>
<string>Utilities</string>
<key>rating</key>
<integer>4</integer>
<key>commentCount</key>
<integer>11</integer>
<key>price</key>
<string></string>
<key>iap</key>
<false/>
</dict>
<dict>
<key>iconName</key>
<string>messenger.jpeg</string>
<key>title</key>
<string>Messenger</string>
<key>category</key>
<string>Social Netowrking</string>
<key>rating</key>
<integer>3</integer>
<key>commentCount</key>
<integer>2052</integer>
<key>price</key>
<string></string>
<key>iap</key>
<false/>
</dict>
<dict>
<key>iconName</key>
<string>youtube.jpeg</string>
<key>title</key>
<string>YouTube - Watch and Share Videos, Music &amp; Clips Support</string>
<key>category</key>
<string>Photo &amp; Video</string>
<key>rating</key>
<real>3.5</real>
<key>commentCount</key>
<integer>38</integer>
<key>price</key>
<string></string>
<key>iap</key>
<true/>
</dict>
<dict>
<key>iconName</key>
<string>snapchat.jpeg</string>
<key>title</key>
<string>Snapchat</string>
<key>category</key>
<string>Photo &amp; Video</string>
<key>rating</key>
<integer>3</integer>
<key>commentCount</key>
<integer>33</integer>
<key>price</key>
<string></string>
<key>iap</key>
<true/>
</dict>
<dict>
<key>iconName</key>
<string>fivenights.jpeg</string>
<key>title</key>
<string>Five Nights at Freddy&apos;s: Sister Location Support</string>
<key>category</key>
<string>Games</string>
<key>rating</key>
<real>4.5</real>
<key>commentCount</key>
<integer>814</integer>
<key>price</key>
<string>2.99</string>
<key>iap</key>
<false/>
</dict>
<dict>
<key>iconName</key>
<string>minecraft.jpeg</string>
<key>title</key>
<string>Minecraft: Pocket Edition Support</string>
<key>category</key>
<string>Games</string>
<key>rating</key>
<real>4.5</real>
<key>commentCount</key>
<integer>869</integer>
<key>price</key>
<string>0.99</string>
<key>iap</key>
<true/>
</dict>
</array>
</plist>

21
Cupcake.podspec Normal file
View File

@ -0,0 +1,21 @@
Pod::Spec.new do |s|
s.name = "Cupcake"
s.version = "0.1.0"
s.summary = "An easy way to create and layout UI components for iOS."
s.description = <<-DESC
An easy way to create and layout UI components for iOS. Written in Swift.
DESC
s.homepage = "https://github.com/nerdycat/Cupcake"
s.license = "MIT"
s.author = { "nerdycat" => "nerdymozart@gmail.com" }
s.platform = :ios, "8.0"
s.source = { :git => "https://github.com/nerdycat/Cupcake.git", :tag => "#{s.version}" }
s.requires_arc = true
s.source_files = "Cupcake/*.swift"
end

120
Cupcake/Alert.swift Normal file
View File

@ -0,0 +1,120 @@
//
// Alert.swift
// Cupcake
//
// Created by nerdycat on 2017/3/28.
// Copyright © 2017 nerdycat. All rights reserved.
//
import UIKit
/**
* An easy way to Create Alert and ActionSheet.
* Usages:
Alert.title("Title").message("Message go here").cancel("Cancel").action("OK", {
print("OK")
}).show()
ActionSheet.title("Title").message("Message go here").action("Action1", {
print("Action1")
}).action("Action2", {
print("Action2")
}).destructive("Delete", {
print("Delete")
}).cancel("Cancel").show()
*/
public var Alert: AlertMaker {
return AlertMaker(style: .alert)
}
public var ActionSheet: AlertMaker {
return AlertMaker(style: .actionSheet)
}
public extension AlertMaker {
/**
* Setting Alert/ActionSheet title
* Usages:
.title("Title")
*/
@discardableResult public func title(_ title: Any) -> Self {
self.cpkTitle = title
return self
}
/**
* Setting Alert/ActionSheet message
* Usages:
.message("Message go here")
*/
@discardableResult public func message(_ message: Any) -> Self {
self.cpkMessage = message
return self
}
/**
* Setting tintColor
* tint use Color() internally, so it can take any kind of values that Color() supported.
* See Color.swift for more information.
* Usages:
.tint("red")
.tint("#00F")
...
*/
@discardableResult public func tint(_ tint: Any) -> Self {
self.cpkTint = tint
return self
}
/**
* Adding action button with title and a optional callback handler.
* You can have multiply action button at the same time.
* Usages:
.action("Option1")
.action("Option2", { /* do something */ })
*/
@discardableResult public func action(_ title: Any, _ callback: (()->())? = nil) -> Self {
self.cpk_addAction(style: .default, title: title, handler: callback)
return self
}
/**
* Adding cancel button with title and a optional callback handler.
* You can only have one cancel button.
* Usages:
.cancel("Cancel")
.cancel("Cancel", { /* do something */ }
*/
@discardableResult public func cancel(_ title: Any, _ callback: (()->())? = nil) -> Self {
self.cpk_addAction(style: .cancel, title: title, handler: callback)
return self
}
/**
* Adding destructive button with title and a optional callback handler.
* You can have multiply action button at the same time.
* Usages:
.destructive("Delete")
.destructive("Delete", { /* do someting */ }
*/
@discardableResult public func destructive(_ title: Any, _ callback: (()->())? = nil) -> Self {
self.cpk_addAction(style: .destructive, title: title, handler: callback)
return self
}
/**
* Present Alert/ActionSheet
* You must call this method in the end to make Alert/ActionSheet visible.
* Usages:
.show() //present in the top visible controller
.show(someController) //present in someController
*/
public func show(_ inside: UIViewController? = nil) {
self.cpk_present(inside)
}
}

359
Cupcake/AttStr.swift Normal file
View File

@ -0,0 +1,359 @@
//
// AttStr.swift
// Cupcake
//
// Created by nerdycat on 2017/3/17.
// Copyright © 2017 nerdycat. All rights reserved.
//
import UIKit
/**
* Create NSMutableAttributedString with String or UIImage.
* Usages:
AttStr("hello world").select("world").color("red")
AttStr("A big smile ", Img("smile"), " !!") //insert image attachment
*/
public func AttStr(_ objects: Any?...) -> NSMutableAttributedString {
let result = NSMutableAttributedString()
for object in objects {
var subAtt: NSAttributedString? = nil
if object is NSAttributedString {
subAtt = object as? NSAttributedString
} else if object is String {
subAtt = NSAttributedString(string: object as! String)
} else if object is UIImage {
let attachment = NSTextAttachment()
attachment.image = object as? UIImage
subAtt = NSAttributedString(attachment: attachment)
}
if subAtt != nil {
result.append(subAtt!)
}
}
result.cpk_select(range: NSMakeRange(0, result.length), setFlag: false)
return result
}
extension NSMutableAttributedString {
/**
* NSFontAttributeName
* font use Font() internally, so it can take any kind of values that Font() supported.
* See Font.siwft for more information.
* Usages:
.font(15)
.font("20")
.font("body")
.font(someLabel.font)
...
*/
@discardableResult public func font(_ style: Any) -> Self {
cpk_addAttribute(name: NSFontAttributeName, value: Font(style))
return self
}
/**
* NSForegroundColorAttributeName
* color use Color() internally, so it can take any kind of values that Color() supported.
* See Color.swift for more information.
* Usages:
.color(@"red")
.color(@"#F00")
.color(@"255,0,0")
.color(someView.backgroundColor)
...
*/
@discardableResult public func color(_ any: Any) -> Self {
cpk_addAttribute(name: NSForegroundColorAttributeName, value: Color(any)!)
return self
}
/**
* NSBackgroundColorAttributeName
* bg use Color() internally, so it can take any kind of values that Color() supported.
* See Color.swift for more information.
* Usages:
.bg(@"red")
.bg(@"#F00")
.bg(@"255,0,0")
.bg(someView.backgroundColor)
.bg("cat") //using image
...
*/
@discardableResult public func bg(_ any: Any) -> Self {
cpk_addAttribute(name: NSBackgroundColorAttributeName, value: Color(any) ?? Color(Img(any))!)
return self
}
/**
* NSUnderlineStyleAttributeName, NSUnderlineColorAttributeName
* underline's second argument use Color() internally, so it can take any kind of values that Color() supported.
* See Color.swift for more information.
* Usages:
.underline() //single underline with the same color of text
.underline(.patternDash) //dash underline with the same color of text
.underline("red") //single underline with red color
.underline(.styleDouble, "red") //double underline with red color
...
*/
@discardableResult public func underline(_ style: NSUnderlineStyle = .styleSingle, _ color: Any? = nil) -> Self {
var styles = NSNumber(value: style.rawValue)
if style != .styleNone && style != .styleSingle && style != .styleThick && style != .styleDouble {
styles = NSNumber(value: style.rawValue | NSUnderlineStyle.styleSingle.rawValue)
}
cpk_addAttribute(name: NSUnderlineStyleAttributeName, value: styles)
if let underlineColor = Color(color) {
cpk_addAttribute(name: NSUnderlineColorAttributeName, value: underlineColor)
}
return self
}
@discardableResult public func underline(_ color: Any) -> Self {
return underline(.styleSingle, color)
}
/**
* NSStrikethroughStyleAttributeName, NSStrikethroughColorAttributeName
* strikethrough's second argument use Color() internally, so it can take any kind of values that Color() supported.
* See Color.swift for more information.
* Usages:
.strikethrough() //single strikethrough with the same color of text
.strikethrough(.patternDash) //dash strikethrough with the same color of text
.strikethrough("red") //single strikethrough with red color
.strikethrough(.styleDouble, "red") //double strikethrough with red color
...
*/
@discardableResult public func strikethrough(_ style: NSUnderlineStyle = .styleSingle, _ color: Any? = nil) -> Self {
var styles = NSNumber(value: style.rawValue)
if style != .styleNone && style != .styleSingle && style != .styleThick && style != .styleDouble {
styles = NSNumber(value: style.rawValue | NSUnderlineStyle.styleSingle.rawValue)
}
cpk_addAttribute(name: NSStrikethroughStyleAttributeName, value: styles)
if let strikethroughColor = Color(color) {
cpk_addAttribute(name: NSStrikethroughColorAttributeName, value: strikethroughColor)
}
return self
}
@discardableResult
public func strikethrough(_ color: Any) -> Self {
return strikethrough(.styleSingle, color)
}
/**
* NSStrokeWidthAttributeName, NSStrokeColorAttributeName
* stroke's second argument use Color() internally, so it can take any kind of values that Color() supported.
* See Color.swift for more information.
* Usages:
.stroke(1)
.stroke(-4, "red")
*/
@discardableResult
public func stroke(_ width: CGFloat, _ color: Any? = nil) -> Self {
cpk_addAttribute(name: NSStrokeWidthAttributeName, value: width)
if let strokeColor = Color(color) {
cpk_addAttribute(name: NSStrokeColorAttributeName, value: strokeColor)
}
return self
}
/**
* NSObliquenessAttributeName
* Usages:
.oblique(0.3)
.oblique(-0.3)
*/
@discardableResult
public func oblique(_ value: CGFloat) -> Self {
cpk_addAttribute(name: NSObliquenessAttributeName, value: value)
return self
}
/**
* NSBaselineOffsetAttributeName
* Usages:
.offset(20)
.offset(-20)
*/
@discardableResult
public func offset(_ offset: CGFloat) -> Self {
cpk_addAttribute(name: NSBaselineOffsetAttributeName, value: offset)
return self
}
/**
* NSLinkAttributeName
* Also can be used to add clickable link for UILabel.
* Usages:
.link("http://www.google.com")
.link() //mark as link for UILabel
*/
@discardableResult
public func link(_ url: String? = nil) -> Self {
if let urlString = url {
cpk_addAttribute(name: NSLinkAttributeName, value: urlString)
} else {
cpk_addAttribute(name: CPKLabelLinkAttributeName, value: CPKLabelLinkAttributeValue)
}
return self
}
/**
* Line spacing
* Usages:
.lineGap(10)
*/
@discardableResult
public func lineGap(_ spacing: CGFloat) -> Self {
cpk_addParagraphAttribute(key: "lineSpacing", value: spacing)
return self
}
/**
* First line head indent
* Usages:
.indent(20)
*/
@discardableResult
public func indent(_ headIntent: CGFloat) -> Self {
cpk_addParagraphAttribute(key: "firstLineHeadIndent", value: headIntent)
return self
}
/**
* TextAlignment
* Usages:
.align(.center)
.align(.justified)
...
*/
@discardableResult
public func align(_ alignment: NSTextAlignment) -> Self {
cpk_addParagraphAttribute(key: "alignment", value: NSNumber(value: alignment.rawValue))
return self
}
/**
* Select substrings
* By default Attributes are applied to the whole string.
You can make them only affect some parts of them by selecting substrings with regular expression or range.
* You can pass multiply options at the same time.
* See AttStrSelectionOptions for more information.
* Usages:
AttStr("hello world").select("world").color("red") //only "world" are red
AttStr("abc123").select("[a-z]+").color("red") //only "abc" are red
AttStr("abc123").select(.number).color("red") //only "123" are red
AttStr("@Tim at #Apple").select(.nameTag, .hashTag).color("red") //@Tim" and "#Apple" are red
AttStr("@Tim at #apple").select(.range(5, 2)).color("red") //only "@at" are red
...
* .select("pattern") is just the shorthand of .select(.match("pattern"))
*/
@discardableResult
public func select(_ optionOrStringLiterals: AttStrSelectionOptions...) -> Self {
for option in optionOrStringLiterals {
var regExp: NSRegularExpression?
var patternString: String?
var selectedRange = false
switch option {
case .all:
cpk_select(range: NSMakeRange(0, self.length))
case .url:
regExp = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue)
case .date:
regExp = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.date.rawValue)
case .phoneNumber:
regExp = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.phoneNumber.rawValue)
case .hashTag:
patternString = "(?<!\\w)#([\\w\\_]+)?"
case .nameTag:
patternString = "(?<!\\w)@([\\w\\_]+)?"
case .number:
patternString = "\\d+(\\.\\d+)?"
case let .match(value):
if let regExpObject = value as? NSRegularExpression {
regExp = regExpObject
} else {
patternString = String(describing: value)
}
case let .range(location, length):
cpk_select(range: NSMakeRange(location >= 0 ? location : self.length + location, length))
return self
}
if patternString != nil {
regExp = try? NSRegularExpression(pattern: patternString!,
options: NSRegularExpression.Options(rawValue: 0))
}
if regExp != nil {
let matches = regExp!.matches(in: self.string,
options: NSRegularExpression.MatchingOptions(rawValue: 0),
range: NSMakeRange(0, self.length))
for result in matches {
cpk_select(range: result.range)
selectedRange = true
}
}
if !selectedRange {
cpk_select(range: nil)
}
}
return self
}
/**
* Prevent overriding attribute.
* By default, the attribute value applied later will override the previous one if they are the same attributes.
* Usages:
AttStr(@"hello").color(@"red").color(@"green") //green color
AttStr(@"hello").color(@"red").preventOverride.color(@"green") //red color
*/
@discardableResult
public func preventOverride(_ flag: Bool = true) -> Self {
self.cpkPreventOverrideAttribute = flag
return self
}
}
public enum AttStrSelectionOptions {
case all //select whole string
case url //select all urls
case date //select all dates
case number //select all numbers
case phoneNumber //select all phone numbers
case hashTag //select all hash tags
case nameTag //select all name tags
case match(Any) //select substrings with regExp pattern or regExp Object
case range(Int, Int) //select substring with range
}

227
Cupcake/Button.swift Normal file
View File

@ -0,0 +1,227 @@
//
// Button.swift
// Cupcake
//
// Created by nerdycat on 2017/3/23.
// Copyright © 2017 nerdycat. All rights reserved.
//
import UIKit
public var Button: UIButton {
let button = UIButton()
cpk_higherHuggingAndResistance(forView: button)
return button
}
public extension UIButton {
/**
* Setting normal title or normal attributedTitle
* str can take any kind of value, even primitive type like Int.
* Usages:
.str(1024)
.str("hello world")
.str( AttStr("hello world").strikethrough() )
...
*/
@discardableResult public func str(_ any: Any) -> Self {
if let attStr = any as? NSAttributedString {
setAttributedTitle(attStr, for: .normal)
} else {
setTitle(String(describing: any), for: .normal)
}
return self
}
/**
* Setting font
* font use Font() internally, so it can take any kind of values that Font() supported.
* See Font.siwft for more information.
* Usages:
.font(15)
.font("20")
.font("body")
.font("Helvetica,15")
.font(someLabel.font)
...
**/
@discardableResult public func font(_ any: Any) -> Self {
self.titleLabel?.font = Font(any)
return self
}
/**
* Setting titleColor
* color use Color() internally, so it can take any kind of values that Color() supported.
* See Color.swift for more information.
* Usages:
.color(@"red")
.color(@"#F00")
.color(@"255,0,0")
.color(someLabel.textColor)
...
*/
@discardableResult public func color(_ any: Any) -> Self {
setTitleColor(Color(any), for: .normal)
return self
}
/**
* Setting highlighted titleColor
* highColor use Color() internally, so it can take any kind of values that Color() supported.
* See Color.swift for more information.
* Usages:
.highColor(@"red")
.highColor(@"#F00")
.highColor(@"255,0,0")
.highColor(someLabel.textColor)
...
*/
@discardableResult public func highColor(_ any: Any) -> Self {
setTitleColor(Color(any), for: .highlighted)
return self
}
/**
* Setting normal image
* img use Img() internally, so it can take any kind of values that Img() supported.
* See Img.swift for more information.
* Usages:
.img("cat")
.img("#button-background")
.img("$home-icon")
.img(someImage)
...
*/
@discardableResult public func img(_ any: Any) -> Self {
let image = Img(any)
setImage(image, for: .normal)
if self.frame.isEmpty {
self.frame.size = image.size
}
return self
}
/**
* Setting highlighted image
* highImg use Img() internally, so it can take any kind of values that Img() supported.
* See Img.swift for more information.
* Usages:
.highImg("cat")
.highImg("#button-background")
.highImg("$home-icon")
.highImg(someImage)
...
*/
@discardableResult public func highImg(_ any: Any) -> Self {
setImage(Img(any), for: .highlighted)
return self
}
/**
* Setting background with Color or Image.
* bg use Img() internally, so it can take any kind of values that Img() supported.
* See Img.swift for more information.
* Usages:
.bg(@"red")
.bg(@"#F00")
.bg(@"255,0,0")
.bg(someView.backgroundColor)
.bg("cat") //using image
.bg(someImage) //using image
...
*/
@discardableResult override public func bg(_ any: Any) -> Self {
let image = Img(any)
setBackgroundImage(image, for: .normal)
cpk_masksToBoundsIfNeed()
if self.frame.isEmpty {
self.frame.size = image.size
}
return self
}
/**
* Setting highlighted background with Color or Image.
* highBg use Img() internally, so it can take any kind of values that Img() supported.
* See Img.swift for more information.
* Usages:
.highBg(@"red")
.highBg(@"#F00")
.highBg(@"255,0,0")
.highBg(someView.backgroundColor)
.highBg("cat") //using image
.highBg(someImage) //using image
...
*/
@discardableResult public func highBg(_ any: Any) -> Self {
setBackgroundImage(Img(any), for: .highlighted)
cpk_masksToBoundsIfNeed()
return self
}
/**
* Setting contentEdgeInsets
* Usages:
.padding(10) //top: 10, left: 10, bottom: 10, right: 10
.padding(10, 20) //top: 10, left: 20, bottom: 10, right: 20
.padding(10, 20, 30) //top: 10, left: 20, bottom: 0 , right: 30
.padding(10, 20, 30, 40) //top: 10, left: 20, bottom: 30, right: 40
*/
@discardableResult public func padding(_ contentEdgeInsets: CGFloat...) -> Self {
cpk_updatePadding(contentEdgeInsets, forView: self)
return self
}
/**
* Setting spacing between title and image.
* Usages:
.gap(10)
*/
@discardableResult public func gap(_ spacing: CGFloat) -> Self {
self.cpkGap = spacing
let halfGap = spacing / 2
self.titleEdgeInsets = UIEdgeInsetsMake(0, halfGap, 0, -halfGap)
self.imageEdgeInsets = UIEdgeInsetsMake(0, -halfGap, 0, halfGap)
var insets = self.cpkInsets ?? UIEdgeInsetsMake(0, 0, 0, 0)
insets.left += halfGap
insets.right += halfGap
self.contentEdgeInsets = insets
return self
}
/**
* Swapping title and image position.
* Usages:
.reversed()
.reversed(false)
*/
@discardableResult public func reversed(_ reversed: Bool = true) -> Self {
let t = reversed ? CATransform3DMakeScale(-1, 1, 1) : CATransform3DIdentity
self.layer.sublayerTransform = t
self.imageView?.layer.transform = t
self.titleLabel?.layer.transform = t
return self
}
/**
* Enable multilines for Button.
* Usages:
.lines(2)
.lines(0) //multilines
.lines() //same as .lines(0)
*/
@discardableResult public func lines(_ numberOfLines: CGFloat = 0) -> Self {
self.titleLabel?.numberOfLines = Int(numberOfLines)
self.titleLabel?.lineBreakMode = .byWordWrapping
self.titleLabel?.textAlignment = .center
return self
}
}

708
Cupcake/CPKStackView.swift Normal file
View File

@ -0,0 +1,708 @@
//
// StackView.swift
// Cupcake
//
// Created by nerdycat on 2017/3/29.
// Copyright © 2017 nerdycat. All rights reserved.
//
import UIKit
/**
* Usually you don't use CPKStackView directly.
* Use HStack and VStack instread. See Stack.swift for more information.
*/
fileprivate let kDefaultEnclosurePriority: UILayoutPriority = 200
fileprivate let kAlignmentPriority: UILayoutPriority = 1000
fileprivate let kSpacingPriority: UILayoutPriority = 1000
fileprivate let kSpringPriority: UILayoutPriority = 1000
let kFixSizePriority: UILayoutPriority = 900
let kLowPriority: UILayoutPriority = 100
let kDefaultHuggingPriority: UILayoutPriority = 250
public class CPKStackView: UIView {
private var alignmentConstraints = [NSLayoutConstraint]()
private var spacingConstraints = [NSLayoutConstraint]()
private var enslosureConstraints = [NSLayoutConstraint]()
private var springConstraints = [NSLayoutConstraint]()
private var _alignment: CPKStackAlignment = .left
private var _spacing: CGFloat = 0
private var _axis: UILayoutConstraintAxis = .horizontal
public private(set) var arrangedSubviews = [UIView]()
public var alignment: CPKStackAlignment {
get { return _alignment }
set { if _alignment != newValue { _alignment = newValue; alignmentDidChange() } }
}
public var spacing: CGFloat {
get { return _spacing }
set { if _spacing != newValue { _spacing = newValue; spacingDidChange() } }
}
public var axis: UILayoutConstraintAxis {
get { return _axis }
set { if _axis != newValue { _axis = newValue; axisDidChange() } }
}
public func addArrangedSubview(item: Any) {
insertArrangedSubview(item: item, at: self.arrangedSubviews.count)
}
public func addArrangedSubviews(items: [Any]) {
for item in items {
if let array = item as? Array<Any> {
for item in array {
self.addArrangedSubview(item: item)
}
} else {
self.addArrangedSubview(item: item)
}
}
}
public func insertArrangedSubview(item: Any, at index: Int) {
let sub = item is String ? StackSpring() : item
if let view = sub as? UIView {
self.insertSubview(view, at: index)
self.arrangedSubviews.insert(view, at: index)
view.translatesAutoresizingMaskIntoConstraints = false
view.addObserver(self, forKeyPath: "hidden", options: [.new, .old], context: nil)
if UIEdgeInsetsEqualToEdgeInsets(view.layoutMargins, UIEdgeInsetsMake(8, 8, 8, 8)) {
view.layoutMargins = UIEdgeInsets.zero
}
if !view.isHidden {
addAndActivateConstraintsForView(at: index)
}
} else if let array = sub as? [Any] {
for i in 0..<array.count {
insertArrangedSubview(item: array[i], at: min(index + i, self.arrangedSubviews.count))
}
}
else {
let spacing = CPKFloat(sub)
if index == 0 {
self.cpkAttachSpacing = spacing
attachSpaceDidChangeForView(at: -1)
} else {
let previousView = self.arrangedSubviews[index - 1]
previousView.cpkAttachSpacing = spacing
attachSpaceDidChangeForView(at: index - 1)
}
}
}
public func removeArrangedSubview(view: UIView) {
view.removeFromSuperview()
}
public func removeArrangedSubview(at index: Int) {
let item = itemAt(index: index)
if item != self { item.removeFromSuperview() }
}
//MARK: Override
public override init(frame: CGRect) {
super.init(frame: frame)
self.layoutMargins = UIEdgeInsets.zero
}
public required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// public override class var layerClass: Swift.AnyClass {
// return CPKTransformLayer.self
// }
public override func willRemoveSubview(_ subview: UIView) {
if let index = self.arrangedSubviews.index(of: subview) {
removeAndDeactivateConstraintsForView(at: index)
self.arrangedSubviews.remove(at: index)
subview.removeObserver(self, forKeyPath: "hidden", context: nil)
subview.cpkAttachSpacing = nil
}
super.willRemoveSubview(subview)
}
public override func sizeThatFits(_ size: CGSize) -> CGSize {
return systemLayoutSizeFitting(UILayoutFittingCompressedSize)
}
//MARK: KVO
public override func observeValue(forKeyPath keyPath: String?,
of object: Any?,
change: [NSKeyValueChangeKey : Any]?,
context: UnsafeMutableRawPointer?) {
if keyPath == "hidden" {
let oldValue = change?[.oldKey] as? Bool
let newValue = change?[.newKey] as? Bool
if newValue != oldValue {
if let item = object as? UIView {
if let index = self.arrangedSubviews.index(of: item) {
if item.isHidden {
removeAndDeactivateConstraintsForView(at: index)
} else {
addAndActivateConstraintsForView(at: index)
}
}
}
}
}
}
//MARK: Constraints
private func addAndActivateConstraintsForView(at index: Int) {
if itemAt(index: index).isHidden {
return;
}
var oldConstraints = [NSLayoutConstraint]()
var newConstraints = [NSLayoutConstraint]()
let previousIndex = previousVisibleViewIndexForView(at: index)
let nextIndex = nextVisibleViewIndexForView(at: index)
newConstraints.append(contentsOf: addAlignmentConstraint(at: index))
newConstraints.append(contentsOf: addEnclosureConstraints(at: index))
if let c = removeSpacingConstraintBetween(index1: previousIndex, index2: nextIndex) {
oldConstraints.append(c)
}
if let c = addSpacingConstraintBetween(index1: previousIndex, index2: index) {
newConstraints.append(c)
}
if let c = addSpacingConstraintBetween(index1: index, index2: nextIndex) {
newConstraints.append(c)
}
NSLayoutConstraint.deactivate(oldConstraints)
NSLayoutConstraint.activate(newConstraints)
if let spring = itemAt(index: index) as? StackSpring {
spring.axis = self.axis
addAndActivateSpringConstraintsForSpring(at: index)
}
}
private func addAndActivateConstraintsForAll() {
addAndActivateAlignmentConstraintsForAll()
addAndActivateSpacingConstraintsForAll()
addAndActivateEnclosureConstraintsForAll()
for (i, item) in self.arrangedSubviews.enumerated() {
if let spring = item as? StackSpring {
spring.axis = self.axis
addAndActivateSpringConstraintsForSpring(at: i)
}
}
}
private func removeAndDeactivateConstraintsForView(at index: Int) {
var oldConstraints = [NSLayoutConstraint]()
if let c = removeAlignmentConstraint(at: index) {
oldConstraints.append(c)
}
oldConstraints.append(contentsOf: removeEnclosureConstraints(at: index))
NSLayoutConstraint.deactivate(oldConstraints)
removeAndRebuildSpacingConstraints(at: index)
removeAndDeactivateSpringConstraintsForSpring(at: index)
}
private func removeAndDeactivateAllConstriants() {
removeAndDeactivateAllAlignmentConstriants()
removeAndDeactivateAllEnclosureConstraints()
removeAndDeactivateAllSpacingConstraints()
removeAndDeactivateAllSpringsConstraints()
}
//MARK: Alignment
@discardableResult
private func addAlignmentConstraint(at index: Int) -> [NSLayoutConstraint] {
var newConstraints = [NSLayoutConstraint]()
var att = NSLayoutAttribute.notAnAttribute
if self.alignment == .fill {
if self.axis == .vertical {
newConstraints.append( makeConstraint(index, .leftMargin, .equal, -1, .leftMargin, 1, 0, 1000) )
newConstraints.append( makeConstraint(index, .rightMargin, .equal, -1, .rightMargin, 1, 0, 1000) )
} else {
newConstraints.append( makeConstraint(index, .topMargin, .equal, -1, .topMargin, 1, 0, 1000) )
newConstraints.append( makeConstraint(index, .bottomMargin, .equal, -1, .bottomMargin, 1, 0, 1000) )
}
} else {
if self.axis == .vertical {
if self.alignment == .left { att = .leftMargin }
if self.alignment == .right { att = .rightMargin }
if self.alignment == .center { att = .centerXWithinMargins }
} else {
if self.alignment == .top { att = .topMargin }
if self.alignment == .bottom { att = .bottomMargin }
if self.alignment == .center { att = .centerYWithinMargins }
if self.alignment == .baseline { att = .lastBaseline }
}
newConstraints.append( makeConstraint(index, att, .equal, -1, att, 1, 0, kAlignmentPriority) )
}
self.alignmentConstraints.append(contentsOf: newConstraints)
return newConstraints
}
private func addAndActivateAlignmentConstraintsForAll() {
for i in 0..<self.arrangedSubviews.count {
if !self.arrangedSubviews[i].isHidden {
addAlignmentConstraint(at: i)
}
}
NSLayoutConstraint.activate(self.alignmentConstraints)
}
private func removeAlignmentConstraint(at index :Int) -> NSLayoutConstraint? {
let item = itemAt(index: index)
if item != self {
for (i, c) in self.alignmentConstraints.enumerated() {
if c.firstItem === item || c.secondItem === item {
self.alignmentConstraints.remove(at: i)
return c
}
}
}
return nil
}
private func removeAndDeactivateAllAlignmentConstriants() {
NSLayoutConstraint.deactivate(self.alignmentConstraints)
self.alignmentConstraints.removeAll()
}
//MARK: Enclosure
@discardableResult
private func addEnclosureConstraints(at index: Int) -> [NSLayoutConstraint] {
var newConstraints = [NSLayoutConstraint]()
let enclosurePriority = kDefaultEnclosurePriority
if self.axis == .vertical {
newConstraints.append(makeConstraint(index, .leftMargin, .equal, -1, .leftMargin, enclosurePriority))
newConstraints.append(makeConstraint(index, .leftMargin, .greaterThanOrEqual, -1, .leftMargin, 1000))
newConstraints.append(makeConstraint(index, .rightMargin, .equal, -1, .rightMargin, enclosurePriority))
newConstraints.append(makeConstraint(index, .rightMargin, .lessThanOrEqual, -1, .rightMargin, 1000))
} else {
newConstraints.append(makeConstraint(index, .topMargin, .equal, -1, .topMargin, enclosurePriority))
newConstraints.append(makeConstraint(index, .topMargin, .greaterThanOrEqual, -1, .topMargin, 1000))
var att: NSLayoutAttribute = .bottomMargin
if self.alignment == .baseline {
att = .lastBaseline
}
newConstraints.append(makeConstraint(index, att, .equal, -1, att, enclosurePriority))
newConstraints.append(makeConstraint(index, att, .lessThanOrEqual, -1, att, 1000))
}
self.enslosureConstraints.append(contentsOf: newConstraints)
return newConstraints
}
private func addAndActivateEnclosureConstraintsForAll() {
for i in 0..<self.arrangedSubviews.count {
if !self.arrangedSubviews[i].isHidden {
addEnclosureConstraints(at: i)
}
}
NSLayoutConstraint.activate(self.enslosureConstraints)
}
private func removeEnclosureConstraints(at index: Int) -> [NSLayoutConstraint] {
var oldConstriants = [NSLayoutConstraint]()
let item = itemAt(index: index)
for i in stride(from: self.enslosureConstraints.count - 1, through: 0, by: -1) {
let c = self.enslosureConstraints[i]
if c.firstItem === item || c.secondItem === item {
oldConstriants.append(c)
self.enslosureConstraints.remove(at: i)
}
}
return oldConstriants
}
private func removeAndDeactivateAllEnclosureConstraints() {
NSLayoutConstraint.deactivate(self.enslosureConstraints)
self.enslosureConstraints.removeAll()
}
//MARK: Spacing
@discardableResult
private func addSpacingConstraintBetween(index1: Int, index2: Int) -> NSLayoutConstraint? {
let item1 = itemAt(index: index1)
let item2 = itemAt(index: index2)
if item1 === item2 {
return nil
}
var att1 = NSLayoutAttribute.notAnAttribute
var att2 = NSLayoutAttribute.notAnAttribute
if self.axis == .vertical {
att1 = (item1 == self ? .topMargin : .bottomMargin)
att2 = (item2 != self ? .topMargin : .bottomMargin)
} else {
att1 = (item1 == self ? .leftMargin : .rightMargin)
att2 = (item2 != self ? .leftMargin : .rightMargin)
}
var spacing: CGFloat = 0
if let attachSpacing = item1.cpkAttachSpacing {
spacing = -attachSpacing
} else if item1 != self && item2 != self {
spacing = -self.spacing
}
let c = makeConstraint(index1, att1, .equal, index2, att2, 1, spacing, kSpacingPriority)
self.spacingConstraints.append(c)
return c
}
private func addAndActivateSpacingConstraintsForAll() {
if self.arrangedSubviews.count > 0 {
var index1 = -1
while index1 < self.arrangedSubviews.count {
let index2 = nextVisibleViewIndexForView(at: index1)
addSpacingConstraintBetween(index1: index1, index2: index2)
index1 = index2
}
NSLayoutConstraint.activate(self.spacingConstraints)
}
}
private func removeSpacingConstraintBetween(index1: Int, index2: Int) -> NSLayoutConstraint? {
let item1 = itemAt(index: index1)
let item2 = itemAt(index: index2)
for (i, c) in self.spacingConstraints.enumerated() {
if c.firstItem === item1 && c.secondItem === item2 {
self.spacingConstraints.remove(at: i)
return c
}
}
return nil
}
private func removeAndDeactivateAllSpacingConstraints() {
NSLayoutConstraint.deactivate(self.spacingConstraints)
self.spacingConstraints.removeAll()
}
private func removeAndRebuildSpacingConstraints(at index: Int) {
var oldConstraints = [NSLayoutConstraint]()
var newConstarints = [NSLayoutConstraint]()
let previousIndex = previousVisibleViewIndexForView(at: index)
let nextIndex = nextVisibleViewIndexForView(at: index)
if let c = removeSpacingConstraintBetween(index1: previousIndex, index2: index) {
oldConstraints.append(c)
}
if let c = removeSpacingConstraintBetween(index1: index, index2: nextIndex) {
oldConstraints.append(c)
}
if oldConstraints.count > 0 {
if let c = addSpacingConstraintBetween(index1: previousIndex, index2: nextIndex) {
newConstarints.append(c)
}
}
NSLayoutConstraint.deactivate(oldConstraints)
NSLayoutConstraint.activate(newConstarints)
}
private func attachSpaceDidChangeForView(at index: Int) {
let item1 = itemAt(index: index)
let item2 = itemAt(index: index + 1)
if let spacing = item1.cpkAttachSpacing {
for c in self.spacingConstraints {
if c.firstItem === item1 && c.secondItem === item2 {
c.constant = -spacing
break
}
}
}
}
//MARK: Spring
private func addAndActivateSpringConstraintsForSpring(at index: Int) {
var newConstraints = [NSLayoutConstraint]()
for i in 0..<self.arrangedSubviews.count {
let item = self.arrangedSubviews[i]
if item is StackSpring && i != index {
var hasConstraint = false
let item1 = itemAt(index: i)
let item2 = itemAt(index: index)
for c in self.springConstraints {
if (c.firstItem === item1 && c.secondItem === item2) ||
(c.firstItem === item2 && c.secondItem === item1) {
hasConstraint = true
break
}
}
if !hasConstraint {
let att: NSLayoutAttribute = (self.axis == .vertical ? .height : .width)
let c = makeConstraint(i, att, .equal, index, att, 1, 0, kSpringPriority)
self.springConstraints.append(c)
newConstraints.append(c)
}
}
}
NSLayoutConstraint.activate(newConstraints)
}
private func removeAndDeactivateSpringConstraintsForSpring(at index: Int) {
let item = itemAt(index: index)
var oldConstraints = [NSLayoutConstraint]()
for i in stride(from: self.springConstraints.count - 1, through: 0, by: -1) {
let c = self.springConstraints[i]
if c.firstItem === item || c.secondItem === item {
self.springConstraints.remove(at: i)
oldConstraints.append(c)
}
}
NSLayoutConstraint.deactivate(oldConstraints)
}
private func removeAndDeactivateAllSpringsConstraints() {
NSLayoutConstraint.deactivate(self.springConstraints)
self.springConstraints.removeAll()
}
//MARK: Utils
private func makeConstraint(_ index1: Int,
_ att1: NSLayoutAttribute,
_ relation: NSLayoutRelation,
_ index2: Int,
_ att2: NSLayoutAttribute,
_ multiplier: CGFloat,
_ constant: CGFloat,
_ priority: UILayoutPriority) -> NSLayoutConstraint {
let item1 = itemAt(index: index1)
let item2 = itemAt(index: index2)
let c = NSLayoutConstraint(item: item1,
attribute: att1,
relatedBy: relation,
toItem: item2,
attribute: att2,
multiplier: multiplier,
constant: constant)
c.priority = priority
return c
}
private func makeConstraint(_ index1: Int,
_ att1: NSLayoutAttribute,
_ relation: NSLayoutRelation,
_ index2: Int,
_ att2: NSLayoutAttribute,
_ priority: UILayoutPriority) -> NSLayoutConstraint {
return makeConstraint(index1, att1, relation, index2, att2, 1, 0, priority)
}
private func previousVisibleViewIndexForView(at index: Int) -> Int {
for i in stride(from: index - 1, through: 0, by: -1) {
let item = itemAt(index: i)
if !item.isHidden { return i }
}
return -1
}
private func nextVisibleViewIndexForView(at index: Int) -> Int {
for i in stride(from: index + 1, to: self.arrangedSubviews.count, by: 1) {
let item = itemAt(index: i)
if !item.isHidden { return i }
}
return self.arrangedSubviews.count
}
private func itemAt(index: Int) -> UIView {
if index >= 0 && index < self.arrangedSubviews.count {
return self.arrangedSubviews[index]
} else {
return self
}
}
private func alignmentDidChange() {
removeAndDeactivateAllAlignmentConstriants()
removeAndDeactivateAllEnclosureConstraints()
addAndActivateAlignmentConstraintsForAll()
addAndActivateEnclosureConstraintsForAll()
}
private func spacingDidChange() {
for c in self.spacingConstraints {
let item1 = c.firstItem as! UIView
let item2 = c.secondItem as! UIView
if item1 != self && item2 != self {
if let attachSpacing = item1.cpkAttachSpacing {
c.constant = -attachSpacing
} else {
c.constant = -self.spacing
}
}
}
}
private func axisDidChange() {
removeAndDeactivateAllConstriants()
addAndActivateConstraintsForAll()
}
}
extension UIView {
var cpkAttachSpacing: CGFloat? {
get { return cpk_associatedObjectFor(key: #function) as? CGFloat }
set { cpk_setAssociated(object: newValue, forKey: #function) }
}
}
class CPKTransformLayer: CATransformLayer {
override var isOpaque: Bool {
set {}
get { return true }
}
}
public class StackSpring: UIView {
private var _axis: UILayoutConstraintAxis = .horizontal
fileprivate var axis: UILayoutConstraintAxis {
get { return _axis }
set {
if _axis != newValue {
_axis = newValue
updateContentPriorities()
invalidateIntrinsicContentSize()
}
}
}
override init(frame: CGRect) {
super.init(frame: frame)
self.isUserInteractionEnabled = false
updateContentPriorities()
}
public required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
public override var intrinsicContentSize: CGSize {
return CGSize.zero
}
public override var backgroundColor: UIColor? {
set {}
get { return nil }
}
public override var isOpaque: Bool {
set {}
get { return true }
}
public override var clipsToBounds: Bool {
set {}
get { return false }
}
public override class var layerClass: Swift.AnyClass {
return CPKTransformLayer.self
}
private func updateContentPriorities() {
let horPriority = UILayoutPriority(self.axis == .horizontal ? 1 : 1000)
let verPriority = UILayoutPriority(self.axis == .horizontal ? 1000 : 1)
setContentHuggingPriority(horPriority, for: .horizontal)
setContentHuggingPriority(verPriority, for: .vertical)
setContentCompressionResistancePriority(horPriority, for: .horizontal)
setContentCompressionResistancePriority(verPriority, for: .vertical)
}
}

106
Cupcake/Color.swift Normal file
View File

@ -0,0 +1,106 @@
//
// Color.swift
// Cupcake
//
// Created by nerdycat on 2017/3/17.
// Copyright © 2017 nerdycat. All rights reserved.
//
import UIKit
/**
* Create UIColor Object.
* Color argument can be:
1) UIColor object
2) UIImage object, return a pattern image color
3) "red", "green", "blue", "clear", etc. (any system color)
5) "random": randomColor
6) "255,0,0": RGB color
7) "#FF0000" or "0xF00": Hex color
* All the string representation can have an optional alpha value.
* Usages:
Color([UIColor redColor])
Color("red")
Color("red,0.1") //with alpha
Color("255,0,0)
Color("255,0,0,1") //with alpha
Color("#FF0000")
Color("#F00,0.1") //with alpha
Color("random,0.5")
Color(Img("image")) //using image
...
*/
func Color(_ any: Any?) -> UIColor? {
if any == nil {
return nil
}
if let color = any as? UIColor {
return color;
}
if let image = any as? UIImage {
return UIColor(patternImage: image)
}
guard any is String else {
return nil
}
var alpha: CGFloat = 1
var components = (any as! String).components(separatedBy: ",")
if components.count == 2 || components.count == 4 {
alpha = min(CGFloat(Float(components.last!) ?? 1), 1)
components.removeLast()
}
var r: Int?, g: Int? , b: Int?
if components.count == 1 {
let string = components.first!
let sel = NSSelectorFromString(string + "Color")
//system color
if let color = UIColor.cpk_safePerform(selector: sel) as? UIColor {
return color.withAlphaComponent(alpha)
}
if string == "random" {
r = Int(arc4random_uniform(256))
g = Int(arc4random_uniform(256))
b = Int(arc4random_uniform(256))
} else if string.hasPrefix("#") {
if string.characters.count == 4 {
r = Int(string.subAt(1), radix:16)! * 17
g = Int(string.subAt(2), radix:16)! * 17
b = Int(string.subAt(3), radix:16)! * 17
} else if string.characters.count == 7 {
r = Int(string.subAt(1...2), radix:16)
g = Int(string.subAt(3...4), radix:16)
b = Int(string.subAt(5...6), radix:16)
}
}
} else if components.count == 3 {
r = Int(components[0])
g = Int(components[1])
b = Int(components[2])
}
if r != nil && g != nil && b != nil {
return UIColor(red: CGFloat(r!) / 255.0,
green: CGFloat(g!) / 255.0,
blue: CGFloat(b!) / 255.0,
alpha: alpha)
}
return nil
}

230
Cupcake/Cons.swift Normal file
View File

@ -0,0 +1,230 @@
//
// Constraint.swift
// Cupcake
//
// Created by nerdycat on 2017/3/22.
// Copyright © 2017 nerdycat. All rights reserved.
//
import UIKit
/**
* A SnapKit like syntax to setup constraints.
* Usages:
view1.makeCons({ make in
make.left.top.equal(view2).offset(20, 20)
make.width.height.equal(100, 100)
})
view1.remakeCons({
$0.left.equal(20)
$0.top.euqal(view2).bottom.offset(20)
$0.size.equal(view2).multiply(0.5)
})
*/
public extension ConsAtts {
public var left: Cons {
return addAttributes(.left)
}
public var leftMargin: Cons {
return addAttributes(.leftMargin)
}
public var right: Cons {
return addAttributes(.right)
}
public var rightMargin: Cons {
return addAttributes(.rightMargin)
}
public var top: Cons {
return addAttributes(.top)
}
public var topMargin: Cons {
return addAttributes(.topMargin)
}
public var bottom: Cons {
return addAttributes(.bottom)
}
public var bottomMargin: Cons {
return addAttributes(.bottomMargin)
}
public var leading: Cons {
return addAttributes(.leading)
}
public var leadingMargin: Cons {
return addAttributes(.leadingMargin)
}
public var trailing: Cons {
return addAttributes(.trailing)
}
public var trailingMagin: Cons {
return addAttributes(.trailingMargin)
}
public var width: Cons {
return addAttributes(.width)
}
public var height: Cons {
return addAttributes(.height)
}
public var centerX: Cons {
return addAttributes(.centerX)
}
public var centerY: Cons {
return addAttributes(.centerY)
}
public var centerXInMargins: Cons {
return addAttributes(.centerXWithinMargins)
}
public var centerYInMargins: Cons {
return addAttributes(.centerYWithinMargins)
}
public var baseline: Cons {
return addAttributes(.lastBaseline)
}
public var firstBaseline: Cons {
return addAttributes(.firstBaseline)
}
public var center: Cons {
return addAttributes(.centerX, .centerY)
}
public var origin: Cons {
return addAttributes(.left, .top)
}
public var size: Cons {
return addAttributes(.width, .height)
}
public var edge: Cons {
return addAttributes(.top, .left, .bottom, .right)
}
}
public extension Cons {
/**
* equal relationship, can be UIView or values. This is the default relationship.
* If you don't specify item2 (by using equal, lessEqual or greaterEqual),
and the item1's attribute is not width and height,
then the item2 is automatically refer to item1's superview.
* Usages:
make.left.equal(superview)
make.left.offset(0) //same as above
make.width.equal(100)
make.size.equal(100, 200)
*/
@discardableResult public func equal(_ item2OrValues: Any...) -> Self {
self.relation = .equal
updateSecondItem(item2OrValues)
return self
}
/**
* lessThanOrEqual relationship, can be UIView or values.
* Usages:
make.left.lessEqual(superview)
make.width.lessEqual(100)
make.size.lessEqual(100, 200)
*/
@discardableResult public func lessEqual(_ item2OrValues: Any...) -> Self {
self.relation = .lessThanOrEqual
updateSecondItem(item2OrValues)
return self
}
/**
* greaterThanOrEqual relationship, can be UIView or values.
* Usages:
make.left.greaterEqual(superview)
make.width.greaterEqual(100)
make.size.greaterEqual(100, 200)
*/
@discardableResult public func greaterEqual(_ item2OrValues: Any...) -> Self {
self.relation = .greaterThanOrEqual
updateSecondItem(item2OrValues)
return self
}
/**
* offset can accept multiply values.
* Usages:
make.left.offset(20)
make.left.top.offset(20, 40)
make.edge.offset(10, 20, 30, 40)
...
*/
@discardableResult public func offset(_ offsets: CGFloat...) -> Self {
self.constantValues = offsets
return self
}
/**
* multiply can accept multiply values.
* Usages:
make.width.equal(view2).multiply(0.5)
make.size.equal(view2).multiply(0.5, 0.8)
...
*/
@discardableResult public func multiply(_ multipliers: CGFloat...) -> Self {
self.multiplierValues = multipliers
return self
}
/**
* priority can accept multiply values.
* Usages:
make.width.equal(100).priority(800)
make.edge.offset(10).priority(1000, 1000, 900, 900)
...
*/
@discardableResult public func priority(_ priorities: UILayoutPriority...) -> Self {
self.priorityValues = priorities
return self
}
/**
* Save constraints to variables, it can accept multiply values.
* Very useful when you need to alter constraint later.
* Usages:
var topConstriant = NSLayoutConstraint()
View.bg("red").addTo(self.view).makeCons({
$0.size.equal(100, 100)
$0.center.offset(0).priority(900)
$0.top.offset(0).saveTo(&topConstriant)
})
...
topConstraint.isActivate = false
*/
@discardableResult public func saveTo(_ constraints: UnsafeMutablePointer<NSLayoutConstraint>...) -> Self {
self.storePointers = constraints
return self
}
}

50
Cupcake/Font.swift Normal file
View File

@ -0,0 +1,50 @@
//
// Font.swift
// Cupcake
//
// Created by nerdycat on 2017/3/17.
// Copyright © 2017 nerdycat. All rights reserved.
//
import UIKit
/**
* Create UIFont object.
* Font argument can be:
1) UIFont object
2) 15: systemFontOfSize 15
3) "15": boldSystemFontOfSize 15
4) "headline", "body", "caption1", and any other UIFontTextStyle.
5) "Helvetica,15": fontName + fontSize, separated by comma.
* Usages:
Font(someLabel.font),
Font(15)
Font("15")
Font("body")
Font("Helvetica,15")
...
*/
public func Font(_ any: Any) -> UIFont {
if let font = any as? UIFont {
return font
}
if let string = any as? String {
let elements = string.components(separatedBy: ",")
if elements.count == 2 {
return UIFont(name: elements[0], size: CGFloat(Float(elements[1])!))!
}
if let fontSize = Float(string), fontSize > 0 {
return UIFont.boldSystemFont(ofSize: CGFloat(fontSize))
}
let value = "UICTFontTextStyle" + string.capitalized
return UIFont.preferredFont(forTextStyle: UIFontTextStyle(rawValue: value))
}
return UIFont.systemFont(ofSize: CPKFloat(any))
}

54
Cupcake/ImageView.swift Normal file
View File

@ -0,0 +1,54 @@
//
// ImageView.swift
// Cupcake
//
// Created by nerdycat on 2017/3/23.
// Copyright © 2017 nerdycat. All rights reserved.
//
import UIKit
public var ImageView: UIImageView {
let imageView = UIImageView()
cpk_higherHuggingAndResistance(forView: imageView)
return imageView
}
public extension UIImageView {
/**
* Setting image
* img use Img() internally, so it can take any kind of values that Img() supported.
* See Img.swift for more information.
* Usages:
.img("cat")
.img("#button-background")
.img("$home-icon")
.img(someImage)
...
*/
@discardableResult public func img(_ any: Any) -> Self {
self.image = Img(any)
if self.image != nil {
if self.frame.isEmpty {
self.frame.size = self.image!.size
}
}
cpk_masksToBoundsIfNeed()
return self
}
/**
* Setting contentMode
* Usages:
.mode(.scaleAspectFit)
.mode(.center)
...
*/
@discardableResult public func mode(_ contentMode: UIViewContentMode) -> Self {
self.contentMode = contentMode
return self
}
}

123
Cupcake/Img.swift Normal file
View File

@ -0,0 +1,123 @@
//
// Img.swift
// Cupcake
//
// Created by nerdycat on 2017/3/17.
// Copyright © 2017 nerdycat. All rights reserved.
//
import UIKit
/**
* Create UIImage object.
* Img argument can be:
1) UIImage object
2) "imageName"
3) "#imageName": stretchable image
4) "$imageName": template image
5) Any color value that Color() supported.
* Prefixing image name with # character will create a stretchable image.
* Prefixing image name with $ character will create a template image.
* Passing a color value will create an 1x1 size image with specific color.
* Usages:
Img(someImage)
Img("cat")
Img("#button-background")
Img("$home-icon")
Img("33,33,33,0.5")
Img("red").resize(100, 100)
...
*/
public func Img(_ any: Any) -> UIImage {
if let image = any as? UIImage {
return image
}
if let string = any as? String {
let stretching = string.hasPrefix("#")
let templating = string.hasPrefix("$")
let imageName = (stretching || templating) ? string.subFrom(1) : string
var image = UIImage(named: imageName)
if stretching {
if image == nil {
image = UIImage(named: string)
} else {
let leftCap = Int(image!.size.width) / 2
let topCap = Int(image!.size.height) / 2
image = image?.stretchableImage(withLeftCapWidth:leftCap, topCapHeight: topCap)
}
}
if templating {
if image == nil {
image = UIImage(named: string)
} else {
image = image?.withRenderingMode(.alwaysTemplate)
}
}
if image != nil {
return image!
}
}
if let color = Color(any) {
return cpk_onePointImageWithColor(color)
} else {
assert(false, "invalid image")
return UIImage()
}
}
public extension UIImage {
/**
* Resize image.
* When passing integer value, it means the target size.
* When passing floating value, it means multiply with original size.
* Usages:
.resize(100, 100) //resize to 100x100
.resize(100) //same as .resize(100, 100)
.resize(0.8, 0.8) //0.8 * original size
.resize(0.8) //same as .resize(0.8, 0.8)
*/
@discardableResult public func resize(_ p1: Any, _ p2: Any? = nil) -> UIImage {
var newWidth: CGFloat = 0
var newHeight: CGFloat = 0
if let width = p1 as? Int {
newWidth = CGFloat(width)
} else {
newWidth = CPKFloat(p1) * self.size.width
}
if p2 == nil {
newHeight = newWidth
} else {
if let height = p2 as? Int {
newHeight = CGFloat(height)
} else {
newHeight = CPKFloat(p2!) * self.size.height
}
}
let rect = CGRect(x: 0, y: 0, width: newWidth, height: newHeight)
let hasAlpha = cpk_imageHasAlphaChannel(self)
UIGraphicsBeginImageContextWithOptions(rect.size, !hasAlpha, self.scale)
self.draw(in: rect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
}

120
Cupcake/Label.swift Normal file
View File

@ -0,0 +1,120 @@
//
// Label.swift
// Cupcake
//
// Created by nerdycat on 2017/3/17.
// Copyright © 2017 nerdycat. All rights reserved.
//
import UIKit
public var Label: UILabel {
let label = UILabel()
return label
}
public extension UILabel {
/**
* Setting text or attributedText
* str can take any kind of value, even primitive type like Int.
* Usages:
.str(1024)
.str("hello world")
.str( AttStr("hello world").strikethrough() )
...
*/
@discardableResult public func str(_ any: Any) -> Self {
if let attStr = any as? NSAttributedString {
self.attributedText = attStr
} else {
self.text = String(describing: any)
}
return self
}
/**
* Setting font
* font use Font() internally, so it can take any kind of values that Font() supported.
* See Font.swift for more information.
* Usages:
.font(15)
.font("20")
.font("body")
.font("Helvetica,15")
.font(someLabel.font)
...
**/
@discardableResult public func font(_ any: Any) -> Self {
self.font = Font(any)
return self
}
/**
* Setting textColor
* color use Color() internally, so it can take any kind of values that Color() supported.
* See Color.swift for more information.
* Usages:
.color(@"red")
.color(@"#F00")
.color(@"255,0,0")
.color(someLabel.textColor)
...
*/
@discardableResult public func color(_ any: Any) -> Self {
self.textColor = Color(any)
return self
}
/**
* Setting numberOfLines
* Usages:
.lines(2)
.lines(0) //multilines
.lines() //same as .lines(0)
*/
@discardableResult public func lines(_ numberOfLines: CGFloat = 0) -> Self {
self.numberOfLines = Int(numberOfLines)
return self
}
/**
* Setting lineSpacing
* Usages:
.lineGap(8)
*/
@discardableResult public func lineGap(_ lineSpacing: CGFloat) -> Self {
self.cpkLineGap = lineSpacing
return self
}
/**
* Setting textAlignment
* Usages:
.align(.center)
.align(.justified)
...
*/
@discardableResult public func align(_ textAlignment: NSTextAlignment) -> Self {
self.textAlignment = textAlignment
return self
}
/**
* Setup link handler.
* Use onLink in conjunction with AttStr's link method to make text clickable.
* This will automatically set isUserInteractionEnabled to true as well.
* Be aware of retain cycle when using this method.
* Usages:
.onLink({ text in print(text) })
.onLink({ [weak self] text in //capture self as weak reference when needed
print(text)
})
*/
@discardableResult func onLink(_ closure: @escaping (String)->()) -> Self {
self.isUserInteractionEnabled = true
self.cpkLinkHandler = closure
return self
}
}

118
Cupcake/Stack.swift Normal file
View File

@ -0,0 +1,118 @@
//
// Stack.swift
// Cupcake
//
// Created by nerdycat on 2017/3/29.
// Copyright © 2017 nerdycat. All rights reserved.
//
import UIKit
public enum CPKStackAlignment : Int {
case left //left alignment for VStack
case right //right alignment for VStack
case center //center alignment for HStack and VStack
case fill //make subviews fill the the Stack
case baseline //lastBaseline alignment for HStack
//top alignment for HStack
public static var top: CPKStackAlignment { return CPKStackAlignment.left }
//bottom alignment for HStack
public static var bottom: CPKStackAlignment { return CPKStackAlignment.right }
}
/**
* Create horizontal/vertical StackView with items.
* There are two special items:
1. Number: represent individual gap between two items.
2. String: represent a Spring that will take as much space as possible.
* There are three way to specified spacing between items:
1. Using Number as StackView item, which will add spacing between two items.
2. Using .gap() method, which will add spacing to all items.
3. Using .margin() method on UIView. A negative value of margin will add spacing around view.
* If you use Number and .gap() at the same time, the individual spacing will take precedence.
* By default, all items are enclosured inside StackView.
You can make a view exceeding the bounds of StackView by using a positive value of margin.
* Usages:
HStack(
View.bg("random").pin(30, 30),
20,
View.bg("random").pin(60, 60),
20,
View.bg("random").pin(90, 90)
).pin(.xy(20, 20)).addTo(self.view).border(1)
VStack(
View.bg("random").pin(30, 30),
View.bg("random").pin(60, 60),
View.bg("random").pin(90, 90)
).gap(20).pin(.xy(20, 140)).addTo(self.view).border(1)
VStack(
HStack(
View.bg("random").pin(30, 30),
"<-->",
View.bg("random").pin(30, 30)
),
View.bg("random").pin(30).margin(-10),
View.bg("random").pin(30).margin(0, 10)
).embedIn(self.view, 390, 20, 20).border(1)
*/
public func HStack(_ items: Any...) -> CPKStackView {
let stack = CPKStackView()
stack.axis = .horizontal
stack.alignment = .center
stack.addArrangedSubviews(items: items)
return stack
}
public func VStack(_ items: Any...) -> CPKStackView {
let stack = CPKStackView()
stack.axis = .vertical
stack.alignment = .left
stack.addArrangedSubviews(items: items)
return stack
}
public extension CPKStackView {
/**
* Universal gap between items.
* For individual gap between items, use Number as StackView item.
* Usages:
.gap(10)
*/
@discardableResult public func gap(_ spacing: CGFloat) -> Self {
self.spacing = spacing
return self
}
/**
* StackView alignment
* For HStack, the default alignment is .center.
* For VStack, the default alignment is .left.
* Usages:
.align(.top)
.align(.fill)
...
*/
@discardableResult public func align(_ alignment: CPKStackAlignment) -> Self {
self.alignment = alignment
return self
}
}

447
Cupcake/StaticTable.swift Normal file
View File

@ -0,0 +1,447 @@
//
// StaticTable.swift
// Cupcake
//
// Created by nerdycat on 2017/4/21.
// Copyright © 2017 nerdycat. All rights reserved.
//
import UIKit
/**
* PlainTable and GroupTable allow you to create static tableView with ease.
* Usages:
PlainTable("Option1", "Option2", "Option3").singleCheck().embedIn(self.view)
PlainTable(["Option1", "Option2", "Option3"]).multiCheck().embedIn(self.view)
GroupTable(
Section(
Row.str("Row1").detail("detail1"),
Row.str("Row2").arrow().onClick({_ in print("row2") })
),
Section(
Row.str("Row3").detail("detail3").style(.subtitle),
Row.str(AttStr("Row4").color("red")).switchOn().onChange({ row in
print(row.switchView.isOn)
})
).header(20),
Row.custom({ row in
Label.str("Delete").color("red").align(.center).embedIn(row.cell.contentView)
})
).embedIn(self.view)
*/
public func PlainTable(_ sectionsOrRows: Any...) -> StaticTableView {
let tableView = StaticTableView(sectionsOrRows: sectionsOrRows, style: .plain)
tableView.tableFooterView = UIView()
return tableView
}
public func GroupTable(_ sectionsOrRows: Any...) -> StaticTableView {
return StaticTableView(sectionsOrRows: sectionsOrRows, style: .grouped)
}
public func Section(_ rowsOrStrings: Any...) -> StaticSection {
return StaticSection(rowsOrStrings: rowsOrStrings)
}
public var Row: StaticRow {
return StaticRow()
}
public extension StaticTableView {
/**
* Setting font for all titleLabels.
* If you only want to change one particular titleLabel's font, use AttStr instead.
* font use Font() internally, so it can take any kind of values that Font() supported.
* See Font.swift for more information.
* Usages:
.font(15)
.font("20")
.font("body")
.font(someLabel.font)
...
*/
@discardableResult func font(_ any: Any) -> Self {
self.textFont = any
return self
}
/**
* Setting textColor for all titleLabels.
* If you only want to change one particular titleLabel's textColor, use AttStr instead.
* color use Color() internally, so it can take any kind of values that Color() supported.
* See Color.swift for more information.
* Usages:
.color(@"red")
.color(@"#F00")
.color(@"255,0,0")
.color(someLabel.textColor)
...
*/
@discardableResult func color(_ any: Any) -> Self {
self.textColor = any
return self
}
/**
* Setting font for all detailTextLabels.
* If you only want to change one particular detailTextLabel's font, use AttStr instead.
* font use Font() internally, so it can take any kind of values that Font() supported.
* See Font.swift for more information.
* Usages:
.font(15)
.font("20")
.font("body")
.font(someLabel.font)
...
*/
@discardableResult func detailFont(_ any: Any) -> Self {
self.detailFont = any
return self
}
/**
* Setting textColor for all detailTextLabels.
* If you only want to change one particular detailTextLabel's textColor, use AttStr instead.
* color use Color() internally, so it can take any kind of values that Color() supported.
* See Color.swift for more information.
* Usages:
.color(@"red")
.color(@"#F00")
.color(@"255,0,0")
.color(someLabel.textColor)
...
*/
@discardableResult func detailColor(_ any: Any) -> Self {
self.detailColor = any
return self
}
/**
* Setting height for all cells.
* Usages:
.rowHeight(50)
.rowHeight(-1) //negative value means use UITableViewAutomaticDimension
*/
@discardableResult func rowHeight(_ height: CGFloat) -> Self {
self.cellHeight = height
return self
}
/**
* Setting separator line indent for all cells.
* Usages:
.lineIndent(0)
.lineIndent(10)
*/
@discardableResult func lineIndent(_ indent: CGFloat) -> Self {
self.separatorIndent = indent
return self
}
/**
* Shorthand for setting disclosureIndicator for all cells.
* Usages:
.arrow()
.arrow(false)
*/
@discardableResult func arrow(_ showArrow: Bool = true) -> Self {
self.accessoryType = (showArrow ? .disclosureIndicator : nil)
return self
}
/**
* Call when cell is about to appear.
* You can use this method to further customize cell.
* Be aware of retain cycle when using this method.
* Usages:
.custom({ [weak self] row in
let cell = row.cell
let indexPath = row.indexPath
...
})
*/
@discardableResult func custom(_ handler: @escaping (StaticRow)->()) -> Self {
self.customHandler = handler
return self
}
/**
* Call when cell is being selected.
* By passing a callback handler, all the cells will become selectable.
* Be aware of retain cycle when using this method.
* Usages:
.onClick({ [weak self] row in
let cell = row.cell
let indexPath = row.indexPath
...
})
*/
@discardableResult override func onClick(_ callback: @escaping (StaticRow)->()) -> Self {
self.onClickHandler = callback
return self
}
}
public extension StaticSection {
/**
* Setting section header.
* By default, grouped style tableView' sections have default header and footer height.
* Header can be Number, String, View.
* Usages:
.header(10) //10 point header height, useful for changing GroupTableView's section gap.
.header("Header1") //header with string
.header(headerView) //header with view
*/
@discardableResult func header(_ any: Any) -> Self {
self.headerValue = any
return self
}
/**
* Setting section footer.
* By default, grouped style tableView' sections have default header and footer height.
* Footer can be Number, String, View.
* Usages:
.footer(10) //10 point footer height, useful for changing GroupTableView's section gap.
.footer("Footer1") //footer with string
.footer(footerView) //footer with view
*/
@discardableResult func footer(_ any: Any) -> Self {
self.footerValue = any
return self
}
/**
* Turning on single-check behavior for section.
* To find out which cells are being checked, use checkedIndexPaths property on StaticTableView.
* Usages:
.singleCheck() //use system checkmark
.singleCheck("checked") //use custom image
.singleCheck("checked", "unchecked") //use custom images
*/
@discardableResult func singleCheck(_ checkedImage: Any? = nil, _ uncheckedImage: Any? = nil) -> Self {
self.enableSingleCheck = true
self.checkedImage = checkedImage != nil ? Img(checkedImage!) : nil
self.uncheckedImage = uncheckedImage != nil ? Img(uncheckedImage!) : nil
return self
}
/**
* Turning on multi-mheck behavior for section.
* To find out which cells are being checked, use checkedIndexPaths property on StaticTableView.
* Usages:
.multiCheck() //use system checkmark
.multiCheck("checked") //use custom image
.multiCheck("checked", "unchecked") //use custom images
*/
@discardableResult func multiCheck(_ checkedImage: Any? = nil, _ uncheckedImage: Any? = nil) -> Self {
self.enableMultiCheck = true
self.checkedImage = checkedImage != nil ? Img(checkedImage!) : nil
self.uncheckedImage = uncheckedImage != nil ? Img(uncheckedImage!) : nil
return self
}
}
public extension StaticRow {
/**
* Setting image for cell.
* img use Img() internally, so it can take any kind of values that Img() supported.
* See Img.swift for more information.
* Usages:
.img("cat")
.img("#button-background")
.img("$home-icon")
.img(someImage)
...
*/
@discardableResult func img(_ any: Any) -> Self {
self.image = Img(any)
return self
}
/**
* Setting text or attributedText for cell's textLabel.
* str can take any kind of value, even primitive type like Int.
* Usages:
.str(1024)
.str("hello world")
.str( AttStr("hello world").strikethrough() )
...
*/
@discardableResult func str(_ any: Any) -> Self {
self.text = any
return self
}
/**
* Setting text or attributedText for cell's detailTextLabel.
* detail can take any kind of value, even primitive type like Int.
* Usages:
.detail(1024)
.detail("hello world")
.detail( AttStr("hello world").strikethrough() )
...
*/
@discardableResult func detail(_ any: Any) -> Self {
self.detailText = any
return self
}
/**
* Setting cell style.
* Usages:
.style(.subtitle)
.style(.value2)
...
*/
@discardableResult func style(_ style: UITableViewCellStyle) -> Self {
self.cellStyle = style
return self
}
/**
* Setting accessoryType or accessoryView.
* Usages:
.accessory(.disclosureIndicator)
.accessory(.detailButton)
.accessory(.view(someView)) //setting accessoryView
...
*/
@discardableResult func accessory(_ type: CPKTableViewCellAccessoryType) -> Self {
self.accessoryType = type
return self
}
/**
* Shorthand for setting accessoryType with .disclosureIndicator.
* Usages:
.arrow()
.arrow(false)
*/
@discardableResult func arrow(_ showArrow: Bool = true) -> Self {
self.accessoryType = showArrow ? .disclosureIndicator : CPKTableViewCellAccessoryType.none
return self
}
/**
* Shorthand for setting accessoryType with .checkmark.
* Usages:
.check()
.check(false)
*/
@discardableResult func check(_ checked: Bool = true) -> Self {
self.accessoryType = checked ? .checkmark : CPKTableViewCellAccessoryType.none
return self
}
/**
* Shorthand for setting accessoryView with UISwitch.
* Usages:
.switchOn()
.switchOn(false)
*/
@discardableResult func switchOn(_ isOn: Bool = true) -> Self {
let sw: UISwitch! = self.switchView ?? {self.switchView = UISwitch(); return self.switchView}()
sw.isOn = isOn
self.accessory(.view(sw))
return self
}
/**
* Settting cell height.
* Usages:
.height(50)
.height(-1) //negative value means use UITableViewAutomaticDimension
*/
@discardableResult func height(_ height: CGFloat) -> Self {
self.cellHeight = height
return self
}
/**
* Setting separator line indent.
* Usages:
.lineIndent(10)
*/
@discardableResult func lineIndent(_ indent: CGFloat) -> Self {
self.separatorIndent = indent
return self
}
/**
* Call when cell is being selected.
* By passing a callback handler, the cell become selectable.
* Be aware of retain cycle when using this method.
* Usages:
.onClick({ [weak self] row in
let cell = row.cell
let indexPath = row.indexPath
...
})
*/
@discardableResult override func onClick(_ callback: @escaping (StaticRow)->()) -> Self {
self.onClickHandler = callback
return self
}
/**
* Call when cell's detailButton is being clicked.
* Be aware of retain cycle when using this method.
* Usages:
.onButton({ [weak self] row in
let cell = row.cell
let indexPath = row.indexPath
...
})
*/
@discardableResult func onButton(_ callback: @escaping (StaticRow)->()) -> Self {
self.onButtonHandler = callback
return self
}
/**
* Call when switch's value is changed.
* Be aware of retain cycle when using this method.
* Usages:
.onChange({ [weak self] row in
let sw = row.switchView
let indexPath = row.indexPath
...
})
*/
@discardableResult func onChange(_ callback: @escaping (StaticRow)->()) -> Self {
self.onChangeHandler = callback
return self
}
/**
* Call when cell is about to appear.
* You can use this method to further customize cell.
* Usages:
.custom({ row in
let cell = row.cell
let indexPath = row.indexPath
...
})
*/
@discardableResult func custom(_ handler: @escaping (StaticRow)->()) -> Self {
self.customHandler = handler
return self
}
}

160
Cupcake/Str.swift Normal file
View File

@ -0,0 +1,160 @@
//
// Str.swift
// Cupcake
//
// Created by nerdycat on 2017/3/17.
// Copyright © 2017 nerdycat. All rights reserved.
//
import UIKit
/**
* Create String from formatted string.
* Also can be use to convert any type to String.
* Usages:
Str("1+1=%d", 2) //"1+1=2"
Str(1024) //"1024"
Str(3.14) //"3.14"
...
*/
public func Str(_ any: Any, _ arguments: CVarArg...) -> String {
let str = String(format: String(describing: any), arguments: Array(arguments))
return str
}
public extension String {
/**
* Return substring from index or to some particular string.
* Usages:
"hello world".subFrom(6) //"world"
"hello world".subFrom(-5) //"world"
"hello world".subFrom("wo") //"world"
*/
@discardableResult public func subFrom(_ indexOrSubstring: Any) -> String {
if var index = indexOrSubstring as? Int {
if index < 0 { index += self.characters.count }
return self.substring(from: self.index(self.startIndex, offsetBy: index))
} else if let substr = indexOrSubstring as? String {
if let range = self.range(of: substr) {
return self.substring(from: range.lowerBound)
}
}
return ""
}
/**
* Return substring to index or to some particular string.
* Usages:
"hello world".subTo(5) //"hello"
"hello world".subTo(-6) //"hello"
"hello world".subTo(" ") //"hello"
*/
@discardableResult public func subTo(_ indexOrSubstring: Any) -> String {
if var index = indexOrSubstring as? Int {
if index < 0 { index += self.characters.count }
return self.substring(to: self.index(self.startIndex, offsetBy: index))
} else if let substr = indexOrSubstring as? String {
if let range = self.range(of: substr) {
return self.substring(to: range.lowerBound)
}
}
return ""
}
/**
* Return substring at index or in range.
* Usages:
"hello world".subAt(1) //"e"
"hello world".subAt(1..<4) //"ell"
*/
@discardableResult public func subAt(_ indexOrRange: Any) -> String {
if let index = indexOrRange as? Int {
return String(self[self.index(self.startIndex, offsetBy: index)])
} else if let range = indexOrRange as? Range<String.Index> {
return self.substring(with: range)
} else if let range = indexOrRange as? Range<Int> {
let lower = self.index(self.startIndex, offsetBy: range.lowerBound)
let upper = self.index(self.startIndex, offsetBy: range.upperBound)
return self.substring(with: lower..<upper)
} else if let range = indexOrRange as? CountableRange<Int> {
let lower = self.index(self.startIndex, offsetBy: range.lowerBound)
let upper = self.index(self.startIndex, offsetBy: range.upperBound)
return self.substring(with: lower..<upper)
} else if let range = indexOrRange as? ClosedRange<Int> {
let lower = self.index(self.startIndex, offsetBy: range.lowerBound)
let upper = self.index(self.startIndex, offsetBy: range.upperBound + 1)
return self.substring(with: lower..<upper)
} else if let range = indexOrRange as? CountableClosedRange<Int> {
let lower = self.index(self.startIndex, offsetBy: range.lowerBound)
let upper = self.index(self.startIndex, offsetBy: range.upperBound + 1)
return self.substring(with: lower..<upper)
} else if let range = indexOrRange as? NSRange {
let lower = self.index(self.startIndex, offsetBy: range.location)
let upper = self.index(self.startIndex, offsetBy: range.location + range.length)
return self.substring(with: lower..<upper)
}
return ""
}
/**
* Return substring that match the pattern.
* Usages:
"abc123".subMatch("\\d+") //"123"
*/
@discardableResult public func subMatch(_ pattern: String) -> String {
let options = NSRegularExpression.Options(rawValue: 0)
if let exp = try? NSRegularExpression(pattern: pattern, options: options) {
let options = NSRegularExpression.MatchingOptions(rawValue: 0)
let matchRange = exp.rangeOfFirstMatch(in: self,
options:options,
range: NSMakeRange(0, self.characters.count))
if matchRange.location != NSNotFound {
return self.subAt(matchRange)
}
}
return ""
}
/**
* Replace substring with template.
* Usages:
"abc123".subReplace("abc", "ABC") //ABC123
"abc123".subReplace("([a-z]+)(\\d+)", "$2$1") //"123abc"
*/
@discardableResult public func subReplace(_ pattern: String, _ template: String) -> String {
let options = NSRegularExpression.Options(rawValue: 0)
if let exp = try? NSRegularExpression(pattern: pattern, options: options) {
let options = NSRegularExpression.MatchingOptions(rawValue: 0)
return exp.stringByReplacingMatches(in: self,
options: options,
range: NSMakeRange(0, self.characters.count),
withTemplate: template)
}
return ""
}
}

160
Cupcake/Styles.swift Normal file
View File

@ -0,0 +1,160 @@
//
// Styles.swift
// Cupcake
//
// Created by nerdycat on 2017/5/3.
// Copyright © 2017 nerdycat. All rights reserved.
//
import UIKit
/**
* Some of the chainable property can be set as style.
* There are two type of styles:
1) Global style:
Styles("GlobalStyleName").color("red").font(15)
2) Local style:
let localStyle = Styles.color("red").font(15)
* After creation, you can apply styles to any UIView:
Label.styles("GlobalStyleName", localStyle)
Button.styles(localStyle).font(17)
*/
public var Styles: StylesMaker {
return StylesMaker()
}
public func Styles(_ name: String) -> StylesMaker {
let makers = StylesMaker()
StylesMaker.globalStyles[name] = makers
return makers
}
public extension StylesMaker {
//View
@discardableResult public func bg(_ any: Any) -> Self {
return addStyle(key: #function, value: any)
}
@discardableResult public func tint(_ any: Any) -> Self {
return addStyle(key: #function, value: any)
}
@discardableResult public func radius(_ cornerRadius: CGFloat) -> Self {
return addStyle(key: #function, value: cornerRadius)
}
@discardableResult public func border(_ borderWidth: CGFloat, _ borderColor: Any? = nil) -> Self {
var dict: Dictionary<String, Any> = ["borderWidth": borderWidth]
if let color = borderColor {
dict["borderColor"] = color
}
return addStyle(key: #function, value: dict)
}
@discardableResult public func shadow(_ shadowOpacity: CGFloat,
_ shadowRadius: CGFloat = 3,
_ shadowOffsetX: CGFloat = 0,
_ shadowOffsetY: CGFloat = 3) -> Self {
let dict: Dictionary<String, CGFloat> = ["opacity": shadowOpacity,
"radius": shadowRadius,
"offsetX": shadowOffsetX,
"offsetY": shadowOffsetY]
return addStyle(key: #function, value: dict)
}
@discardableResult public func pin(_ options: CPKViewPinOptions...) -> Self {
return addStyle(key: #function, value: options)
}
//Label
@discardableResult public func str(_ any: Any) -> Self {
return addStyle(key: #function, value: any)
}
@discardableResult public func font(_ any: Any) -> Self {
return addStyle(key: #function, value: any)
}
@discardableResult public func color(_ any: Any) -> Self {
return addStyle(key: #function, value: any)
}
@discardableResult public func lines(_ numberOfLines: CGFloat = 0) -> Self {
return addStyle(key: #function, value: numberOfLines)
}
@discardableResult public func lineGap(_ lineSpacing: CGFloat) -> Self {
return addStyle(key: #function, value: lineSpacing)
}
@discardableResult public func align(_ textAlignment: NSTextAlignment) -> Self {
return addStyle(key: #function, value: textAlignment)
}
//ImageView
@discardableResult public func img(_ any: Any) -> Self {
return addStyle(key: #function, value: any)
}
@discardableResult public func mode(_ contentMode: UIViewContentMode) -> Self {
return addStyle(key: #function, value: contentMode)
}
//Button
@discardableResult public func highColor(_ any: Any) -> Self {
return addStyle(key: #function, value: any)
}
@discardableResult public func highBg(_ any: Any) -> Self {
return addStyle(key: #function, value: any)
}
@discardableResult public func padding(_ contentEdgeInsets: CGFloat...) -> Self {
return addStyle(key: #function, value: contentEdgeInsets)
}
@discardableResult public func gap(_ spacing: CGFloat) -> Self {
return addStyle(key: #function, value: spacing)
}
@discardableResult public func reversed(_ reversed: Bool = true) -> Self {
return addStyle(key: #function, value: reversed)
}
//TextField
@discardableResult public func maxLength(_ length: CGFloat) -> Self {
return addStyle(key: #function, value: length)
}
@discardableResult public func secure(_ secureTextEntry: Bool = true) -> Self {
return addStyle(key: #function, value: secureTextEntry)
}
@discardableResult public func keyboard(_ keyboardType: UIKeyboardType) -> Self {
return addStyle(key: #function, value: keyboardType)
}
@discardableResult public func returnKey(_ returnKeyType: UIReturnKeyType) -> Self {
return addStyle(key: #function, value: returnKeyType)
}
@discardableResult public func clearMode(_ clearButtonMode: UITextFieldViewMode) -> Self {
return addStyle(key: #function, value: clearButtonMode)
}
}

198
Cupcake/TextField.swift Normal file
View File

@ -0,0 +1,198 @@
//
// TextField.swift
// Cupcake
//
// Created by nerdycat on 2017/3/24.
// Copyright © 2017 nerdycat. All rights reserved.
//
import UIKit
var TextField: UITextField {
let textField = UITextField()
textField.enablesReturnKeyAutomatically = true
textField.returnKeyType = .done
return textField
}
extension UITextField {
/**
* Setting text or attributedText
* str can take any kind of value, even primitive type like Int.
* Usages:
.str(1024)
.str("hello world")
.str( AttStr("hello world").strikethrough() )
...
*/
@discardableResult public func str(_ any: Any) -> Self {
if let attStr = any as? NSAttributedString {
self.attributedText = attStr
} else {
self.text = String(describing: any)
}
return self
}
/**
* Setting placeholder or attributedPlaceholder
* hint can take any kind of value, even primitive type like Int.
* Usages:
.hint(1024)
.hint("Enter your name")
.hint( AttStr("Enter your name").font(13) )
*/
@discardableResult public func hint(_ any: Any) -> Self {
if let attStr = any as? NSAttributedString {
self.attributedPlaceholder = attStr
} else {
self.placeholder = String(describing: any)
}
return self
}
/**
* Setting font
* font use Font() internally, so it can take any kind of values that Font() supported.
* See Font.siwft for more information.
* Usages:
.font(15)
.font("20")
.font("body")
.font(someLabel.font)
...
**/
@discardableResult public func font(_ any: Any) -> Self {
self.font = Font(any)
return self
}
/**
* Setting textColor
* color use Color() internally, so it can take any kind of values that Color() supported.
* See Color.swift for more information.
* Usages:
.color(@"red")
.color(@"#F00")
.color(@"255,0,0")
.color(someLabel.textColor)
...
*/
@discardableResult public func color(_ any: Any) -> Self {
self.textColor = Color(any)
return self
}
/**
* Max input length
* Usages:
.maxLength(10)
*/
@discardableResult public func maxLength(_ length: CGFloat) -> Self {
self.cpkMaxLength = Int(length)
return self
}
/**
* contentEdgeInsets for UITextField
* Usages:
.padding(10) //top: 10, left: 10, bottom: 10, right: 10
.padding(10, 20) //top: 10, left: 20, bottom: 10, right: 20
.padding(10, 20, 30) //top: 10, left: 20, bottom: 0 , right: 30
.padding(10, 20, 30, 40) //top: 10, left: 20, bottom: 30, right: 40
*/
@discardableResult public func padding(_ contentEdgeInsets: CGFloat...) -> Self {
cpk_updatePadding(contentEdgeInsets, forView: self)
return self
}
/**
* secureTextEntry
* Usages:
.secure() //secureTextEntry = true
.secure(false) //secureTextEntry = false
*/
@discardableResult public func secure(_ secureTextEntry: Bool = true) -> Self {
self.isSecureTextEntry = secureTextEntry
return self
}
/**
* Setting textAlignment
* Usages:
.align(.center)
.align(.justified)
...
*/
@discardableResult public func align(_ textAlignment: NSTextAlignment) -> Self {
self.textAlignment = textAlignment
return self
}
/**
* Setting keyboardType
* Usages:
.keyboard(.numberPad)
.keyboard(.emailAddress)
...
*/
@discardableResult public func keyboard(_ keyboardType: UIKeyboardType) -> Self {
self.keyboardType = keyboardType
return self
}
/**
* Setting returnKeyType
* Usages:
.returnKey(.send)
.returnKey(.google)
...
*/
@discardableResult public func returnKey(_ returnKeyType: UIReturnKeyType) -> Self {
self.returnKeyType = returnKeyType
return self
}
/**
* Setting clearButtonMode
* Usages:
.clearMode(.whileEditing)
.clearMode(.always)
...
*/
@discardableResult public func clearMode(_ clearButtonMode: UITextFieldViewMode) -> Self {
self.clearButtonMode = clearButtonMode
return self
}
/**
* Setup text changed handler.
* Be aware of retain cycle when using this method.
* Usages:
.onChange({ textField in /* ... */ })
.onChange({ _ in /* ... */ })
.onChange({ [weak self] textField in /* ... */ }) //capture self as weak reference when needed
*/
@discardableResult func onChange(_ closure: @escaping (UITextField)->()) -> Self {
self.cpkTextChangedClosure = cpk_generateCallbackClosure(closure, nil)
return self
}
/**
* Setup enter finished handler.
* The handler will be called when user click the return button.
* Be aware of retain cycle when using this method.
* Usages:
.onFinish({ textField in /* ... */ })
.onFinish({ _ in /* ... */ })
.onFinish({ [weak self] textField in /* ... */ }) //capture self as weak reference when needed
*/
@discardableResult func onFinish(_ closure: @escaping (UITextField)->()) -> Self {
self.cpkDidEndOnExistClosure = cpk_generateCallbackClosure(closure, nil)
return self
}
}

130
Cupcake/TextView.swift Normal file
View File

@ -0,0 +1,130 @@
//
// TextView.swift
// Cupcake
//
// Created by nerdycat on 2017/3/25.
// Copyright © 2017 nerdycat. All rights reserved.
//
import UIKit
var TextView: UITextView {
return UITextView().font(17)
}
extension UITextView {
/**
* Setting text or attributedText
* str can take any kind of value, even primitive type like Int.
* Usages:
.str(1024)
.str("hello world")
.str( AttStr("hello world").strikethrough() )
...
*/
@discardableResult public func str(_ any: Any) -> Self {
if let attStr = any as? NSAttributedString {
self.attributedText = attStr
} else {
self.text = String(describing: any)
}
return self
}
/**
* Setting placeholder or attributedPlaceholder
* hint can take any kind of value, even primitive type like Int.
* Usages:
.hint(1024)
.hint("Enter here")
.hint( AttStr("Enter here").font(13) )
*/
@discardableResult public func hint(_ any: Any) -> Self {
cpk_setPlaceholder(any)
return self
}
/**
* Setting font
* font use Font() internally, so it can take any kind of values that Font() supported.
* See Font.siwft for more information.
* Usages:
.font(15)
.font("20")
.font("body")
.font(someLabel.font)
...
**/
@discardableResult public func font(_ any: Any) -> Self {
self.font = Font(any)
return self
}
/**
* Setting textColor
* color use Color() internally, so it can take any kind of values that Color() supported.
* See Color.swift for more information.
* Usages:
.color(@"red")
.color(@"#F00")
.color(@"255,0,0")
.color(someLabel.textColor)
...
*/
@discardableResult public func color(_ any: Any) -> Self {
self.textColor = Color(any)
return self
}
/**
* Max input length
* Usages:
.maxLength(10)
*/
@discardableResult public func maxLength(_ length: CGFloat) -> Self {
self.cpkMaxLength = Int(length)
return self
}
/**
* contentEdgeInsets for UITextView
* Usages:
.padding(10) //top: 10, left: 10, bottom: 10, right: 10
.padding(10, 20) //top: 10, left: 20, bottom: 10, right: 20
.padding(10, 20, 30) //top: 10, left: 20, bottom: 0 , right: 30
.padding(10, 20, 30, 40) //top: 10, left: 20, bottom: 30, right: 40
*/
@discardableResult public func padding(_ contentEdgeInsets: CGFloat...) -> Self {
cpk_updatePadding(contentEdgeInsets, forView: self)
return self
}
/**
* Setting textAlignment
* Usages:
.align(.center)
.align(.justified)
...
*/
@discardableResult public func align(_ textAlignment: NSTextAlignment) -> Self {
self.textAlignment = textAlignment
return self
}
/**
* Setup text changed handler.
* Be aware of retain cycle when using this method.
* Usages:
.onChange({ textView in /* ... */ })
.onChange({ _ in /* ... */ })
.onChange({ [weak self] textView in /* ... */ }) //capture self as weak reference when needed
*/
@discardableResult func onChange(_ closure: @escaping (UITextView)->()) -> Self {
self.cpkTextChangedClosure = cpk_generateCallbackClosure(closure, nil)
return self
}
}

362
Cupcake/View.swift Normal file
View File

@ -0,0 +1,362 @@
//
// View.swift
// Cupcake
//
// Created by nerdycat on 2017/3/22.
// Copyright © 2017 nerdycat. All rights reserved.
//
import UIKit
public var View: UIView {
let view = UIView()
return view
}
extension UIView {
/**
* Setting background with Color or Image.
* bg use Color() internally, so it can take any kind of values that Color() supported.
* See Color.swift for more information.
* Usages:
.bg(@"red")
.bg(@"#F00")
.bg(@"255,0,0")
.bg(someView.backgroundColor)
.bg("cat") //using image
...
*/
@discardableResult public func bg(_ any: Any) -> Self {
self.backgroundColor = Color(any) ?? Color(Img(any))
return self
}
/**
* Setting tintColor
* tint use Color() internally, so it can take any kind of values that Color() supported.
* See Color.swift for more information.
* Usages:
.tint("red")
.tint("#00F")
*/
@discardableResult public func tint(_ any: Any) -> Self {
self.tintColor = Color(any)
return self
}
/**
* Setting cornerRadius
* radius support auto rounding, which is a very useful trick when working with AutoLayout.
* Auto rounding will alway set cornerRadius to half of the height not matter what size it is.
* Usages:
.radius(10)
.radius(-1) //passing negative number means using auto rounding
*/
@discardableResult public func radius(_ cornerRadius: CGFloat) -> Self {
if cornerRadius >= 0 {
self.layer.cornerRadius = cornerRadius
self.cpkAutoRoundingRadius = false
} else {
self.layer.cornerRadius = self.bounds.height / 2
self.cpkAutoRoundingRadius = true
}
cpk_masksToBoundsIfNeed()
return self
}
/**
* Setting border with borderWidth and borderColor (optional).
* border's second argument use Color() internally, so it can take any kind of values that Color() supported.
* See Color.swift for more information.
* Usages:
.border(1)
.border(1, "red")
*/
@discardableResult public func border(_ borderWidth: CGFloat, _ borderColor: Any? = nil) -> Self {
self.layer.borderWidth = borderWidth
self.layer.borderColor = Color(borderColor)?.cgColor
return self
}
/**
* Drop shadow with opacity, radius (optional) and offset (optional).
* Usages:
.shadow(1)
.shadow(0.7, 2)
.shadow(0.7, 3, 0, 0)
*/
@discardableResult public func shadow(_ shadowOpacity: CGFloat,
_ shadowRadius: CGFloat = 3,
_ shadowOffsetX: CGFloat = 0,
_ shadowOffsetY: CGFloat = 3) -> Self {
self.layer.shadowOpacity = Float(shadowOpacity)
self.layer.shadowRadius = shadowRadius
self.layer.shadowOffset = CGSize(width: shadowOffsetX, height: shadowOffsetY)
return self
}
/**
* Apply at most 4 styles to view.
* See Styles.swift for more information.
Usages:
.styles(myStyle)
.styles(myStyle1, myStyle2, "globalStyle1")
*/
@discardableResult public func styles(_ s1: Any, _ s2: Any? = nil, _ s3: Any? = nil, _ s4: Any? = nil) -> Self {
var array = Array<Any>()
array.append(s1)
if s2 != nil { array.append(s2!) }
if s3 != nil { array.append(s3!) }
if s4 != nil { array.append(s4!) }
for styles in array {
if let style = styles as? StylesMaker {
style.applyTo(view: self)
} else if let name = styles as? String {
if let style = StylesMaker.globalStyles[name] {
style.applyTo(view: self)
}
}
}
return self
}
/**
* Setting touch insets.
* Very useful for extending touch area.
* It can take variety of forms.
* Usages:
.touchInsets(10) //top: 10, left: 10, bottom: 10, right: 10
.touchInsets(10, 20) //top: 10, left: 20, bottom: 10, right: 20
.touchInsets(10, 20, 30) //top: 10, left: 20, bottom: 0 , right: 30
.touchInsets(10, 20, 30, 40) //top: 10, left: 20, bottom: 30, right: 40
*/
@discardableResult public func touchInsets(_ p1: Any, _ p2: Any? = nil, _ p3: Any? = nil, _ p4: Any? = nil) -> Self {
self.cpkTouchInsets = cpk_edgeInsetsFromParameters(p1, p2, p3, p4)
return self
}
/**
* Setup click handler.
* This will automatically set isUserInteractionEnabled to true as well.
* Be aware of retain cycle when using this method.
* Usages:
.onClick({ view in /* ... */ }) //the view being clicked is pass as parameter
.onClick({ _ in /* ... */ }) //if you don't care at all
.onClick({ [weak self] _ in /* ... */ }) //capture self as weak reference when needed
*/
@discardableResult func onClick(_ closure: @escaping (UIView)->()) -> Self {
cpk_onClick(closure, nil)
return self
}
/**
* Add current view to superview.
* Usages:
.addTo(superView)
*/
@discardableResult public func addTo(_ superview: UIView) -> Self {
superview.addSubview(self)
return self
}
/**
* Add multiply subviews at the same time.
* Usages:
.addSubview(view1, view2, view3)
*/
@discardableResult public func addSubviews(_ children: Any...) -> Self {
func addChildren(_ children: Array<Any>) {
for child in children {
if let view = child as? UIView {
self.addSubview(view)
} else if let array = child as? Array<UIView> {
addChildren(array)
}
}
}
addChildren(children)
return self
}
}
public extension UIView {
/**
* Setting layout margins.
* Very useful when embed in StackView.
* It can take variety of forms.
* Usages:
.margin(10) //top: 10, left: 10, bottom: 10, right: 10
.margin(10, 20) //top: 10, left: 20, bottom: 10, right: 20
.margin(10, 20, 30) //top: 10, left: 20, bottom: 0 , right: 30
.margin(10, 20, 30, 40) //top: 10, left: 20, bottom: 30, right: 40
*/
@discardableResult public func margin(_ p1: Any, _ p2: Any? = nil, _ p3: Any? = nil, _ p4: Any? = nil) -> Self {
self.layoutMargins = cpk_edgeInsetsFromParameters(p1, p2, p3, p4)
return self
}
/**
* An easy way to setup constraints relative to superview.
* You can pass multiply options at the same time.
* Usages:
.pin(.x(20)) //add left constraint
.pin(.y(20)) //add top constraint
.pin(.xy(20, 20)) //and left and top constraints
.pin(.w(100)) //add width constraint
.pin(.h(100)) //add height constraint
.pin(.wh(100, 100)) //add width and height constraints
.pin(.xywh(20, 20, 100, 100)) //add left, top, width and height constraints
.pin(.maxX(-20)) //add right constraint
.pin(.maxY(-20)) //add bottom constraint
.pin(.maxXY(-20, -20)) //add right and bottom constraints
.pin(.centerX(0)) //add centerX constriant
.pin(.centerY(0)) //add centerY constraint
.pin(.centerXY(0, 0)) //add centerX and centerY constraints
.pin(.center) //add centerX and centerY constraints with offset of 0
.pin(.whRatio(2)) //width = height * 2 constraint
.pin(.ratio) //width = height * currentSizeRatio constraint, very useful for ImageView
.pin(.lowHugging) //Low content hugging priority, very useful when embed in StackView.
//The lowest hugging view will take as much space as possible.
.pin(.lowRegistance) //Low content compression resistance priority, very useful when embed in StackView.
//The lowest resistance view will be compressed when there are no more space available.
.pin(100) //shorthand for .pin(.h(100))
.pin(100, 100) //shorthand for .pin(.wh(100, 100))
.pin(20, 20, 100, 100) //shorthand for .pin(.xywh(20, 20, 100, 100))
//pass multiply options at the same time
.pin(.xy(20, 20), .maxX(-20), 100)
.pin(100, 100, .center)
...
*/
@discardableResult public func pin(_ options: CPKViewPinOptions...) -> Self {
cpk_pinOptions(options, forView: self)
return self
}
/**
* Making constraints just like SnapKit.
* makeCons will only create new constraints when needed.
* Usages:
.makeCons({
_.left.top.equal(someView).offset(20, 20)
_.size.equal(100, 100)
})
*/
@discardableResult public func makeCons(_ closure: (ConsMaker)->()) -> Self {
let maker = ConsMaker(firstItem: self)
closure(maker)
maker.updateConstraints()
return self
}
/**
* Remake constarints just like SnapKit.
* remakeCons will remove all previous installed constarints first.
* Usages:
.makeCons({
_.center.equal(someView)
_.size.equal(anotherView)
})
*/
@discardableResult public func remakeCons(_ closure: (ConsMaker)->()) -> Self {
let maker = ConsMaker(firstItem: self)
closure(maker)
maker.remakeConstraints()
return self
}
/**
* Embed self into superview with optional edge constraints.
* Passing nil means no constraint.
* Usages:
.embedIn(superview) //top: 0, left: 0, bottom: 0, right: 0
.embedIn(superview, 10) //top: 10, left: 10, bottom: 10, right: 10
.embedIn(superview, 10, 20) //top: 10, left: 20, bottom: 10, right: 20
.embedIn(superview, 10, 20, 30) //top: 10, left: 20, right: 30
.embedIn(superview, 10, 20, 30, 40) //top: 10, left: 20, bottom: 30, right: 40
.embedIn(superview, 10, 20, nil) //top: 10, left: 20
*/
@discardableResult
public func embedIn(_ superview: UIView,
_ p1: Any? = "", _ p2: Any? = "",
_ p3: Any? = "", _ p4: Any? = "") -> Self {
superview.addSubview(self)
let edge = cpk_edgeInsetsTupleFromParameters(p1, p2, p3, p4)
makeCons({
if let top = edge.0 {
$0.top.offset(top)
}
if let left = edge.1 {
$0.left.offset(left)
}
if let bottom = edge.2 {
$0.bottom.offset(-bottom)
}
if let right = edge.3 {
$0.right.offset(-right)
}
})
return self
}
}
public enum CPKViewPinOptions {
case x(CGFloat) //left constraint
case y(CGFloat) //top constraint
case xy(CGFloat, CGFloat) //left and top constraints
case w(CGFloat) //width constraint
case h(CGFloat) //height constraint
case wh(CGFloat, CGFloat) //width and height constraints
case xywh(CGFloat, CGFloat, //left, top, width and height constraints
CGFloat, CGFloat)
case maxX(CGFloat) //right constraint
case maxY(CGFloat) //bottom constraint
case maxXY(CGFloat, CGFloat) //right and bottom constraint
case centerX(CGFloat) //centerX constraint
case centerY(CGFloat) //centerY constraint
case centerXY(CGFloat, CGFloat) //centerX and centerY constraints
case center //centerX and centerY constraints with offset of 0
case whRatio(CGFloat) //width = height * ratio constraint
case ratio //width = height * currentSizeRatio constraint
case hhp(UILayoutPriority) //horizontal content hugging priority
case vhp(UILayoutPriority) //vertical content hugging priority
case hrp(UILayoutPriority) //horizontal content compression resistance priority
case vrp(UILayoutPriority) //vertical content compression resistance priority
case lowHugging //low content hugging priority
case lowResistance //low content compression resistance priority
case ___value(CGFloat) //private usage
}

File diff suppressed because it is too large Load Diff

BIN
res/appstore.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

BIN
res/cupcake.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB