Commit dd421e02 by Daniel Dahan

master: Swift 2.0 release preparation

parents fa7a3d7b ebc0991f
Pod::Spec.new do |s| Pod::Spec.new do |s|
s.name = 'MK' s.name = 'MK'
s.version = '1.12.0' s.version = '1.13.0'
s.license = { :type => "AGPLv3+", :file => "LICENSE" } s.license = { :type => "AGPLv3+", :file => "LICENSE" }
s.summary = 'A Material Design Framework In Swift' s.summary = 'A Material Design Framework In Swift'
s.homepage = 'http://materialkit.io' s.homepage = 'http://materialkit.io'
......
...@@ -15,10 +15,10 @@ ...@@ -15,10 +15,10 @@
65B965871B8BEEC60055B139 /* SideNavigationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65B965861B8BEEC60055B139 /* SideNavigationViewController.swift */; }; 65B965871B8BEEC60055B139 /* SideNavigationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65B965861B8BEEC60055B139 /* SideNavigationViewController.swift */; };
65DBE4201B9A9244000C804F /* Roboto-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 65DBE41E1B9A9244000C804F /* Roboto-Bold.ttf */; }; 65DBE4201B9A9244000C804F /* Roboto-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 65DBE41E1B9A9244000C804F /* Roboto-Bold.ttf */; };
65DBE4211B9A9244000C804F /* Roboto-Thin.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 65DBE41F1B9A9244000C804F /* Roboto-Thin.ttf */; }; 65DBE4211B9A9244000C804F /* Roboto-Thin.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 65DBE41F1B9A9244000C804F /* Roboto-Thin.ttf */; };
962F3E531BACA68C0004B8AD /* NavigationBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 962F3E521BACA68C0004B8AD /* NavigationBarView.swift */; };
962F3E541BACA7FB0004B8AD /* NavigationBarView.swift in Headers */ = {isa = PBXBuildFile; fileRef = 962F3E521BACA68C0004B8AD /* NavigationBarView.swift */; settings = {ATTRIBUTES = (Public, ); }; };
963832421B88DFD80015F710 /* MaterialKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 963832361B88DFD80015F710 /* MaterialKit.framework */; }; 963832421B88DFD80015F710 /* MaterialKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 963832361B88DFD80015F710 /* MaterialKit.framework */; };
9638325A1B88E31A0015F710 /* MaterialKitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 963832581B88E31A0015F710 /* MaterialKitTests.swift */; }; 9638325A1B88E31A0015F710 /* MaterialKitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 963832581B88E31A0015F710 /* MaterialKitTests.swift */; };
963832691B88E5BF0015F710 /* Capture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9638325E1B88E5BF0015F710 /* Capture.swift */; };
9638326A1B88E5BF0015F710 /* CapturePreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9638325F1B88E5BF0015F710 /* CapturePreview.swift */; };
9638326B1B88E5BF0015F710 /* FabButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 963832601B88E5BF0015F710 /* FabButton.swift */; }; 9638326B1B88E5BF0015F710 /* FabButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 963832601B88E5BF0015F710 /* FabButton.swift */; };
9638326C1B88E5BF0015F710 /* FlatButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 963832611B88E5BF0015F710 /* FlatButton.swift */; }; 9638326C1B88E5BF0015F710 /* FlatButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 963832611B88E5BF0015F710 /* FlatButton.swift */; };
963832701B88E5BF0015F710 /* RaisedButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 963832651B88E5BF0015F710 /* RaisedButton.swift */; }; 963832701B88E5BF0015F710 /* RaisedButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 963832651B88E5BF0015F710 /* RaisedButton.swift */; };
...@@ -31,8 +31,6 @@ ...@@ -31,8 +31,6 @@
963832811B89070E0015F710 /* FabButton.swift in Headers */ = {isa = PBXBuildFile; fileRef = 963832601B88E5BF0015F710 /* FabButton.swift */; settings = {ATTRIBUTES = (Public, ); }; }; 963832811B89070E0015F710 /* FabButton.swift in Headers */ = {isa = PBXBuildFile; fileRef = 963832601B88E5BF0015F710 /* FabButton.swift */; settings = {ATTRIBUTES = (Public, ); }; };
963832821B89070E0015F710 /* FlatButton.swift in Headers */ = {isa = PBXBuildFile; fileRef = 963832611B88E5BF0015F710 /* FlatButton.swift */; settings = {ATTRIBUTES = (Public, ); }; }; 963832821B89070E0015F710 /* FlatButton.swift in Headers */ = {isa = PBXBuildFile; fileRef = 963832611B88E5BF0015F710 /* FlatButton.swift */; settings = {ATTRIBUTES = (Public, ); }; };
963832831B89070E0015F710 /* RaisedButton.swift in Headers */ = {isa = PBXBuildFile; fileRef = 963832651B88E5BF0015F710 /* RaisedButton.swift */; settings = {ATTRIBUTES = (Public, ); }; }; 963832831B89070E0015F710 /* RaisedButton.swift in Headers */ = {isa = PBXBuildFile; fileRef = 963832651B88E5BF0015F710 /* RaisedButton.swift */; settings = {ATTRIBUTES = (Public, ); }; };
963832841B89070E0015F710 /* Capture.swift in Headers */ = {isa = PBXBuildFile; fileRef = 9638325E1B88E5BF0015F710 /* Capture.swift */; settings = {ATTRIBUTES = (Public, ); }; };
963832851B89070E0015F710 /* CapturePreview.swift in Headers */ = {isa = PBXBuildFile; fileRef = 9638325F1B88E5BF0015F710 /* CapturePreview.swift */; settings = {ATTRIBUTES = (Public, ); }; };
963832881B8908180015F710 /* Layout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 963832871B8908180015F710 /* Layout.swift */; }; 963832881B8908180015F710 /* Layout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 963832871B8908180015F710 /* Layout.swift */; };
963832891B89097D0015F710 /* Layout.swift in Headers */ = {isa = PBXBuildFile; fileRef = 963832871B8908180015F710 /* Layout.swift */; settings = {ATTRIBUTES = (Public, ); }; }; 963832891B89097D0015F710 /* Layout.swift in Headers */ = {isa = PBXBuildFile; fileRef = 963832871B8908180015F710 /* Layout.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96B57D4E1B90AF7D00DE7BBB /* MaterialTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96B57D4D1B90AF7D00DE7BBB /* MaterialTheme.swift */; }; 96B57D4E1B90AF7D00DE7BBB /* MaterialTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96B57D4D1B90AF7D00DE7BBB /* MaterialTheme.swift */; };
...@@ -64,13 +62,12 @@ ...@@ -64,13 +62,12 @@
65B965861B8BEEC60055B139 /* SideNavigationViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SideNavigationViewController.swift; sourceTree = "<group>"; }; 65B965861B8BEEC60055B139 /* SideNavigationViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SideNavigationViewController.swift; sourceTree = "<group>"; };
65DBE41E1B9A9244000C804F /* Roboto-Bold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-Bold.ttf"; sourceTree = "<group>"; }; 65DBE41E1B9A9244000C804F /* Roboto-Bold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-Bold.ttf"; sourceTree = "<group>"; };
65DBE41F1B9A9244000C804F /* Roboto-Thin.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-Thin.ttf"; sourceTree = "<group>"; }; 65DBE41F1B9A9244000C804F /* Roboto-Thin.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-Thin.ttf"; sourceTree = "<group>"; };
962F3E521BACA68C0004B8AD /* NavigationBarView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationBarView.swift; sourceTree = "<group>"; };
963832361B88DFD80015F710 /* MaterialKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MaterialKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 963832361B88DFD80015F710 /* MaterialKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MaterialKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
963832411B88DFD80015F710 /* MaterialKitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MaterialKitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 963832411B88DFD80015F710 /* MaterialKitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MaterialKitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
963832541B88E30F0015F710 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; 963832541B88E30F0015F710 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
963832581B88E31A0015F710 /* MaterialKitTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MaterialKitTests.swift; sourceTree = "<group>"; }; 963832581B88E31A0015F710 /* MaterialKitTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MaterialKitTests.swift; sourceTree = "<group>"; };
963832591B88E31A0015F710 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; 963832591B88E31A0015F710 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
9638325E1B88E5BF0015F710 /* Capture.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Capture.swift; sourceTree = "<group>"; };
9638325F1B88E5BF0015F710 /* CapturePreview.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CapturePreview.swift; sourceTree = "<group>"; };
963832601B88E5BF0015F710 /* FabButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FabButton.swift; sourceTree = "<group>"; }; 963832601B88E5BF0015F710 /* FabButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FabButton.swift; sourceTree = "<group>"; };
963832611B88E5BF0015F710 /* FlatButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FlatButton.swift; sourceTree = "<group>"; }; 963832611B88E5BF0015F710 /* FlatButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FlatButton.swift; sourceTree = "<group>"; };
963832631B88E5BF0015F710 /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE; sourceTree = "<group>"; }; 963832631B88E5BF0015F710 /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE; sourceTree = "<group>"; };
...@@ -108,17 +105,11 @@ ...@@ -108,17 +105,11 @@
/* End PBXFrameworksBuildPhase section */ /* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */ /* Begin PBXGroup section */
65B965731B8A58E60055B139 /* View */ = {
isa = PBXGroup;
children = (
);
name = View;
sourceTree = "<group>";
};
65B965851B8BEEB00055B139 /* Navigation */ = { 65B965851B8BEEB00055B139 /* Navigation */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
65B965861B8BEEC60055B139 /* SideNavigationViewController.swift */, 65B965861B8BEEC60055B139 /* SideNavigationViewController.swift */,
962F3E521BACA68C0004B8AD /* NavigationBarView.swift */,
); );
name = Navigation; name = Navigation;
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -149,12 +140,10 @@ ...@@ -149,12 +140,10 @@
96B57D4C1B90AF6A00DE7BBB /* Theme */, 96B57D4C1B90AF6A00DE7BBB /* Theme */,
9AAC38521B89553800FE6B2D /* Font */, 9AAC38521B89553800FE6B2D /* Font */,
963832861B8907FE0015F710 /* Layout */, 963832861B8907FE0015F710 /* Layout */,
65B965731B8A58E60055B139 /* View */,
65B965851B8BEEB00055B139 /* Navigation */, 65B965851B8BEEB00055B139 /* Navigation */,
963832761B88E8990015F710 /* Text */, 963832761B88E8990015F710 /* Text */,
963832751B88E87B0015F710 /* Button */, 963832751B88E87B0015F710 /* Button */,
9AAC384B1B89524E00FE6B2D /* Card */, 9AAC384B1B89524E00FE6B2D /* Card */,
963832741B88E86B0015F710 /* Capture */,
); );
path = Source; path = Source;
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -184,15 +173,6 @@ ...@@ -184,15 +173,6 @@
name = "Supporting Files"; name = "Supporting Files";
sourceTree = "<group>"; sourceTree = "<group>";
}; };
963832741B88E86B0015F710 /* Capture */ = {
isa = PBXGroup;
children = (
9638325E1B88E5BF0015F710 /* Capture.swift */,
9638325F1B88E5BF0015F710 /* CapturePreview.swift */,
);
name = Capture;
sourceTree = "<group>";
};
963832751B88E87B0015F710 /* Button */ = { 963832751B88E87B0015F710 /* Button */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
...@@ -267,15 +247,14 @@ ...@@ -267,15 +247,14 @@
963832811B89070E0015F710 /* FabButton.swift in Headers */, 963832811B89070E0015F710 /* FabButton.swift in Headers */,
963832821B89070E0015F710 /* FlatButton.swift in Headers */, 963832821B89070E0015F710 /* FlatButton.swift in Headers */,
963832831B89070E0015F710 /* RaisedButton.swift in Headers */, 963832831B89070E0015F710 /* RaisedButton.swift in Headers */,
963832841B89070E0015F710 /* Capture.swift in Headers */,
9A94D0F91B895C8C00F586A5 /* Roboto.swift in Headers */, 9A94D0F91B895C8C00F586A5 /* Roboto.swift in Headers */,
963832851B89070E0015F710 /* CapturePreview.swift in Headers */,
963832891B89097D0015F710 /* Layout.swift in Headers */, 963832891B89097D0015F710 /* Layout.swift in Headers */,
657CD02A1B8EE0D3008C0029 /* MaterialCardView.swift in Headers */, 657CD02A1B8EE0D3008C0029 /* MaterialCardView.swift in Headers */,
657CD02C1B8EE0D3008C0029 /* SideNavigationViewController.swift in Headers */, 657CD02C1B8EE0D3008C0029 /* SideNavigationViewController.swift in Headers */,
657CD02D1B8EE0D3008C0029 /* MaterialButton.swift in Headers */, 657CD02D1B8EE0D3008C0029 /* MaterialButton.swift in Headers */,
96C910ED1B95804B00E7CE5C /* MaterialTheme.swift in Headers */, 96C910ED1B95804B00E7CE5C /* MaterialTheme.swift in Headers */,
657CD02F1B8EE0D3008C0029 /* ImageCardView.swift in Headers */, 657CD02F1B8EE0D3008C0029 /* ImageCardView.swift in Headers */,
962F3E541BACA7FB0004B8AD /* NavigationBarView.swift in Headers */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
...@@ -324,7 +303,9 @@ ...@@ -324,7 +303,9 @@
9638322D1B88DFD80015F710 /* Project object */ = { 9638322D1B88DFD80015F710 /* Project object */ = {
isa = PBXProject; isa = PBXProject;
attributes = { attributes = {
LastUpgradeCheck = 0640; LastSwiftMigration = 0710;
LastSwiftUpdateCheck = 0710;
LastUpgradeCheck = 0700;
ORGANIZATIONNAME = "GraphKit, Inc."; ORGANIZATIONNAME = "GraphKit, Inc.";
TargetAttributes = { TargetAttributes = {
963832351B88DFD80015F710 = { 963832351B88DFD80015F710 = {
...@@ -381,15 +362,14 @@ ...@@ -381,15 +362,14 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
963832691B88E5BF0015F710 /* Capture.swift in Sources */,
9A94D10B1B8A485C00F586A5 /* ImageCardView.swift in Sources */, 9A94D10B1B8A485C00F586A5 /* ImageCardView.swift in Sources */,
96B57D4E1B90AF7D00DE7BBB /* MaterialTheme.swift in Sources */, 96B57D4E1B90AF7D00DE7BBB /* MaterialTheme.swift in Sources */,
962F3E531BACA68C0004B8AD /* NavigationBarView.swift in Sources */,
9638326B1B88E5BF0015F710 /* FabButton.swift in Sources */, 9638326B1B88E5BF0015F710 /* FabButton.swift in Sources */,
9A94D1091B8A3F5100F586A5 /* MaterialCardView.swift in Sources */, 9A94D1091B8A3F5100F586A5 /* MaterialCardView.swift in Sources */,
65B965871B8BEEC60055B139 /* SideNavigationViewController.swift in Sources */, 65B965871B8BEEC60055B139 /* SideNavigationViewController.swift in Sources */,
9638326C1B88E5BF0015F710 /* FlatButton.swift in Sources */, 9638326C1B88E5BF0015F710 /* FlatButton.swift in Sources */,
963832711B88E5BF0015F710 /* MaterialText.swift in Sources */, 963832711B88E5BF0015F710 /* MaterialText.swift in Sources */,
9638326A1B88E5BF0015F710 /* CapturePreview.swift in Sources */,
9AAC38541B89559900FE6B2D /* Roboto.swift in Sources */, 9AAC38541B89559900FE6B2D /* Roboto.swift in Sources */,
65B9657E1B8A7C330055B139 /* MaterialButton.swift in Sources */, 65B9657E1B8A7C330055B139 /* MaterialButton.swift in Sources */,
963832881B8908180015F710 /* Layout.swift in Sources */, 963832881B8908180015F710 /* Layout.swift in Sources */,
...@@ -441,6 +421,7 @@ ...@@ -441,6 +421,7 @@
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99; GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO; GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES; GCC_NO_COMMON_BLOCKS = YES;
...@@ -456,7 +437,7 @@ ...@@ -456,7 +437,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0; IPHONEOS_DEPLOYMENT_TARGET = 8.3;
MTL_ENABLE_DEBUG_INFO = YES; MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos; SDKROOT = iphoneos;
...@@ -498,7 +479,7 @@ ...@@ -498,7 +479,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0; IPHONEOS_DEPLOYMENT_TARGET = 8.3;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos; SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
...@@ -519,6 +500,7 @@ ...@@ -519,6 +500,7 @@
INFOPLIST_FILE = Source/Info.plist; INFOPLIST_FILE = Source/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "io.graphkit.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = MaterialKit; PRODUCT_NAME = MaterialKit;
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_OPTIMIZATION_LEVEL = "-Onone";
...@@ -536,6 +518,7 @@ ...@@ -536,6 +518,7 @@
INFOPLIST_FILE = Source/Info.plist; INFOPLIST_FILE = Source/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "io.graphkit.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = MaterialKit; PRODUCT_NAME = MaterialKit;
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
}; };
...@@ -554,6 +537,7 @@ ...@@ -554,6 +537,7 @@
); );
INFOPLIST_FILE = Tests/Info.plist; INFOPLIST_FILE = Tests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "io.graphkit.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = MaterialKitTests; PRODUCT_NAME = MaterialKitTests;
}; };
name = Debug; name = Debug;
...@@ -567,6 +551,7 @@ ...@@ -567,6 +551,7 @@
); );
INFOPLIST_FILE = Tests/Info.plist; INFOPLIST_FILE = Tests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "io.graphkit.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = MaterialKitTests; PRODUCT_NAME = MaterialKitTests;
}; };
name = Release; name = Release;
......
...@@ -16,13 +16,14 @@ Make a call to action with a Floating Action Button. ...@@ -16,13 +16,14 @@ Make a call to action with a Floating Action Button.
```swift ```swift
var button: FabButton = FabButton() let button: FabButton = FabButton()
button.setTitle("+", forState: .Normal) button.setTitle("+", forState: .Normal)
button.titleLabel!.font = Roboto.lightWithSize(16) button.titleLabel!.font = Roboto.lightWithSize(16)
// layout // layout
view.addSubview(button) view.addSubview(button)
Layout.size(view, child: button, width: 60, height: 60) Layout.size(view, child: button, width: 60, height: 60)
Layout.alignFromBottomRight(view, child: button, bottom: 20, right: 20)
``` ```
### Raised Button ### Raised Button
...@@ -34,12 +35,13 @@ Use a Raised Button to capture attention. ...@@ -34,12 +35,13 @@ Use a Raised Button to capture attention.
```swift ```swift
var button: RaisedButton = RaisedButton() let button: RaisedButton = RaisedButton()
button.setTitle("Raised", forState: .Normal) button.setTitle("Raised", forState: .Normal)
// layout // layout
view.addSubview(button) view.addSubview(button)
Layout.size(view, child: button, width: 200, height: 60) Layout.size(view, child: button, width: 200, height: 60)
Layout.alignFromBottomRight(view, child: button, bottom: 20, right: 20)
``` ```
### Flat Button ### Flat Button
...@@ -51,12 +53,13 @@ Keep it simple and elegant with a Flat Button. ...@@ -51,12 +53,13 @@ Keep it simple and elegant with a Flat Button.
```swift ```swift
var button: RaisedButton = RaisedButton() let button: FlatButton = FlatButton()
button.setTitle("Flat", forState: .Normal) button.setTitle("Flat", forState: .Normal)
// layout // layout
view.addSubview(button) view.addSubview(button)
Layout.size(view, child: button, width: 200, height: 60) Layout.size(view, child: button, width: 200, height: 60)
Layout.alignFromBottomRight(view, child: button, bottom: 20, right: 20)
``` ```
### Basic Card ### Basic Card
...@@ -68,7 +71,7 @@ Easily make cards with fully customizable components. ...@@ -68,7 +71,7 @@ Easily make cards with fully customizable components.
```swift ```swift
var card: BasicCardView = BasicCardView() let card: BasicCardView = BasicCardView()
// title // title
card.titleLabel = UILabel() card.titleLabel = UILabel()
...@@ -82,12 +85,12 @@ card.detailLabel!.text = "I am a very simple card. I am good at containing small ...@@ -82,12 +85,12 @@ card.detailLabel!.text = "I am a very simple card. I am good at containing small
card.divider = UIView() card.divider = UIView()
// buttons // buttons
var cancelButton: FlatButton = FlatButton() let cancelButton: FlatButton = FlatButton()
cancelButton.pulseColor = MaterialTheme.blueGrey.darken3 cancelButton.pulseColor = MaterialTheme.blueGrey.darken3
cancelButton.setTitle("Cancel", forState: .Normal) cancelButton.setTitle("Cancel", forState: .Normal)
cancelButton.setTitleColor(MaterialTheme.yellow.darken3, forState: .Normal) cancelButton.setTitleColor(MaterialTheme.yellow.darken3, forState: .Normal)
var okButton: FlatButton = FlatButton() let okButton: FlatButton = FlatButton()
okButton.pulseColor = MaterialTheme.blueGrey.darken3 okButton.pulseColor = MaterialTheme.blueGrey.darken3
okButton.setTitle("Okay", forState: .Normal) okButton.setTitle("Okay", forState: .Normal)
okButton.setTitleColor(MaterialTheme.yellow.darken3, forState: .Normal) okButton.setTitleColor(MaterialTheme.yellow.darken3, forState: .Normal)
...@@ -96,8 +99,8 @@ card.leftButtons = [cancelButton, okButton] ...@@ -96,8 +99,8 @@ card.leftButtons = [cancelButton, okButton]
// layout // layout
view.addSubview(card) view.addSubview(card)
view.addConstraints(Layout.constraint("H:|-(pad)-[child]-(pad)-|", options: nil, metrics: ["pad": 20], views: ["child": card])) view.addConstraints(Layout.constraint("H:|-(pad)-[child]-(pad)-|", options: NSLayoutFormatOptions(rawValue: 0), metrics: ["pad": 20], views: ["child": card]))
view.addConstraints(Layout.constraint("V:|-(pad)-[child]", options: nil, metrics: ["pad": 100], views: ["child": card])) view.addConstraints(Layout.constraint("V:|-(pad)-[child]", options: NSLayoutFormatOptions(rawValue: 0), metrics: ["pad": 100], views: ["child": card]))
``` ```
### Image Card ### Image Card
...@@ -109,13 +112,13 @@ Add photos with an Image Card. ...@@ -109,13 +112,13 @@ Add photos with an Image Card.
```swift ```swift
var card: ImageCardView = ImageCardView() let card: ImageCardView = ImageCardView()
card.imageView = UIImageView(image: UIImage(named: "photo.jpg")) card.imageView = UIImageView(image: UIImage(named: "photo.jpg"))
// layout // layout
view.addSubview(card) view.addSubview(card)
view.addConstraints(Layout.constraint("H:|-(pad)-[child]-(pad)-|", options: nil, metrics: ["pad": 20], views: ["child": card])) view.addConstraints(Layout.constraint("H:|-(pad)-[child]-(pad)-|", options: NSLayoutFormatOptions(rawValue: 0), metrics: ["pad": 20], views: ["child": card]))
view.addConstraints(Layout.constraint("V:|-(pad)-[child]", options: nil, metrics: ["pad": 100], views: ["child": card])) view.addConstraints(Layout.constraint("V:|-(pad)-[child]", options: NSLayoutFormatOptions(rawValue: 0), metrics: ["pad": 100], views: ["child": card]))
``` ```
### Full Image Card ### Full Image Card
...@@ -127,7 +130,7 @@ Allow the Image Card to really shine by adding a title, some details, and button ...@@ -127,7 +130,7 @@ Allow the Image Card to really shine by adding a title, some details, and button
```swift ```swift
var card: ImageCardView = ImageCardView() let card: ImageCardView = ImageCardView()
card.imageView = UIImageView(image: UIImage(named: "photo.jpg")) card.imageView = UIImageView(image: UIImage(named: "photo.jpg"))
// title // title
...@@ -142,12 +145,12 @@ card.detailLabel!.text = "I am a very simple card. I am good at containing small ...@@ -142,12 +145,12 @@ card.detailLabel!.text = "I am a very simple card. I am good at containing small
card.divider = UIView() card.divider = UIView()
// buttons // buttons
var cancelButton: FlatButton = FlatButton() let cancelButton: FlatButton = FlatButton()
cancelButton.pulseColor = MaterialTheme.blueGrey.darken3 cancelButton.pulseColor = MaterialTheme.blueGrey.darken3
cancelButton.setTitle("Cancel", forState: .Normal) cancelButton.setTitle("Cancel", forState: .Normal)
cancelButton.setTitleColor(MaterialTheme.yellow.darken3, forState: .Normal) cancelButton.setTitleColor(MaterialTheme.yellow.darken3, forState: .Normal)
var okButton: FlatButton = FlatButton() let okButton: FlatButton = FlatButton()
okButton.pulseColor = MaterialTheme.blueGrey.darken3 okButton.pulseColor = MaterialTheme.blueGrey.darken3
okButton.setTitle("Okay", forState: .Normal) okButton.setTitle("Okay", forState: .Normal)
okButton.setTitleColor(MaterialTheme.yellow.darken3, forState: .Normal) okButton.setTitleColor(MaterialTheme.yellow.darken3, forState: .Normal)
...@@ -156,8 +159,8 @@ card.leftButtons = [cancelButton, okButton] ...@@ -156,8 +159,8 @@ card.leftButtons = [cancelButton, okButton]
// layout // layout
view.addSubview(card) view.addSubview(card)
view.addConstraints(Layout.constraint("H:|-(pad)-[child]-(pad)-|", options: nil, metrics: ["pad": 20], views: ["child": card])) view.addConstraints(Layout.constraint("H:|-(pad)-[child]-(pad)-|", options: NSLayoutFormatOptions(rawValue: 0), metrics: ["pad": 20], views: ["child": card]))
view.addConstraints(Layout.constraint("V:|-(pad)-[child]", options: nil, metrics: ["pad": 100], views: ["child": card])) view.addConstraints(Layout.constraint("V:|-(pad)-[child]", options: NSLayoutFormatOptions(rawValue: 0), metrics: ["pad": 100], views: ["child": card]))
``` ```
### Side Navigation ### Side Navigation
...@@ -170,11 +173,9 @@ Add a sleek Side Navigation to give your users a wonderful experience. ...@@ -170,11 +173,9 @@ Add a sleek Side Navigation to give your users a wonderful experience.
```swift ```swift
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
sideNav = SideNavigationViewController(mainViewController: MainViewController(), leftViewController: LeftViewController(), rightViewController: RightViewController()) let sideNavigationController = SideNavigationViewController(mainViewController: ViewController(), leftViewController: LeftViewController(), bottomViewController: BottomViewController(), rightViewController: RightViewController(), topViewController: TopViewController())
sideNav!.delegate = self self.window?.rootViewController = sideNavigationController
window = UIWindow(frame: UIScreen.mainScreen().bounds) self.window?.makeKeyAndVisible()
window!.rootViewController = sideNav
window!.makeKeyAndVisible()
return true return true
} }
``` ```
...@@ -186,11 +187,14 @@ Beautify your app with color. All Material Design color palettes are supported. ...@@ -186,11 +187,14 @@ Beautify your app with color. All Material Design color palettes are supported.
[Color Palettes](http://www.google.com/design/spec/style/color.html) [Color Palettes](http://www.google.com/design/spec/style/color.html)
```swift ```swift
var button: RaisedButton = RaisedButton() let button: RaisedButton = RaisedButton()
button.setTitle("Raised", forState: .Normal) button.setTitle("Raised", forState: .Normal)
button.setTitleColor(MaterialTheme.blue.darken3, forState: .Normal) button.setTitleColor(MaterialTheme.blue.darken3, forState: .Normal)
button.backgroundColor = MaterialTheme.yellow.darken3 button.backgroundColor = MaterialTheme.yellow.darken3
button.pulseColor = MaterialTheme.blueGrey.color button.pulseColor = MaterialTheme.blueGrey.color
view.addSubview(button)
Layout.size(view, child: button, width: 100, height: 60)
Layout.alignFromBottomRight(view, child: button, bottom: 20, right: 20)
``` ```
### License ### License
......
...@@ -18,21 +18,16 @@ ...@@ -18,21 +18,16 @@
import UIKit import UIKit
public class BasicCardView : MaterialCardView, Comparable, Equatable { public class BasicCardView : MaterialCardView, Comparable {
// //
// :name: layoutConstraints // :name: layoutConstraints
// //
internal lazy var layoutConstraints: Array<NSLayoutConstraint> = Array<NSLayoutConstraint>() internal lazy var layoutConstraints: Array<NSLayoutConstraint> = Array<NSLayoutConstraint>()
//
// :name: views
//
internal lazy var views: Dictionary<String, AnyObject> = Dictionary<String, AnyObject>()
/** /**
:name: titleLabelVerticalInset :name: titleLabelVerticalInset
*/ */
public var titleLabelVerticalInset: CGFloat = MaterialTheme.verticalInset { public var titleLabelVerticalInset: CGFloat = MaterialTheme.cardVerticalInset {
didSet { didSet {
titleLabelTopInset = titleLabelVerticalInset titleLabelTopInset = titleLabelVerticalInset
titleLabelBottomInset = titleLabelVerticalInset titleLabelBottomInset = titleLabelVerticalInset
...@@ -42,17 +37,25 @@ public class BasicCardView : MaterialCardView, Comparable, Equatable { ...@@ -42,17 +37,25 @@ public class BasicCardView : MaterialCardView, Comparable, Equatable {
/** /**
:name: titleLabelTopInset :name: titleLabelTopInset
*/ */
public var titleLabelTopInset: CGFloat = MaterialTheme.verticalInset public var titleLabelTopInset: CGFloat = MaterialTheme.cardVerticalInset {
didSet {
reloadView()
}
}
/** /**
:name: titleLabelBottomInset :name: titleLabelBottomInset
*/ */
public var titleLabelBottomInset: CGFloat = MaterialTheme.verticalInset public var titleLabelBottomInset: CGFloat = MaterialTheme.cardVerticalInset {
didSet {
reloadView()
}
}
/** /**
:name: titleLabelHorizontalInset :name: titleLabelHorizontalInset
*/ */
public var titleLabelHorizontalInset: CGFloat = MaterialTheme.horizontalInset { public var titleLabelHorizontalInset: CGFloat = MaterialTheme.cardHorizontalInset {
didSet { didSet {
titleLabelLeftInset = titleLabelHorizontalInset titleLabelLeftInset = titleLabelHorizontalInset
titleLabelRightInset = titleLabelHorizontalInset titleLabelRightInset = titleLabelHorizontalInset
...@@ -62,17 +65,25 @@ public class BasicCardView : MaterialCardView, Comparable, Equatable { ...@@ -62,17 +65,25 @@ public class BasicCardView : MaterialCardView, Comparable, Equatable {
/** /**
:name: titleLabelLeftInset :name: titleLabelLeftInset
*/ */
public var titleLabelLeftInset: CGFloat = MaterialTheme.horizontalInset public var titleLabelLeftInset: CGFloat = MaterialTheme.cardHorizontalInset {
didSet {
reloadView()
}
}
/** /**
:name: titleLabelRightInset :name: titleLabelRightInset
*/ */
public var titleLabelRightInset: CGFloat = MaterialTheme.horizontalInset public var titleLabelRightInset: CGFloat = MaterialTheme.cardHorizontalInset {
didSet {
reloadView()
}
}
/** /**
:name: detailLabelVerticalInset :name: detailLabelVerticalInset
*/ */
public var detailLabelVerticalInset: CGFloat = MaterialTheme.verticalInset { public var detailLabelVerticalInset: CGFloat = MaterialTheme.cardVerticalInset {
didSet { didSet {
detailLabelTopInset = detailLabelVerticalInset detailLabelTopInset = detailLabelVerticalInset
detailLabelBottomInset = detailLabelVerticalInset detailLabelBottomInset = detailLabelVerticalInset
...@@ -82,17 +93,25 @@ public class BasicCardView : MaterialCardView, Comparable, Equatable { ...@@ -82,17 +93,25 @@ public class BasicCardView : MaterialCardView, Comparable, Equatable {
/** /**
:name: detailLabelTopInset :name: detailLabelTopInset
*/ */
public var detailLabelTopInset: CGFloat = MaterialTheme.verticalInset public var detailLabelTopInset: CGFloat = MaterialTheme.cardVerticalInset {
didSet {
reloadView()
}
}
/** /**
:name: detailLabelBottomInset :name: detailLabelBottomInset
*/ */
public var detailLabelBottomInset: CGFloat = MaterialTheme.verticalInset public var detailLabelBottomInset: CGFloat = MaterialTheme.cardVerticalInset {
didSet {
reloadView()
}
}
/** /**
:name: detailLabelHorizontalInset :name: detailLabelHorizontalInset
*/ */
public var detailLabelHorizontalInset: CGFloat = MaterialTheme.horizontalInset { public var detailLabelHorizontalInset: CGFloat = MaterialTheme.cardHorizontalInset {
didSet { didSet {
detailLabelLeftInset = detailLabelHorizontalInset detailLabelLeftInset = detailLabelHorizontalInset
detailLabelRightInset = detailLabelHorizontalInset detailLabelRightInset = detailLabelHorizontalInset
...@@ -102,17 +121,25 @@ public class BasicCardView : MaterialCardView, Comparable, Equatable { ...@@ -102,17 +121,25 @@ public class BasicCardView : MaterialCardView, Comparable, Equatable {
/** /**
:name: detailLabelLeftInset :name: detailLabelLeftInset
*/ */
public var detailLabelLeftInset: CGFloat = MaterialTheme.horizontalInset public var detailLabelLeftInset: CGFloat = MaterialTheme.cardHorizontalInset {
didSet {
reloadView()
}
}
/** /**
:name: detailLabelRightInset :name: detailLabelRightInset
*/ */
public var detailLabelRightInset: CGFloat = MaterialTheme.horizontalInset public var detailLabelRightInset: CGFloat = MaterialTheme.cardHorizontalInset {
didSet {
reloadView()
}
}
/** /**
:name: buttonVerticalInset :name: buttonVerticalInset
*/ */
public var buttonVerticalInset: CGFloat = MaterialTheme.verticalInset { public var buttonVerticalInset: CGFloat = MaterialTheme.cardVerticalInset {
didSet { didSet {
buttonTopInset = buttonVerticalInset buttonTopInset = buttonVerticalInset
buttonBottomInset = buttonVerticalInset buttonBottomInset = buttonVerticalInset
...@@ -122,17 +149,25 @@ public class BasicCardView : MaterialCardView, Comparable, Equatable { ...@@ -122,17 +149,25 @@ public class BasicCardView : MaterialCardView, Comparable, Equatable {
/** /**
:name: buttonTopInset :name: buttonTopInset
*/ */
public var buttonTopInset: CGFloat = MaterialTheme.verticalInset public var buttonTopInset: CGFloat = MaterialTheme.cardVerticalInset / 2 {
didSet {
reloadView()
}
}
/** /**
:name: buttonBottomInset :name: buttonBottomInset
*/ */
public var buttonBottomInset: CGFloat = MaterialTheme.verticalInset public var buttonBottomInset: CGFloat = MaterialTheme.cardVerticalInset / 2 {
didSet {
reloadView()
}
}
/** /**
:name: buttonHorizontalInset :name: buttonHorizontalInset
*/ */
public var buttonHorizontalInset: CGFloat = MaterialTheme.horizontalInset { public var buttonHorizontalInset: CGFloat = MaterialTheme.cardHorizontalInset {
didSet { didSet {
buttonLeftInset = buttonHorizontalInset buttonLeftInset = buttonHorizontalInset
buttonRightInset = buttonHorizontalInset buttonRightInset = buttonHorizontalInset
...@@ -142,17 +177,20 @@ public class BasicCardView : MaterialCardView, Comparable, Equatable { ...@@ -142,17 +177,20 @@ public class BasicCardView : MaterialCardView, Comparable, Equatable {
/** /**
:name: buttonLeftInset :name: buttonLeftInset
*/ */
public var buttonLeftInset: CGFloat = MaterialTheme.horizontalInset public var buttonLeftInset: CGFloat = MaterialTheme.cardHorizontalInset / 2 {
didSet {
reloadView()
}
}
/** /**
:name: buttonRightInset :name: buttonRightInset
*/ */
public var buttonRightInset: CGFloat = MaterialTheme.horizontalInset public var buttonRightInset: CGFloat = MaterialTheme.cardHorizontalInset / 2 {
didSet {
/** reloadView()
:name: titleLabelContainer }
*/ }
public private(set) var titleLabelContainer: UIView?
/** /**
:name: shadow :name: shadow
...@@ -160,10 +198,25 @@ public class BasicCardView : MaterialCardView, Comparable, Equatable { ...@@ -160,10 +198,25 @@ public class BasicCardView : MaterialCardView, Comparable, Equatable {
public var shadow: Bool = true { public var shadow: Bool = true {
didSet { didSet {
false == shadow ? removeShadow() : prepareShadow() false == shadow ? removeShadow() : prepareShadow()
reloadView()
} }
} }
/** /**
:name: maximumTitleLabelHeight
*/
public var maximumTitleLabelHeight: CGFloat = 0 {
didSet {
reloadView()
}
}
/**
:name: titleLabelContainer
*/
public private(set) var titleLabelContainer: UIView?
/**
:name: titleLabel :name: titleLabel
*/ */
public var titleLabel: UILabel? { public var titleLabel: UILabel? {
...@@ -172,30 +225,35 @@ public class BasicCardView : MaterialCardView, Comparable, Equatable { ...@@ -172,30 +225,35 @@ public class BasicCardView : MaterialCardView, Comparable, Equatable {
// container // container
if nil == titleLabelContainer { if nil == titleLabelContainer {
titleLabelContainer = UIView() titleLabelContainer = UIView()
titleLabelContainer!.setTranslatesAutoresizingMaskIntoConstraints(false) titleLabelContainer!.translatesAutoresizingMaskIntoConstraints = false
titleLabelContainer!.backgroundColor = MaterialTheme.clear.color titleLabelContainer!.backgroundColor = MaterialTheme.clear.color
addSubview(titleLabelContainer!) addSubview(titleLabelContainer!)
} }
// text // text
titleLabelContainer!.addSubview(t) titleLabelContainer!.addSubview(t)
t.setTranslatesAutoresizingMaskIntoConstraints(false) t.translatesAutoresizingMaskIntoConstraints = false
t.textColor = MaterialTheme.white.color
t.backgroundColor = MaterialTheme.clear.color t.backgroundColor = MaterialTheme.clear.color
t.font = Roboto.medium t.font = Roboto.regular
t.numberOfLines = 1 t.numberOfLines = 0
t.lineBreakMode = .ByTruncatingTail t.lineBreakMode = .ByTruncatingTail
prepareCard() t.textColor = MaterialTheme.white.color
} else { } else {
titleLabelContainer?.removeFromSuperview() titleLabelContainer?.removeFromSuperview()
titleLabelContainer = nil
} }
reloadView()
} }
} }
/** /**
:name: maximumDetailLabelHeight :name: maximumDetailLabelHeight
*/ */
public var maximumDetailLabelHeight: CGFloat = 144 public var maximumDetailLabelHeight: CGFloat = 0 {
didSet {
reloadView()
}
}
/** /**
:name: detailLabelContainer :name: detailLabelContainer
...@@ -211,23 +269,24 @@ public class BasicCardView : MaterialCardView, Comparable, Equatable { ...@@ -211,23 +269,24 @@ public class BasicCardView : MaterialCardView, Comparable, Equatable {
// container // container
if nil == detailLabelContainer { if nil == detailLabelContainer {
detailLabelContainer = UIView() detailLabelContainer = UIView()
detailLabelContainer!.setTranslatesAutoresizingMaskIntoConstraints(false) detailLabelContainer!.translatesAutoresizingMaskIntoConstraints = false
detailLabelContainer!.backgroundColor = MaterialTheme.clear.color detailLabelContainer!.backgroundColor = MaterialTheme.clear.color
addSubview(detailLabelContainer!) addSubview(detailLabelContainer!)
} }
// text // text
detailLabelContainer!.addSubview(l) detailLabelContainer!.addSubview(l)
l.setTranslatesAutoresizingMaskIntoConstraints(false) l.translatesAutoresizingMaskIntoConstraints = false
l.textColor = MaterialTheme.white.color l.textColor = MaterialTheme.white.color
l.backgroundColor = MaterialTheme.clear.color l.backgroundColor = MaterialTheme.clear.color
l.font = Roboto.light l.font = Roboto.light
l.numberOfLines = 0 l.numberOfLines = 0
l.lineBreakMode = .ByTruncatingTail l.lineBreakMode = .ByTruncatingTail
prepareCard()
} else { } else {
detailLabelContainer?.removeFromSuperview() detailLabelContainer?.removeFromSuperview()
detailLabelContainer = nil
} }
reloadView()
} }
} }
...@@ -237,13 +296,13 @@ public class BasicCardView : MaterialCardView, Comparable, Equatable { ...@@ -237,13 +296,13 @@ public class BasicCardView : MaterialCardView, Comparable, Equatable {
public var divider: UIView? { public var divider: UIView? {
didSet { didSet {
if let d = divider { if let d = divider {
d.setTranslatesAutoresizingMaskIntoConstraints(false) d.translatesAutoresizingMaskIntoConstraints = false
d.backgroundColor = MaterialTheme.blueGrey.color d.backgroundColor = MaterialTheme.blueGrey.darken1
addSubview(d) addSubview(d)
prepareCard()
} else { } else {
divider?.removeFromSuperview() divider?.removeFromSuperview()
} }
reloadView()
} }
} }
...@@ -257,17 +316,20 @@ public class BasicCardView : MaterialCardView, Comparable, Equatable { ...@@ -257,17 +316,20 @@ public class BasicCardView : MaterialCardView, Comparable, Equatable {
*/ */
public var leftButtons: Array<MaterialButton>? { public var leftButtons: Array<MaterialButton>? {
didSet { didSet {
if let b = leftButtons { if nil == rightButtons && nil == leftButtons {
if nil == buttonsContainer {
buttonsContainer = UIView()
buttonsContainer!.setTranslatesAutoresizingMaskIntoConstraints(false)
buttonsContainer!.backgroundColor = MaterialTheme.clear.color
addSubview(buttonsContainer!)
}
prepareCard()
} else {
buttonsContainer?.removeFromSuperview() buttonsContainer?.removeFromSuperview()
buttonsContainer = nil
} else if nil == buttonsContainer {
buttonsContainer = UIView()
buttonsContainer!.translatesAutoresizingMaskIntoConstraints = false
buttonsContainer!.backgroundColor = MaterialTheme.clear.color
addSubview(buttonsContainer!)
} else {
for v in buttonsContainer!.subviews {
v.removeFromSuperview()
}
} }
reloadView()
} }
} }
...@@ -276,24 +338,23 @@ public class BasicCardView : MaterialCardView, Comparable, Equatable { ...@@ -276,24 +338,23 @@ public class BasicCardView : MaterialCardView, Comparable, Equatable {
*/ */
public var rightButtons: Array<MaterialButton>? { public var rightButtons: Array<MaterialButton>? {
didSet { didSet {
if let b = rightButtons { if nil == rightButtons && nil == leftButtons {
if nil == buttonsContainer {
buttonsContainer = UIView()
buttonsContainer!.setTranslatesAutoresizingMaskIntoConstraints(false)
buttonsContainer!.backgroundColor = MaterialTheme.clear.color
addSubview(buttonsContainer!)
}
prepareCard()
} else {
buttonsContainer?.removeFromSuperview() buttonsContainer?.removeFromSuperview()
buttonsContainer = nil
} else if nil == buttonsContainer {
buttonsContainer = UIView()
buttonsContainer!.translatesAutoresizingMaskIntoConstraints = false
buttonsContainer!.backgroundColor = MaterialTheme.clear.color
addSubview(buttonsContainer!)
} }
reloadView()
} }
} }
/** /**
:name: init :name: init
*/ */
public required init(coder aDecoder: NSCoder) { public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder) super.init(coder: aDecoder)
} }
...@@ -304,91 +365,87 @@ public class BasicCardView : MaterialCardView, Comparable, Equatable { ...@@ -304,91 +365,87 @@ public class BasicCardView : MaterialCardView, Comparable, Equatable {
self.init(frame: CGRectZero) self.init(frame: CGRectZero)
prepareProperties(titleLabel, detailLabel: detailLabel, divider: divider, leftButtons: leftButtons, rightButtons: rightButtons) prepareProperties(titleLabel, detailLabel: detailLabel, divider: divider, leftButtons: leftButtons, rightButtons: rightButtons)
} }
/** /**
:name: init :name: init
*/ */
public required init(frame: CGRect) { public required init(frame: CGRect) {
super.init(frame: CGRectZero) super.init(frame: frame)
} }
// /**
// :name: prepareProperties :name: isEqual
// */
internal func prepareProperties(titleLabel: UILabel?, detailLabel: UILabel?, divider: UIView?, leftButtons: Array<MaterialButton>?, rightButtons: Array<MaterialButton>?) { public override func isEqual(object: AnyObject?) -> Bool {
self.titleLabel = titleLabel if let rhs = object as? BasicCardView {
self.detailLabel = detailLabel return tag == rhs.tag
self.divider = divider }
self.leftButtons = leftButtons return false
self.rightButtons = rightButtons
}
//
// :name: prepareView
//
internal override func prepareView() {
super.prepareView()
prepareShadow()
backgroundColor = MaterialTheme.blueGrey.darken1
} }
// /**
// :name: prepareCard :name: reloadView
// */
internal override func prepareCard() { public func reloadView() {
super.prepareCard() // clear all constraints
// deactivate and clear all constraints
NSLayoutConstraint.deactivateConstraints(layoutConstraints) NSLayoutConstraint.deactivateConstraints(layoutConstraints)
layoutConstraints.removeAll(keepCapacity: false) layoutConstraints.removeAll(keepCapacity: false)
// detect all components and create constraints // detect all components and create constraints
var verticalFormat: String = "V:|" var verticalFormat: String = "V:|"
var views: Dictionary<String, AnyObject> = Dictionary<String, AnyObject>()
// title // title
if nil != titleLabelContainer && nil != titleLabel { if nil != titleLabel {
// clear for updated constraints // clear for updated constraints
titleLabelContainer!.removeConstraints(titleLabelContainer!.constraints()) titleLabelContainer!.removeConstraints(titleLabelContainer!.constraints)
// container // container
layoutConstraints += Layout.constraint("H:|[titleLabelContainer]|", options: nil, metrics: nil, views: ["titleLabelContainer": titleLabelContainer!]) layoutConstraints += Layout.constraint("H:|[titleLabelContainer]|", options: [], metrics: nil, views: ["titleLabelContainer": titleLabelContainer!])
verticalFormat += "[titleLabelContainer]" verticalFormat += "[titleLabelContainer]"
views["titleLabelContainer"] = titleLabelContainer! views["titleLabelContainer"] = titleLabelContainer!
// common text // common text
Layout.height(titleLabelContainer!, child: titleLabel!, height: 1.5 * titleLabel!.font.pointSize) if 0 == maximumTitleLabelHeight {
Layout.expandToParentWithPad(titleLabelContainer!, child: titleLabel!, top: titleLabelTopInset, left: titleLabelLeftInset, bottom: titleLabelBottomInset, right: titleLabelRightInset) Layout.expandToParentWithPad(titleLabelContainer!, child: titleLabel!, top: titleLabelTopInset, left: titleLabelLeftInset, bottom: titleLabelBottomInset, right: titleLabelRightInset)
} else {
Layout.expandToParentHorizontallyWithPad(titleLabelContainer!, child: titleLabel!, left: titleLabelLeftInset, right: titleLabelRightInset)
titleLabelContainer!.addConstraints(Layout.constraint("V:|-(titleLabelTopInset)-[titleLabel(<=maximumTitleLabelHeight)]-(titleLabelBottomInset)-|", options: [], metrics: ["titleLabelTopInset": titleLabelTopInset, "titleLabelBottomInset": titleLabelBottomInset, "maximumTitleLabelHeight": maximumTitleLabelHeight], views: ["titleLabel": titleLabel!]))
}
} }
// detail // detail
if nil != detailLabelContainer && nil != detailLabel { if nil != detailLabel {
// clear for updated constraints // clear for updated constraints
detailLabelContainer!.removeConstraints(detailLabelContainer!.constraints()) detailLabelContainer!.removeConstraints(detailLabelContainer!.constraints)
// container // container
layoutConstraints += Layout.constraint("H:|[detailLabelContainer]|", options: nil, metrics: nil, views: ["detailLabelContainer": detailLabelContainer!]) layoutConstraints += Layout.constraint("H:|[detailLabelContainer]|", options: [], metrics: nil, views: ["detailLabelContainer": detailLabelContainer!])
verticalFormat += "[detailLabelContainer]" verticalFormat += "[detailLabelContainer]"
views["detailLabelContainer"] = detailLabelContainer! views["detailLabelContainer"] = detailLabelContainer!
// text if 0 == maximumDetailLabelHeight {
Layout.expandToParentHorizontallyWithPad(detailLabelContainer!, child: detailLabel!, left: detailLabelLeftInset, right: detailLabelRightInset) Layout.expandToParentWithPad(detailLabelContainer!, child: detailLabel!, top: detailLabelTopInset, left: detailLabelLeftInset, bottom: detailLabelBottomInset, right: detailLabelRightInset)
detailLabelContainer!.addConstraints(Layout.constraint("V:|-(detailLabelTopInset)-[detailLabel(<=maximumDetailLabelHeight)]-(detailLabelBottomInset)-|", options: nil, metrics: ["detailLabelTopInset": detailLabelTopInset, "detailLabelBottomInset": detailLabelBottomInset, "maximumDetailLabelHeight": maximumDetailLabelHeight], views: ["detailLabel": detailLabel!])) } else {
Layout.expandToParentHorizontallyWithPad(detailLabelContainer!, child: detailLabel!, left: detailLabelLeftInset, right: detailLabelRightInset)
detailLabelContainer!.addConstraints(Layout.constraint("V:|-(detailLabelTopInset)-[detailLabel(<=maximumDetailLabelHeight)]-(detailLabelBottomInset)-|", options: [], metrics: ["detailLabelTopInset": detailLabelTopInset, "detailLabelBottomInset": detailLabelBottomInset, "maximumDetailLabelHeight": maximumDetailLabelHeight], views: ["detailLabel": detailLabel!]))
}
} }
// buttons // buttons
if nil != buttonsContainer && (nil != leftButtons || nil != rightButtons) { if nil != leftButtons || nil != rightButtons {
// clear for updated constraints // clear for updated constraints
buttonsContainer!.removeConstraints(buttonsContainer!.constraints()) buttonsContainer!.removeConstraints(buttonsContainer!.constraints)
// divider // divider
if nil != divider { if nil != divider {
layoutConstraints += Layout.constraint("H:|[divider]|", options: nil, metrics: nil, views: ["divider": divider!]) layoutConstraints += Layout.constraint("H:|[divider]|", options: [], metrics: nil, views: ["divider": divider!])
views["divider"] = divider! views["divider"] = divider!
verticalFormat += "[divider(1)]" verticalFormat += "[divider(1)]"
} }
//container //container
layoutConstraints += Layout.constraint("H:|[buttonsContainer]|", options: nil, metrics: nil, views: ["buttonsContainer": buttonsContainer!]) layoutConstraints += Layout.constraint("H:|[buttonsContainer]|", options: [], metrics: nil, views: ["buttonsContainer": buttonsContainer!])
verticalFormat += "[buttonsContainer]" verticalFormat += "[buttonsContainer]"
views["buttonsContainer"] = buttonsContainer! views["buttonsContainer"] = buttonsContainer!
...@@ -403,7 +460,7 @@ public class BasicCardView : MaterialCardView, Comparable, Equatable { ...@@ -403,7 +460,7 @@ public class BasicCardView : MaterialCardView, Comparable, Equatable {
horizontalFormat += "-(buttonLeftInset)-[button\(i)]" horizontalFormat += "-(buttonLeftInset)-[button\(i)]"
Layout.expandToParentVerticallyWithPad(buttonsContainer!, child: button, top: buttonTopInset, bottom: buttonBottomInset) Layout.expandToParentVerticallyWithPad(buttonsContainer!, child: button, top: buttonTopInset, bottom: buttonBottomInset)
} }
buttonsContainer!.addConstraints(Layout.constraint(horizontalFormat, options: nil, metrics: ["buttonLeftInset": buttonLeftInset], views: buttonViews)) buttonsContainer!.addConstraints(Layout.constraint(horizontalFormat, options: [], metrics: ["buttonLeftInset": buttonLeftInset], views: buttonViews))
} }
// rightButtons // rightButtons
...@@ -417,7 +474,7 @@ public class BasicCardView : MaterialCardView, Comparable, Equatable { ...@@ -417,7 +474,7 @@ public class BasicCardView : MaterialCardView, Comparable, Equatable {
horizontalFormat += "[button\(i)]-(buttonRightInset)-" horizontalFormat += "[button\(i)]-(buttonRightInset)-"
Layout.expandToParentVerticallyWithPad(buttonsContainer!, child: button, top: buttonTopInset, bottom: buttonBottomInset) Layout.expandToParentVerticallyWithPad(buttonsContainer!, child: button, top: buttonTopInset, bottom: buttonBottomInset)
} }
buttonsContainer!.addConstraints(Layout.constraint(horizontalFormat + "|", options: nil, metrics: ["buttonRightInset": buttonRightInset], views: buttonViews)) buttonsContainer!.addConstraints(Layout.constraint(horizontalFormat + "|", options: [], metrics: ["buttonRightInset": buttonRightInset], views: buttonViews))
} }
} }
...@@ -425,14 +482,30 @@ public class BasicCardView : MaterialCardView, Comparable, Equatable { ...@@ -425,14 +482,30 @@ public class BasicCardView : MaterialCardView, Comparable, Equatable {
// combine constraints // combine constraints
if 0 < layoutConstraints.count { if 0 < layoutConstraints.count {
layoutConstraints += Layout.constraint(verticalFormat, options: nil, metrics: nil, views: views) layoutConstraints += Layout.constraint(verticalFormat, options: [], metrics: nil, views: views)
NSLayoutConstraint.activateConstraints(layoutConstraints) NSLayoutConstraint.activateConstraints(layoutConstraints)
} }
} }
}
//
public func ==(lhs: BasicCardView, rhs: BasicCardView) -> Bool { // :name: prepareProperties
return lhs.tag == rhs.tag //
internal func prepareProperties(titleLabel: UILabel?, detailLabel: UILabel?, divider: UIView?, leftButtons: Array<MaterialButton>?, rightButtons: Array<MaterialButton>?) {
self.titleLabel = titleLabel
self.detailLabel = detailLabel
self.divider = divider
self.leftButtons = leftButtons
self.rightButtons = rightButtons
}
//
// :name: prepareView
//
internal override func prepareView() {
super.prepareView()
prepareShadow()
backgroundColor = MaterialTheme.blueGrey.color
}
} }
public func <=(lhs: BasicCardView, rhs: BasicCardView) -> Bool { public func <=(lhs: BasicCardView, rhs: BasicCardView) -> Bool {
......
//
// Copyright (C) 2015 GraphKit, Inc. <http://graphkit.io> and other GraphKit contributors.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program located at the root of the software package
// in a file called LICENSE. If not, see <http://www.gnu.org/licenses/>.
//
import UIKit
import AVFoundation
import AssetsLibrary
@objc(CaptureDelegate)
public protocol CaptureDelegate {
optional func captureDeviceConfigurationFailed(capture: Capture, error: NSError!)
optional func captureMediaCaptureFailed(capture: Capture, error: NSError!)
optional func captureAsetLibraryWriteFailed(capture: Capture, error: NSError!)
optional func capture(capture: Capture, assetLibraryDidWrite image: UIImage!)
}
public class Capture: NSObject, AVCaptureFileOutputRecordingDelegate {
//
// :name: activeVideoInput
// :description: The video input that is currently active.
//
private var activeVideoInput: AVCaptureDeviceInput?
//
// :name: imageOutput
// :description: When the session is taking a photo, this is the output manager.
//
private lazy var imageOutput: AVCaptureStillImageOutput = AVCaptureStillImageOutput()
//
// :name: movieOutput
// :description: When the session is shooting a video, this is the output manager.
//
private lazy var movieOutput: AVCaptureMovieFileOutput = AVCaptureMovieFileOutput()
//
// :name: movieOutputURL
// :description: The output URL of the movie file.
//
private var movieOutputURL: NSURL?
//
// :name: queue
// :description: Async job queue.
//
private lazy var queue: dispatch_queue_t = {
return dispatch_queue_create("io.graphkit.Capture", nil)
}()
//
// :name: CaptureAdjustingExposureContext
// :description: Used for KVO observation context.
//
public var CaptureAdjustingExposureContext: NSString?
/**
* cameraCount
* The number of available cameras on the device.
*/
public var cameraCount: Int {
return AVCaptureDevice.devicesWithMediaType(AVMediaTypeVideo).count
}
/**
* session
* An AVCaptureSession that manages all inputs and outputs in that session.
*/
public lazy var session: AVCaptureSession = AVCaptureSession()
/**
* delegate
* An optional instance of CaptureDelegate to handle events that are triggered during various
* stages in the session.
*/
public weak var delegate: CaptureDelegate?
/**
* prepareSession
* A helper method that prepares the session with the various available inputs and outputs.
* @param preset: String, default: AVCaptureSessionPresetHigh
* @return A boolean value, true if successful, false otherwise.
*/
public func prepareSession(preset: String = AVCaptureSessionPresetHigh) -> Bool {
session.sessionPreset = preset
var error: NSError?
// setup default camera device
let videoDevice: AVCaptureDevice = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)
var videoInput: AVCaptureDeviceInput? = AVCaptureDeviceInput.deviceInputWithDevice(videoDevice, error: &error) as? AVCaptureDeviceInput
if nil == videoInput {
return false
}
if session.canAddInput(videoInput) {
session.addInput(videoInput)
activeVideoInput = videoInput
}
let audioDevice: AVCaptureDevice = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeAudio)
var audioInput: AVCaptureDeviceInput? = AVCaptureDeviceInput.deviceInputWithDevice(audioDevice, error: &error) as? AVCaptureDeviceInput
if nil == audioInput {
return false
}
if session.canAddInput(audioInput) {
session.addInput(audioInput)
}
imageOutput.outputSettings = [AVVideoCodecKey: AVVideoCodecJPEG]
if session.canAddOutput(imageOutput) {
session.addOutput(imageOutput)
}
if session.canAddOutput(movieOutput) {
session.addOutput(movieOutput)
}
return true
}
/**
* startSession
* Starts the capture session if it is not already running.
*/
public func startSession() {
if !session.running {
dispatch_async(queue) {
self.session.startRunning()
}
}
}
/**
* stopSession
* Stops the capture session if it is already running.
*/
public func stopSession() {
if session.running {
dispatch_async(queue) {
self.session.stopRunning()
}
}
}
/**
* cameraWithPosition
* @param position: AVCaptureDevicePosition
* @return An AVCaptureDevice optional.
*/
public func cameraWithPosition(position: AVCaptureDevicePosition) -> AVCaptureDevice? {
for device in AVCaptureDevice.devicesWithMediaType(AVMediaTypeVideo) {
if position == device.position {
return device as? AVCaptureDevice
}
}
return nil
}
/**
* activeCamera
* @return The active cameras video input device.
*/
public var activeCamera: AVCaptureDevice {
get {
return activeVideoInput!.device
}
}
/**
* inactiveCamera
* @return The inactive cameras video input device.
*/
public var inactiveCamera: AVCaptureDevice? {
get {
var device: AVCaptureDevice?
if 1 < cameraCount {
device = activeCamera.position == .Back ? cameraWithPosition(.Front) : cameraWithPosition(.Back)
}
return device
}
}
/**
* canSwitchCameras
* Checks whether the camera can be switched. This would require at least two cameras.
* @return A boolean of the result, true if yes, false otherwise.
*/
public var canSwitchCameras: Bool {
return 1 < cameraCount
}
/**
* switchCamera
* If it is possible to switch cameras, then the camera will be switched from the opposite facing camera.
* @return A boolean of the result, true if switched, false otherwise.
* @delegate If the configuration fails, the capture(capture: Capture!, deviceConfigurationFailed error: NSError!) is called.
*/
public func switchCamera() -> Bool {
if !canSwitchCameras {
return false
}
var error: NSError?
var videoDevice: AVCaptureDevice? = inactiveCamera
var videoInput: AVCaptureDeviceInput? = AVCaptureDeviceInput.deviceInputWithDevice(videoDevice, error: &error) as? AVCaptureDeviceInput
if nil == videoInput {
session.beginConfiguration()
session.removeInput(activeVideoInput)
if session.canAddInput(videoInput) {
activeVideoInput = videoInput
} else {
session.addInput(activeVideoInput)
}
session.commitConfiguration()
} else {
delegate?.captureDeviceConfigurationFailed?(self, error: error)
return false
}
return true
}
/**
* cameraHasFlash
* Checks whether the camera supports flash.
* @return A boolean of the result, true if yes, false otherwise.
*/
public var cameraHasFlash: Bool {
return activeCamera.hasFlash
}
/**
* flashMode
* A mutator and accessor for the flashMode property.
* @delegate If the configuration fails, the capture(capture: Capture!, deviceConfigurationFailed error: NSError!) is called.
*/
public var flashMode: AVCaptureFlashMode {
get {
return activeCamera.flashMode
}
set(value) {
let device: AVCaptureDevice = activeCamera
if flashMode != device.flashMode && device.isFlashModeSupported(flashMode) {
var error: NSError?
if device.lockForConfiguration(&error) {
device.flashMode = flashMode
device.unlockForConfiguration()
} else {
delegate?.captureDeviceConfigurationFailed?(self, error: error)
}
}
}
}
/**
* cameraHasTorch
* Checks whether the device supports torch feature.
* @return A boolean of the result, true if yes, false otherwise.
*/
public var cameraHasTorch: Bool {
get {
return activeCamera.hasTorch
}
}
/**
* torchMode
* A mutator and accessor for the torchMode property.
* @delegate If the configuration fails, the capture(capture: Capture!, deviceConfigurationFailed error: NSError!) is called.
*/
public var torchMode: AVCaptureTorchMode {
get {
return activeCamera.torchMode
}
set(value) {
let device: AVCaptureDevice = activeCamera
if torchMode != device.torchMode && device.isTorchModeSupported(torchMode) {
var error: NSError?
if device.lockForConfiguration(&error) {
device.torchMode = torchMode
device.unlockForConfiguration()
} else {
delegate?.captureDeviceConfigurationFailed?(self, error: error)
}
}
}
}
/**
* cameraSupportsTapToFocus
* Checks whether the device supports tap to focus.
* @return A boolean of the result, true if yes, false otherwise.
*/
public var cameraSupportsTapToFocus: Bool {
get {
return activeCamera.focusPointOfInterestSupported
}
}
/**
* focusAtpoint
* Sets the point to focus at on the screen.
* @param point: CGPoint
* @delegate If the configuration fails, the capture(capture: Capture!, deviceConfigurationFailed error: NSError!) is called.
*/
public func focusAtPoint(point: CGPoint) {
let device: AVCaptureDevice = activeCamera
if device.focusPointOfInterestSupported && device.isFocusModeSupported(.AutoFocus) {
var error: NSError?
if device.lockForConfiguration(&error) {
device.focusPointOfInterest = point
device.focusMode = .AutoFocus
device.unlockForConfiguration()
} else {
delegate?.captureDeviceConfigurationFailed?(self, error: error)
}
}
}
/**
* cameraSupportsTapToExpose
* Checks whether the device supports tap to expose.
* @return A boolean of the result, true if yes, false otherwise.
*/
public var cameraSupportsTapToExpose: Bool {
get {
return activeCamera.exposurePointOfInterestSupported
}
}
/**
* exposeAtPoint
* Sets a point for exposure.
* @delegate If the configuration fails, the capture(capture: Capture!, deviceConfigurationFailed error: NSError!) is called.
*/
public func exposeAtPoint(point: CGPoint) {
let device: AVCaptureDevice = activeCamera
var exposureMode: AVCaptureExposureMode = .ContinuousAutoExposure
if device.exposurePointOfInterestSupported && device.isExposureModeSupported(exposureMode) {
var error: NSError?
if device.lockForConfiguration(&error) {
device.exposurePointOfInterest = point
device.exposureMode = exposureMode
if device.isExposureModeSupported(.Locked) {
device.addObserver(self, forKeyPath: "adjustingExposure", options: .New, context: &CaptureAdjustingExposureContext)
}
device.unlockForConfiguration()
} else {
delegate?.captureDeviceConfigurationFailed?(self, error: error)
}
}
}
/**
* override to set observeValueForKeyPath and handle exposure observance.
* @delegate If the configuration fails, the capture(capture: Capture!, deviceConfigurationFailed error: NSError!) is called.
*/
override public func observeValueForKeyPath(keyPath: String, ofObject object: AnyObject, change: [NSObject : AnyObject], context: UnsafeMutablePointer<Void>) {
if context == &CaptureAdjustingExposureContext {
let device: AVCaptureDevice = object as! AVCaptureDevice
if device.adjustingExposure && device.isExposureModeSupported(.Locked) {
object.removeObserver(self, forKeyPath: "adjustingExposure", context: &CaptureAdjustingExposureContext)
dispatch_async(queue) {
var error: NSError?
if device.lockForConfiguration(&error) {
device.unlockForConfiguration()
} else {
self.delegate?.captureDeviceConfigurationFailed?(self, error: error)
}
}
} else {
super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
}
}
}
/**
* resetFocusAndExposureModes
* Resets to default configuration for device focus and exposure mode.
* @delegate If the configuration fails, the capture(capture: Capture!, deviceConfigurationFailed error: NSError!) is called.
*/
public func resetFocusAndExposureModes() {
let device: AVCaptureDevice = activeCamera
var exposureMode: AVCaptureExposureMode = .ContinuousAutoExposure
let canResetExposure: Bool = device.focusPointOfInterestSupported && device.isExposureModeSupported(exposureMode)
var focusMode: AVCaptureFocusMode = .ContinuousAutoFocus
let canResetFocus: Bool = device.focusPointOfInterestSupported && device.isFocusModeSupported(focusMode)
let centerPoint: CGPoint = CGPointMake(0.5, 0.5)
var error: NSError?
if device.lockForConfiguration(&error) {
if canResetFocus {
device.focusMode = focusMode
device.focusPointOfInterest = centerPoint
}
if canResetExposure {
device.exposureMode = exposureMode
device.exposurePointOfInterest = centerPoint
}
device.unlockForConfiguration()
} else {
delegate?.captureDeviceConfigurationFailed?(self, error: error)
}
}
/**
* captureStillImage
* Captures the image and write the photo to the user's asset library.
* @delegate If the success, the capture(capture: Capture!, assetLibraryDidWrite image: UIImage!) is called.
* @delegate If failure, capture(capture: Capture!, assetLibraryWriteFailed error: NSError!) is called.
*/
public func captureStillImage() {
var connection: AVCaptureConnection = imageOutput.connectionWithMediaType(AVMediaTypeVideo)
if connection.supportsVideoOrientation {
connection.videoOrientation = currentVideoOrientation
}
imageOutput.captureStillImageAsynchronouslyFromConnection(connection) { (sampleBuffer: CMSampleBufferRef?, error: NSError?) in
if nil == sampleBuffer {
self.delegate?.captureAsetLibraryWriteFailed?(self, error: error)
} else {
let imageData: NSData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(sampleBuffer)
let image: UIImage = UIImage(data: imageData)!
self.writeImageToAssetsLibrary(image)
}
}
}
/**
* isRecording
* Checkts whether the device is currently recording.
* @return A boolean of the result, true if yes, false otherwise.
*/
public var isRecording: Bool {
get {
return movieOutput.recording
}
}
/**
* startRecording
* If the device is not currently recording, this starts the movie recording.
* @delegate If the configuration fails, the capture(capture: Capture!, deviceConfigurationFailed error: NSError!) is called.
*/
public func startRecording() {
if !isRecording {
let connection: AVCaptureConnection = movieOutput.connectionWithMediaType(AVMediaTypeVideo)
if connection.supportsVideoOrientation {
connection.videoOrientation = currentVideoOrientation
}
if connection.supportsVideoStabilization {
connection.preferredVideoStabilizationMode = .Auto
}
let device: AVCaptureDevice = activeCamera
if device.smoothAutoFocusSupported {
var error: NSError?
if device.lockForConfiguration(&error) {
device.smoothAutoFocusEnabled = false
device.unlockForConfiguration()
} else {
delegate?.captureDeviceConfigurationFailed?(self, error: error)
}
}
movieOutputURL = uniqueURL
movieOutput.startRecordingToOutputFileURL(movieOutputURL, recordingDelegate: self)
}
}
/**
* stopRecording
* If the device is currently recoring, this stops the movie recording.
*/
public func stopRecording() {
if isRecording {
movieOutput.stopRecording()
}
}
/**
* recordedDuration
* Retrieves the movie recorded duration.
* @return A CMTime value.
*/
public var recordedDuration: CMTime {
get {
return movieOutput.recordedDuration
}
}
/**
* currentVideoOrientation
* Retrieves the current orientation of the device.
* @return A AVCaptureVideoOrientation value, [Portrait, LandscapeLeft, PortraitUpsideDown, LandscapeRight].
*/
public var currentVideoOrientation: AVCaptureVideoOrientation {
var orientation: AVCaptureVideoOrientation?
switch UIDevice.currentDevice().orientation {
case .Portrait:
orientation = .Portrait
break
case .LandscapeRight:
orientation = .LandscapeLeft
break
case .PortraitUpsideDown:
orientation = .PortraitUpsideDown
break
default:
orientation = .LandscapeRight
}
return orientation!
}
/**
* uniqueURL
* A unique URL generated for the movie video.
* @return An optional NSURL value.
*/
private var uniqueURL: NSURL? {
var error: NSError?
let fileManager: NSFileManager = NSFileManager.defaultManager()
let tempDirectoryTemplate: String = NSTemporaryDirectory().stringByAppendingPathComponent("FocusLibrary")
if fileManager.createDirectoryAtPath(tempDirectoryTemplate, withIntermediateDirectories: true, attributes: nil, error: &error) {
return NSURL.fileURLWithPath(tempDirectoryTemplate + "/test.mov")
}
return nil
}
/**
* postAssetLibraryNotification
* Fires an asynchronous call to the capture(capture: Capture!, assetLibraryDidWrite image: UIImage!) delegate.
* @param image: UIImage!
* @delegate An asynchronous call to capture(capture: Capture!, assetLibraryDidWrite image: UIImage!) delegate.
*/
private func postAssetLibraryNotification(image: UIImage!) {
dispatch_async(queue) {
self.delegate?.capture?(self, assetLibraryDidWrite: image)
}
}
/**
* writeImageToAssetsLibrary
* Writes the image file to the user's asset library.
* @param image: UIImage!
* @delegate If successful, an asynchronous call to capture(capture: Capture!, assetLibraryDidWrite image: UIImage!) delegate.
* @delegate If failure, capture(capture: Capture!, assetLibraryWriteFailed error: NSError!) is called.
*/
private func writeImageToAssetsLibrary(image: UIImage) {
let library: ALAssetsLibrary = ALAssetsLibrary()
library.writeImageToSavedPhotosAlbum(image.CGImage, orientation: ALAssetOrientation(rawValue: image.imageOrientation.rawValue)!) { (path: NSURL!, error: NSError?) -> Void in
if nil == error {
self.postAssetLibraryNotification(image)
} else {
self.delegate?.captureAsetLibraryWriteFailed?(self, error: error)
}
}
}
/**
* writeVideoToAssetsLibrary
* Writes the video file to the user's asset library.
* @param videoURL: NSURL!
* @delegate If successful, an asynchronous call to capture(capture: Capture!, assetLibraryDidWrite image: UIImage!) delegate.
* @delegate If failure, capture(capture: Capture!, assetLibraryWriteFailed error: NSError!) is called.
*/
private func writeVideoToAssetsLibrary(videoURL: NSURL!) {
let library: ALAssetsLibrary = ALAssetsLibrary()
if library.videoAtPathIsCompatibleWithSavedPhotosAlbum(videoURL) {
library.writeVideoAtPathToSavedPhotosAlbum(videoURL) { (path: NSURL!, error: NSError?) in
if nil == error {
self.generateThumbnailForVideoAtURL(videoURL)
} else {
self.delegate?.captureAsetLibraryWriteFailed?(self, error: error)
}
}
}
}
/**
* generateThumbnailForVideoAtURL
* Generates a thumbnail for the video URL specified.
* @param videoURL: NSURL!
* @delegate An asynchronous call to capture(capture: Capture!, assetLibraryDidWrite image: UIImage!) delegate.
*/
private func generateThumbnailForVideoAtURL(videoURL: NSURL!) {
dispatch_async(queue) {
let asset: AVAsset = AVAsset.assetWithURL(videoURL) as! AVAsset
let imageGenerator: AVAssetImageGenerator = AVAssetImageGenerator(asset: asset)
imageGenerator.maximumSize = CGSizeMake(100, 0)
imageGenerator.appliesPreferredTrackTransform = true
let imageRef: CGImageRef = imageGenerator.copyCGImageAtTime(kCMTimeZero, actualTime: nil, error: nil)
let image: UIImage = UIImage(CGImage: imageRef)!
dispatch_async(dispatch_get_main_queue()) {
self.postAssetLibraryNotification(image)
}
}
}
/**
* delegate method for capturing video file.
* @delegate If successful, an asynchronous call to capture(capture: Capture!, assetLibraryDidWrite image: UIImage!) delegate.
* @delegate If failure, capture(capture: Capture!, mediaCaptureFailed error: NSError!) is called.
*/
public func captureOutput(captureOutput: AVCaptureFileOutput!, didFinishRecordingToOutputFileAtURL outputFileURL: NSURL!, fromConnections connections: [AnyObject]!, error: NSError!) {
if nil == error {
writeVideoToAssetsLibrary(movieOutputURL!.copy() as! NSURL)
} else {
delegate?.captureMediaCaptureFailed?(self, error: error)
}
movieOutputURL = nil
}
}
\ No newline at end of file
//
// Copyright (C) 2015 GraphKit, Inc. <http://graphkit.io> and other GraphKit contributors.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program located at the root of the software package
// in a file called LICENSE. If not, see <http://www.gnu.org/licenses/>.
//
import UIKit
import AVFoundation
@objc(PreviewDelegate)
public protocol PreviewDelegate {
optional func previewTappedToFocusAt(preview: Preview, point: CGPoint)
optional func previewTappedToExposeAt(preview: Preview, point: CGPoint)
optional func previewTappedToReset(preview: Preview, focus: UIView, exposure: UIView)
}
public class Preview: UIView {
/**
:name: boxBounds
:description: A static property that sets the initial size of the focusBox and exposureBox properties.
*/
static public var boxBounds: CGRect = CGRectMake(0, 0, 150, 150)
/**
:name: delegate
:description: An optional instance of PreviewDelegate to handle events that are triggered during various
stages of engagement.
*/
public weak var delegate: PreviewDelegate?
/**
:name: tapToFocusEnabled
:description: A mutator and accessor that enables and disables tap to focus gesture.
*/
public var tapToFocusEnabled: Bool {
get {
return singleTapRecognizer!.enabled
}
set(value) {
singleTapRecognizer!.enabled = value
}
}
/**
:name: tapToExposeEnabled
:description: A mutator and accessor that enables and disables tap to expose gesture.
*/
public var tapToExposeEnabled: Bool {
get {
return doubleTapRecognizer!.enabled
}
set(value) {
doubleTapRecognizer!.enabled = value
}
}
//
// override for layerClass
//
override public class func layerClass() -> AnyClass {
return AVCaptureVideoPreviewLayer.self
}
/**
:name: session
:description: A mutator and accessor for the preview AVCaptureSession value.
*/
public var session: AVCaptureSession {
get {
return (layer as! AVCaptureVideoPreviewLayer).session
}
set(value) {
(layer as! AVCaptureVideoPreviewLayer).session = value
}
}
/**
:name: focusBox
:description: An optional UIView for the focusBox animation. This is used when the
tapToFocusEnabled property is set to true.
*/
public var focusBox: UIView?
/**
:name: exposureBox
:description: An optional UIView for the exposureBox animation. This is used when the
tapToExposeEnabled property is set to true.
*/
public var exposureBox: UIView?
//
// :name: singleTapRecognizer
// :description: Gesture recognizer for single tap.
//
private var singleTapRecognizer: UITapGestureRecognizer?
//
// :name: doubleTapRecognizer
// :description: Gesture recognizer for double tap.
//
private var doubleTapRecognizer: UITapGestureRecognizer?
//
// :name: doubleDoubleTapRecognizer
// :description: Gesture recognizer for double/double tap.
//
private var doubleDoubleTapRecognizer: UITapGestureRecognizer?
required public init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
prepareView()
}
public override init(frame: CGRect) {
super.init(frame: frame)
prepareView()
}
public init() {
super.init(frame: CGRectZero)
setTranslatesAutoresizingMaskIntoConstraints(false)
prepareView()
}
//
// :name: handleSingleTap
//
internal func handleSingleTap(recognizer: UIGestureRecognizer) {
let point: CGPoint = recognizer.locationInView(self)
runBoxAnimationOnView(focusBox, point: point)
delegate?.previewTappedToFocusAt?(self, point: captureDevicePointForPoint(point))
}
//
// :name: handleDoubleTap
//
internal func handleDoubleTap(recognizer: UIGestureRecognizer) {
let point: CGPoint = recognizer.locationInView(self)
runBoxAnimationOnView(exposureBox, point: point)
delegate?.previewTappedToExposeAt?(self, point: captureDevicePointForPoint(point))
}
//
// :name: handleDoubleDoubleTap
//
internal func handleDoubleDoubleTap(recognizer: UIGestureRecognizer) {
runResetAnimation()
}
//
// :name: prepareView
// :description: Common setup for view.
//
private func prepareView() {
let captureLayer: AVCaptureVideoPreviewLayer = layer as! AVCaptureVideoPreviewLayer
captureLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
singleTapRecognizer = UITapGestureRecognizer(target: self, action: "handleSingleTap:")
singleTapRecognizer!.numberOfTapsRequired = 1
doubleTapRecognizer = UITapGestureRecognizer(target: self, action: "handleDoubleTap:")
doubleTapRecognizer!.numberOfTapsRequired = 2
doubleDoubleTapRecognizer = UITapGestureRecognizer(target: self, action: "handleDoubleDoubleTap:")
doubleDoubleTapRecognizer!.numberOfTapsRequired = 2
doubleDoubleTapRecognizer!.numberOfTouchesRequired = 2
addGestureRecognizer(singleTapRecognizer!)
addGestureRecognizer(doubleTapRecognizer!)
addGestureRecognizer(doubleDoubleTapRecognizer!)
singleTapRecognizer!.requireGestureRecognizerToFail(doubleTapRecognizer!)
focusBox = viewWithColor(.redColor())
exposureBox = viewWithColor(.blueColor())
addSubview(focusBox!)
addSubview(exposureBox!)
}
//
// :name: viewWithColor
// :description: Initializes a UIView with a set UIColor.
//
private func viewWithColor(color: UIColor) -> UIView {
let view: UIView = UIView(frame: Preview.boxBounds)
view.backgroundColor = MaterialTheme.clear.color
view.layer.borderColor = color.CGColor
view.layer.borderWidth = 5
view.hidden = true
return view
}
//
// :name: runBoxAnimationOnView
// :description: Runs the animation used for focusBox and exposureBox on single and double
// taps respectively at a given point.
//
private func runBoxAnimationOnView(view: UIView!, point: CGPoint) {
view.center = point
view.hidden = false
UIView.animateWithDuration(0.15, delay: 0, options: .CurveEaseInOut, animations: { _ in
view.layer.transform = CATransform3DMakeScale(0.5, 0.5, 1)
}) { _ in
let delayInSeconds: Double = 0.5
let popTime: dispatch_time_t = dispatch_time(DISPATCH_TIME_NOW, Int64(delayInSeconds * Double(NSEC_PER_SEC)))
dispatch_after(popTime, dispatch_get_main_queue()) {
view.hidden = true
view.transform = CGAffineTransformIdentity
}
}
}
//
// :name: captureDevicePointForPoint
// :description: Interprets the correct point from touch to preview layer.
//
private func captureDevicePointForPoint(point: CGPoint) -> CGPoint {
let previewLayer: AVCaptureVideoPreviewLayer = layer as! AVCaptureVideoPreviewLayer
return previewLayer.captureDevicePointOfInterestForPoint(point)
}
//
// :name: runResetAnimation
// :description: Executes the reset animation for focus and exposure.
//
private func runResetAnimation() {
if !tapToFocusEnabled && !tapToExposeEnabled {
return
}
let previewLayer: AVCaptureVideoPreviewLayer = layer as! AVCaptureVideoPreviewLayer
let centerPoint: CGPoint = previewLayer.pointForCaptureDevicePointOfInterest(CGPointMake(0.5, 0.5))
focusBox!.center = centerPoint
exposureBox!.center = centerPoint
exposureBox!.transform = CGAffineTransformMakeScale(1.2, 1.2)
focusBox!.hidden = false
exposureBox!.hidden = false
UIView.animateWithDuration(0.15, delay: 0, options: .CurveEaseInOut, animations: { _ in
self.focusBox!.layer.transform = CATransform3DMakeScale(0.5, 0.5, 1)
self.exposureBox!.layer.transform = CATransform3DMakeScale(0.7, 0.7, 1)
}) { _ in
let delayInSeconds: Double = 0.5
let popTime: dispatch_time_t = dispatch_time(DISPATCH_TIME_NOW, Int64(delayInSeconds * Double(NSEC_PER_SEC)))
dispatch_after(popTime, dispatch_get_main_queue()) {
self.focusBox!.hidden = true
self.exposureBox!.hidden = true
self.focusBox!.transform = CGAffineTransformIdentity
self.exposureBox!.transform = CGAffineTransformIdentity
self.delegate?.previewTappedToReset?(self, focus: self.focusBox!, exposure: self.exposureBox!)
}
}
}
}
...@@ -41,7 +41,7 @@ public class FabButton : MaterialButton { ...@@ -41,7 +41,7 @@ public class FabButton : MaterialButton {
// //
// :name: pulseBegan // :name: pulseBegan
// //
internal override func pulseBegan(touches: Set<NSObject>, withEvent event: UIEvent) { internal override func pulseBegan(touches: Set<NSObject>, withEvent event: UIEvent?) {
super.pulseBegan(touches, withEvent: event) super.pulseBegan(touches, withEvent: event)
UIView.animateWithDuration(0.3, animations: { UIView.animateWithDuration(0.3, animations: {
self.pulseView!.transform = CGAffineTransformMakeScale(3, 3) self.pulseView!.transform = CGAffineTransformMakeScale(3, 3)
......
...@@ -24,11 +24,11 @@ public class FlatButton : MaterialButton { ...@@ -24,11 +24,11 @@ public class FlatButton : MaterialButton {
// //
internal override func prepareView() { internal override func prepareView() {
super.prepareView() super.prepareView()
titleLabel!.font = Roboto.medium titleLabel!.font = Roboto.regular
setTitleColor(MaterialTheme.blueGrey.darken4, forState: .Normal) setTitleColor(MaterialTheme.blueGrey.darken4, forState: .Normal)
pulseColor = MaterialTheme.blueGrey.lighten3 pulseColor = MaterialTheme.blueGrey.lighten3
backgroundColor = MaterialTheme.clear.color backgroundColor = MaterialTheme.clear.color
contentEdgeInsets = UIEdgeInsetsMake(6, 16, 6, 16) contentEdgeInsets = UIEdgeInsetsMake(MaterialTheme.buttonVerticalInset, MaterialTheme.buttonHorizontalInset, MaterialTheme.buttonVerticalInset, MaterialTheme.buttonHorizontalInset)
} }
// //
...@@ -42,7 +42,7 @@ public class FlatButton : MaterialButton { ...@@ -42,7 +42,7 @@ public class FlatButton : MaterialButton {
// //
// :name: pulseBegan // :name: pulseBegan
// //
internal override func pulseBegan(touches: Set<NSObject>, withEvent event: UIEvent) { internal override func pulseBegan(touches: Set<NSObject>, withEvent event: UIEvent?) {
super.pulseBegan(touches, withEvent: event) super.pulseBegan(touches, withEvent: event)
UIView.animateWithDuration(0.3, animations: { UIView.animateWithDuration(0.3, animations: {
self.pulseView!.transform = CGAffineTransformMakeScale(4, 4) self.pulseView!.transform = CGAffineTransformMakeScale(4, 4)
......
...@@ -18,17 +18,12 @@ ...@@ -18,17 +18,12 @@
import UIKit import UIKit
public class ImageCardView : MaterialCardView, Comparable, Equatable { public class ImageCardView : MaterialCardView, Comparable {
// //
// :name: layoutConstraints // :name: layoutConstraints
// //
internal lazy var layoutConstraints: Array<NSLayoutConstraint> = Array<NSLayoutConstraint>() internal lazy var layoutConstraints: Array<NSLayoutConstraint> = Array<NSLayoutConstraint>()
//
// :name: views
//
internal lazy var views: Dictionary<String, AnyObject> = Dictionary<String, AnyObject>()
/** /**
:name: imageViewVerticalInset :name: imageViewVerticalInset
*/ */
...@@ -42,12 +37,20 @@ public class ImageCardView : MaterialCardView, Comparable, Equatable { ...@@ -42,12 +37,20 @@ public class ImageCardView : MaterialCardView, Comparable, Equatable {
/** /**
:name: imageViewTopInset :name: imageViewTopInset
*/ */
public var imageViewTopInset: CGFloat = 0 public var imageViewTopInset: CGFloat = 0 {
didSet {
reloadView()
}
}
/** /**
:name: imageViewBottomInset :name: imageViewBottomInset
*/ */
public var imageViewBottomInset: CGFloat = 0 public var imageViewBottomInset: CGFloat = 0 {
didSet {
reloadView()
}
}
/** /**
:name: imageViewHorizontalInset :name: imageViewHorizontalInset
...@@ -62,31 +65,53 @@ public class ImageCardView : MaterialCardView, Comparable, Equatable { ...@@ -62,31 +65,53 @@ public class ImageCardView : MaterialCardView, Comparable, Equatable {
/** /**
:name: imageViewLeftInset :name: imageViewLeftInset
*/ */
public var imageViewLeftInset: CGFloat = 0 public var imageViewLeftInset: CGFloat = 0 {
didSet {
reloadView()
}
}
/** /**
:name: imageViewRightInset :name: imageViewRightInset
*/ */
public var imageViewRightInset: CGFloat = 0 public var imageViewRightInset: CGFloat = 0 {
didSet {
reloadView()
}
}
/** /**
:name: titleLabelVerticalInset :name: titleLabelVerticalInset
*/ */
public var titleLabelVerticalInset: CGFloat = MaterialTheme.verticalInset { public var titleLabelVerticalInset: CGFloat = MaterialTheme.cardVerticalInset {
didSet { didSet {
titleLabelTopInset = titleLabelVerticalInset
titleLabelBottomInset = titleLabelVerticalInset titleLabelBottomInset = titleLabelVerticalInset
} }
} }
/** /**
:name: titleLabelTopInset
*/
public var titleLabelTopInset: CGFloat = MaterialTheme.cardVerticalInset {
didSet {
reloadView()
}
}
/**
:name: titleLabelBottomInset :name: titleLabelBottomInset
*/ */
public var titleLabelBottomInset: CGFloat = MaterialTheme.verticalInset public var titleLabelBottomInset: CGFloat = MaterialTheme.cardVerticalInset {
didSet {
reloadView()
}
}
/** /**
:name: titleLabelHorizontalInset :name: titleLabelHorizontalInset
*/ */
public var titleLabelHorizontalInset: CGFloat = MaterialTheme.horizontalInset { public var titleLabelHorizontalInset: CGFloat = MaterialTheme.cardHorizontalInset {
didSet { didSet {
titleLabelLeftInset = titleLabelHorizontalInset titleLabelLeftInset = titleLabelHorizontalInset
titleLabelRightInset = titleLabelHorizontalInset titleLabelRightInset = titleLabelHorizontalInset
...@@ -96,17 +121,25 @@ public class ImageCardView : MaterialCardView, Comparable, Equatable { ...@@ -96,17 +121,25 @@ public class ImageCardView : MaterialCardView, Comparable, Equatable {
/** /**
:name: titleLabelLeftInset :name: titleLabelLeftInset
*/ */
public var titleLabelLeftInset: CGFloat = MaterialTheme.horizontalInset public var titleLabelLeftInset: CGFloat = MaterialTheme.cardHorizontalInset {
didSet {
reloadView()
}
}
/** /**
:name: titleLabelRightInset :name: titleLabelRightInset
*/ */
public var titleLabelRightInset: CGFloat = MaterialTheme.horizontalInset public var titleLabelRightInset: CGFloat = MaterialTheme.cardHorizontalInset {
didSet {
reloadView()
}
}
/** /**
:name: detailLabelVerticalInset :name: detailLabelVerticalInset
*/ */
public var detailLabelVerticalInset: CGFloat = MaterialTheme.verticalInset { public var detailLabelVerticalInset: CGFloat = MaterialTheme.cardVerticalInset {
didSet { didSet {
detailLabelTopInset = detailLabelVerticalInset detailLabelTopInset = detailLabelVerticalInset
detailLabelBottomInset = detailLabelVerticalInset detailLabelBottomInset = detailLabelVerticalInset
...@@ -116,17 +149,25 @@ public class ImageCardView : MaterialCardView, Comparable, Equatable { ...@@ -116,17 +149,25 @@ public class ImageCardView : MaterialCardView, Comparable, Equatable {
/** /**
:name: detailLabelTopInset :name: detailLabelTopInset
*/ */
public var detailLabelTopInset: CGFloat = MaterialTheme.verticalInset public var detailLabelTopInset: CGFloat = MaterialTheme.cardVerticalInset {
didSet {
reloadView()
}
}
/** /**
:name: detailLabelBottomInset :name: detailLabelBottomInset
*/ */
public var detailLabelBottomInset: CGFloat = MaterialTheme.verticalInset public var detailLabelBottomInset: CGFloat = MaterialTheme.cardVerticalInset {
didSet {
reloadView()
}
}
/** /**
:name: detailLabelHorizontalInset :name: detailLabelHorizontalInset
*/ */
public var detailLabelHorizontalInset: CGFloat = MaterialTheme.horizontalInset { public var detailLabelHorizontalInset: CGFloat = MaterialTheme.cardHorizontalInset {
didSet { didSet {
detailLabelLeftInset = detailLabelHorizontalInset detailLabelLeftInset = detailLabelHorizontalInset
detailLabelRightInset = detailLabelHorizontalInset detailLabelRightInset = detailLabelHorizontalInset
...@@ -136,17 +177,25 @@ public class ImageCardView : MaterialCardView, Comparable, Equatable { ...@@ -136,17 +177,25 @@ public class ImageCardView : MaterialCardView, Comparable, Equatable {
/** /**
:name: detailLabelLeftInset :name: detailLabelLeftInset
*/ */
public var detailLabelLeftInset: CGFloat = MaterialTheme.horizontalInset public var detailLabelLeftInset: CGFloat = MaterialTheme.cardHorizontalInset {
didSet {
reloadView()
}
}
/** /**
:name: detailLabelRightInset :name: detailLabelRightInset
*/ */
public var detailLabelRightInset: CGFloat = MaterialTheme.horizontalInset public var detailLabelRightInset: CGFloat = MaterialTheme.cardHorizontalInset {
didSet {
reloadView()
}
}
/** /**
:name: buttonVerticalInset :name: buttonVerticalInset
*/ */
public var buttonVerticalInset: CGFloat = MaterialTheme.verticalInset { public var buttonVerticalInset: CGFloat = MaterialTheme.cardVerticalInset {
didSet { didSet {
buttonTopInset = buttonVerticalInset buttonTopInset = buttonVerticalInset
buttonBottomInset = buttonVerticalInset buttonBottomInset = buttonVerticalInset
...@@ -156,17 +205,25 @@ public class ImageCardView : MaterialCardView, Comparable, Equatable { ...@@ -156,17 +205,25 @@ public class ImageCardView : MaterialCardView, Comparable, Equatable {
/** /**
:name: buttonTopInset :name: buttonTopInset
*/ */
public var buttonTopInset: CGFloat = MaterialTheme.verticalInset public var buttonTopInset: CGFloat = MaterialTheme.cardVerticalInset / 2 {
didSet {
reloadView()
}
}
/** /**
:name: buttonBottomInset :name: buttonBottomInset
*/ */
public var buttonBottomInset: CGFloat = MaterialTheme.verticalInset public var buttonBottomInset: CGFloat = MaterialTheme.cardVerticalInset / 2 {
didSet {
reloadView()
}
}
/** /**
:name: buttonHorizontalInset :name: buttonHorizontalInset
*/ */
public var buttonHorizontalInset: CGFloat = MaterialTheme.horizontalInset { public var buttonHorizontalInset: CGFloat = MaterialTheme.cardHorizontalInset {
didSet { didSet {
buttonLeftInset = buttonHorizontalInset buttonLeftInset = buttonHorizontalInset
buttonRightInset = buttonHorizontalInset buttonRightInset = buttonHorizontalInset
...@@ -176,12 +233,20 @@ public class ImageCardView : MaterialCardView, Comparable, Equatable { ...@@ -176,12 +233,20 @@ public class ImageCardView : MaterialCardView, Comparable, Equatable {
/** /**
:name: buttonLeftInset :name: buttonLeftInset
*/ */
public var buttonLeftInset: CGFloat = MaterialTheme.horizontalInset public var buttonLeftInset: CGFloat = MaterialTheme.cardHorizontalInset / 2 {
didSet {
reloadView()
}
}
/** /**
:name: buttonRightInset :name: buttonRightInset
*/ */
public var buttonRightInset: CGFloat = MaterialTheme.horizontalInset public var buttonRightInset: CGFloat = MaterialTheme.cardHorizontalInset / 2 {
didSet {
reloadView()
}
}
/** /**
:name: shadow :name: shadow
...@@ -189,13 +254,18 @@ public class ImageCardView : MaterialCardView, Comparable, Equatable { ...@@ -189,13 +254,18 @@ public class ImageCardView : MaterialCardView, Comparable, Equatable {
public var shadow: Bool = true { public var shadow: Bool = true {
didSet { didSet {
false == shadow ? removeShadow() : prepareShadow() false == shadow ? removeShadow() : prepareShadow()
reloadView()
} }
} }
/** /**
:name: maximumImageViewHeight :name: maximumImageViewHeight
*/ */
public var maximumImageViewHeight: CGFloat = 200 public var maximumImageViewHeight: CGFloat = 200 {
didSet {
reloadView()
}
}
/** /**
:name: imageViewContainer :name: imageViewContainer
...@@ -211,14 +281,14 @@ public class ImageCardView : MaterialCardView, Comparable, Equatable { ...@@ -211,14 +281,14 @@ public class ImageCardView : MaterialCardView, Comparable, Equatable {
// container // container
if nil == imageViewContainer { if nil == imageViewContainer {
imageViewContainer = UIView() imageViewContainer = UIView()
imageViewContainer!.setTranslatesAutoresizingMaskIntoConstraints(false) imageViewContainer!.translatesAutoresizingMaskIntoConstraints = false
imageViewContainer!.backgroundColor = MaterialTheme.clear.color imageViewContainer!.backgroundColor = MaterialTheme.clear.color
addSubview(imageViewContainer!) addSubview(imageViewContainer!)
} }
// text // text
imageViewContainer!.addSubview(t) imageViewContainer!.addSubview(t)
t.setTranslatesAutoresizingMaskIntoConstraints(false) t.translatesAutoresizingMaskIntoConstraints = false
t.contentMode = .ScaleAspectFill t.contentMode = .ScaleAspectFill
t.userInteractionEnabled = false t.userInteractionEnabled = false
t.clipsToBounds = true t.clipsToBounds = true
...@@ -228,10 +298,20 @@ public class ImageCardView : MaterialCardView, Comparable, Equatable { ...@@ -228,10 +298,20 @@ public class ImageCardView : MaterialCardView, Comparable, Equatable {
titleLabelContainer!.removeFromSuperview() titleLabelContainer!.removeFromSuperview()
imageViewContainer!.addSubview(titleLabelContainer!) imageViewContainer!.addSubview(titleLabelContainer!)
} }
prepareCard()
} else { } else {
imageViewContainer?.removeFromSuperview() imageViewContainer?.removeFromSuperview()
imageViewContainer = nil
} }
reloadView()
}
}
/**
:name: maximumTitleLabelHeight
*/
public var maximumTitleLabelHeight: CGFloat = 0 {
didSet {
reloadView()
} }
} }
...@@ -249,37 +329,42 @@ public class ImageCardView : MaterialCardView, Comparable, Equatable { ...@@ -249,37 +329,42 @@ public class ImageCardView : MaterialCardView, Comparable, Equatable {
// container // container
if nil == titleLabelContainer { if nil == titleLabelContainer {
titleLabelContainer = UIView() titleLabelContainer = UIView()
titleLabelContainer!.setTranslatesAutoresizingMaskIntoConstraints(false) titleLabelContainer!.translatesAutoresizingMaskIntoConstraints = false
addSubview(titleLabelContainer!) addSubview(titleLabelContainer!)
} }
// text // text
titleLabelContainer!.addSubview(t) titleLabelContainer!.addSubview(t)
t.setTranslatesAutoresizingMaskIntoConstraints(false) t.translatesAutoresizingMaskIntoConstraints = false
t.backgroundColor = MaterialTheme.clear.color t.backgroundColor = MaterialTheme.clear.color
t.font = Roboto.medium t.font = Roboto.regular
t.numberOfLines = 1 t.numberOfLines = 0
t.lineBreakMode = .ByTruncatingTail t.lineBreakMode = .ByTruncatingTail
if nil == imageView { if nil == imageView {
titleLabelContainer!.backgroundColor = MaterialTheme.white.color titleLabelContainer!.backgroundColor = MaterialTheme.clear.color
t.textColor = MaterialTheme.black.color t.textColor = MaterialTheme.black.color
} else { } else {
titleLabelContainer!.backgroundColor = MaterialTheme.clear.color titleLabelContainer!.backgroundColor = MaterialTheme.clear.color
t.textColor = MaterialTheme.white.color t.textColor = MaterialTheme.clear.color
titleLabelContainer!.removeFromSuperview() titleLabelContainer!.removeFromSuperview()
imageViewContainer!.addSubview(titleLabelContainer!) imageViewContainer!.addSubview(titleLabelContainer!)
} }
prepareCard()
} else { } else {
titleLabelContainer?.removeFromSuperview() titleLabelContainer?.removeFromSuperview()
titleLabelContainer = nil
} }
reloadView()
} }
} }
/** /**
:name: maximumDetailLabelHeight :name: maximumDetailLabelHeight
*/ */
public var maximumDetailLabelHeight: CGFloat = 144 public var maximumDetailLabelHeight: CGFloat = 0 {
didSet {
reloadView()
}
}
/** /**
:name: detailLabelContainer :name: detailLabelContainer
...@@ -295,23 +380,24 @@ public class ImageCardView : MaterialCardView, Comparable, Equatable { ...@@ -295,23 +380,24 @@ public class ImageCardView : MaterialCardView, Comparable, Equatable {
// container // container
if nil == detailLabelContainer { if nil == detailLabelContainer {
detailLabelContainer = UIView() detailLabelContainer = UIView()
detailLabelContainer!.setTranslatesAutoresizingMaskIntoConstraints(false) detailLabelContainer!.translatesAutoresizingMaskIntoConstraints = false
detailLabelContainer!.backgroundColor = MaterialTheme.white.color detailLabelContainer!.backgroundColor = MaterialTheme.clear.color
addSubview(detailLabelContainer!) addSubview(detailLabelContainer!)
} }
// text // text
detailLabelContainer!.addSubview(l) detailLabelContainer!.addSubview(l)
l.setTranslatesAutoresizingMaskIntoConstraints(false) l.translatesAutoresizingMaskIntoConstraints = false
l.textColor = MaterialTheme.black.color l.textColor = MaterialTheme.black.color
l.backgroundColor = MaterialTheme.clear.color l.backgroundColor = MaterialTheme.clear.color
l.font = Roboto.light l.font = Roboto.light
l.numberOfLines = 0 l.numberOfLines = 0
l.lineBreakMode = .ByTruncatingTail l.lineBreakMode = .ByTruncatingTail
prepareCard()
} else { } else {
detailLabelContainer?.removeFromSuperview() detailLabelContainer?.removeFromSuperview()
detailLabelContainer = nil
} }
reloadView()
} }
} }
...@@ -321,13 +407,13 @@ public class ImageCardView : MaterialCardView, Comparable, Equatable { ...@@ -321,13 +407,13 @@ public class ImageCardView : MaterialCardView, Comparable, Equatable {
public var divider: UIView? { public var divider: UIView? {
didSet { didSet {
if let d = divider { if let d = divider {
d.setTranslatesAutoresizingMaskIntoConstraints(false) d.translatesAutoresizingMaskIntoConstraints = false
d.backgroundColor = MaterialTheme.blueGrey.lighten5 d.backgroundColor = MaterialTheme.blueGrey.lighten5
addSubview(d) addSubview(d)
prepareCard()
} else { } else {
divider?.removeFromSuperview() divider?.removeFromSuperview()
} }
reloadView()
} }
} }
...@@ -341,17 +427,16 @@ public class ImageCardView : MaterialCardView, Comparable, Equatable { ...@@ -341,17 +427,16 @@ public class ImageCardView : MaterialCardView, Comparable, Equatable {
*/ */
public var leftButtons: Array<MaterialButton>? { public var leftButtons: Array<MaterialButton>? {
didSet { didSet {
if let b = leftButtons { if nil == rightButtons && nil == leftButtons {
if nil == buttonsContainer {
buttonsContainer = UIView()
buttonsContainer!.setTranslatesAutoresizingMaskIntoConstraints(false)
buttonsContainer!.backgroundColor = MaterialTheme.white.color
addSubview(buttonsContainer!)
}
prepareCard()
} else {
buttonsContainer?.removeFromSuperview() buttonsContainer?.removeFromSuperview()
buttonsContainer = nil
} else if nil == buttonsContainer {
buttonsContainer = UIView()
buttonsContainer!.translatesAutoresizingMaskIntoConstraints = false
buttonsContainer!.backgroundColor = MaterialTheme.clear.color
addSubview(buttonsContainer!)
} }
reloadView()
} }
} }
...@@ -360,24 +445,27 @@ public class ImageCardView : MaterialCardView, Comparable, Equatable { ...@@ -360,24 +445,27 @@ public class ImageCardView : MaterialCardView, Comparable, Equatable {
*/ */
public var rightButtons: Array<MaterialButton>? { public var rightButtons: Array<MaterialButton>? {
didSet { didSet {
if let b = rightButtons { if nil == rightButtons && nil == leftButtons {
if nil == buttonsContainer {
buttonsContainer = UIView()
buttonsContainer!.setTranslatesAutoresizingMaskIntoConstraints(false)
buttonsContainer!.backgroundColor = MaterialTheme.white.color
addSubview(buttonsContainer!)
}
prepareCard()
} else {
buttonsContainer?.removeFromSuperview() buttonsContainer?.removeFromSuperview()
buttonsContainer = nil
} else if nil == buttonsContainer {
buttonsContainer = UIView()
buttonsContainer!.translatesAutoresizingMaskIntoConstraints = false
buttonsContainer!.backgroundColor = MaterialTheme.clear.color
addSubview(buttonsContainer!)
} else {
for v in buttonsContainer!.subviews {
v.removeFromSuperview()
}
} }
reloadView()
} }
} }
/** /**
:name: init :name: init
*/ */
public required init(coder aDecoder: NSCoder) { public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder) super.init(coder: aDecoder)
} }
...@@ -393,66 +481,54 @@ public class ImageCardView : MaterialCardView, Comparable, Equatable { ...@@ -393,66 +481,54 @@ public class ImageCardView : MaterialCardView, Comparable, Equatable {
:name: init :name: init
*/ */
public required init(frame: CGRect) { public required init(frame: CGRect) {
super.init(frame: CGRectZero) super.init(frame: frame)
} }
// /**
// :name: prepareProperties :name: isEqual
// */
internal func prepareProperties(imageView: UIImageView?, titleLabel: UILabel?, detailLabel: UILabel?, divider: UIView?, leftButtons: Array<MaterialButton>?, rightButtons: Array<MaterialButton>?) { public override func isEqual(object: AnyObject?) -> Bool {
self.imageView = imageView if let rhs = object as? ImageCardView {
self.titleLabel = titleLabel return tag == rhs.tag
self.detailLabel = detailLabel }
self.divider = divider return false
self.leftButtons = leftButtons
self.rightButtons = rightButtons
}
//
// :name: prepareView
//
internal override func prepareView() {
super.prepareView()
prepareShadow()
backgroundColor = MaterialTheme.clear.color
} }
// /**
// :name: prepareCard :name: reloadView
// */
internal override func prepareCard() { public func reloadView() {
super.prepareCard() // clear all constraints
// deactivate and clear all constraints
NSLayoutConstraint.deactivateConstraints(layoutConstraints) NSLayoutConstraint.deactivateConstraints(layoutConstraints)
layoutConstraints.removeAll(keepCapacity: false) layoutConstraints.removeAll(keepCapacity: false)
// detect all components and create constraints // detect all components and create constraints
var verticalFormat: String = "V:|" var verticalFormat: String = "V:|"
var views: Dictionary<String, AnyObject> = Dictionary<String, AnyObject>()
// image // image
if nil != imageViewContainer && nil != imageView { if nil != imageView {
// clear for updated constraints // clear for updated constraints
imageViewContainer!.removeConstraints(imageViewContainer!.constraints()) imageViewContainer!.removeConstraints(imageViewContainer!.constraints)
// container // container
layoutConstraints += Layout.constraint("H:|[imageViewContainer]|", options: nil, metrics: nil, views: ["imageViewContainer": imageViewContainer!]) layoutConstraints += Layout.constraint("H:|[imageViewContainer]|", options: [], metrics: nil, views: ["imageViewContainer": imageViewContainer!])
verticalFormat += "[imageViewContainer]" verticalFormat += "[imageViewContainer]"
views["imageViewContainer"] = imageViewContainer! views["imageViewContainer"] = imageViewContainer!
// text // text
Layout.expandToParentHorizontallyWithPad(imageViewContainer!, child: imageView!, left: imageViewLeftInset, right: imageViewRightInset) Layout.expandToParentHorizontallyWithPad(imageViewContainer!, child: imageView!, left: imageViewLeftInset, right: imageViewRightInset)
imageViewContainer!.addConstraints(Layout.constraint("V:|-(imageViewTopInset)-[imageView(maximumImageViewHeight)]-(imageViewBottomInset)-|", options: nil, metrics: ["imageViewTopInset": imageViewTopInset, "imageViewBottomInset": imageViewBottomInset, "maximumImageViewHeight": maximumImageViewHeight], views: ["imageView": imageView!])) imageViewContainer!.addConstraints(Layout.constraint("V:|-(imageViewTopInset)-[imageView(maximumImageViewHeight)]-(imageViewBottomInset)-|", options: [], metrics: ["imageViewTopInset": imageViewTopInset, "imageViewBottomInset": imageViewBottomInset, "maximumImageViewHeight": maximumImageViewHeight], views: ["imageView": imageView!]))
} }
// title // title
if nil != titleLabelContainer && nil != titleLabel { if nil != titleLabel {
// clear for updated constraints // clear for updated constraints
titleLabelContainer!.removeConstraints(titleLabelContainer!.constraints()) titleLabelContainer!.removeConstraints(titleLabelContainer!.constraints)
if nil == imageView { if nil == imageView {
// container // container
layoutConstraints += Layout.constraint("H:|[titleLabelContainer]|", options: nil, metrics: nil, views: ["titleLabelContainer": titleLabelContainer!]) layoutConstraints += Layout.constraint("H:|[titleLabelContainer]|", options: [], metrics: nil, views: ["titleLabelContainer": titleLabelContainer!])
verticalFormat += "[titleLabelContainer]" verticalFormat += "[titleLabelContainer]"
views["titleLabelContainer"] = titleLabelContainer! views["titleLabelContainer"] = titleLabelContainer!
} else { } else {
...@@ -462,39 +538,47 @@ public class ImageCardView : MaterialCardView, Comparable, Equatable { ...@@ -462,39 +538,47 @@ public class ImageCardView : MaterialCardView, Comparable, Equatable {
} }
// common text // common text
Layout.height(titleLabelContainer!, child: titleLabel!, height: 1.5 * titleLabel!.font.pointSize) if 0 == maximumTitleLabelHeight {
Layout.expandToParentWithPad(titleLabelContainer!, child: titleLabel!, left: titleLabelLeftInset, bottom: titleLabelBottomInset, right: titleLabelRightInset) Layout.expandToParentWithPad(titleLabelContainer!, child: titleLabel!, top: titleLabelTopInset, left: titleLabelLeftInset, bottom: titleLabelBottomInset, right: titleLabelRightInset)
} else {
Layout.expandToParentHorizontallyWithPad(titleLabelContainer!, child: titleLabel!, left: titleLabelLeftInset, right: titleLabelRightInset)
titleLabelContainer!.addConstraints(Layout.constraint("V:|-(titleLabelTopInset)-[titleLabel(<=maximumTitleLabelHeight)]-(titleLabelBottomInset)-|", options: [], metrics: ["titleLabelTopInset": titleLabelTopInset, "titleLabelBottomInset": titleLabelBottomInset, "maximumTitleLabelHeight": maximumTitleLabelHeight], views: ["titleLabel": titleLabel!]))
}
} }
// detail // detail
if nil != detailLabelContainer && nil != detailLabel { if nil != detailLabel {
// clear for updated constraints // clear for updated constraints
detailLabelContainer!.removeConstraints(detailLabelContainer!.constraints()) detailLabelContainer!.removeConstraints(detailLabelContainer!.constraints)
// container // container
layoutConstraints += Layout.constraint("H:|[detailLabelContainer]|", options: nil, metrics: nil, views: ["detailLabelContainer": detailLabelContainer!]) layoutConstraints += Layout.constraint("H:|[detailLabelContainer]|", options: [], metrics: nil, views: ["detailLabelContainer": detailLabelContainer!])
verticalFormat += "[detailLabelContainer]" verticalFormat += "[detailLabelContainer]"
views["detailLabelContainer"] = detailLabelContainer! views["detailLabelContainer"] = detailLabelContainer!
// text // text
Layout.expandToParentHorizontallyWithPad(detailLabelContainer!, child: detailLabel!, left: detailLabelLeftInset, right: detailLabelRightInset) if 0 == maximumDetailLabelHeight {
detailLabelContainer!.addConstraints(Layout.constraint("V:|-(detailLabelTopInset)-[detailLabel(<=maximumDetailLabelHeight)]-(detailLabelBottomInset)-|", options: nil, metrics: ["detailLabelTopInset": detailLabelTopInset, "detailLabelBottomInset": detailLabelBottomInset, "maximumDetailLabelHeight": maximumDetailLabelHeight], views: ["detailLabel": detailLabel!])) Layout.expandToParentWithPad(detailLabelContainer!, child: detailLabel!, top: detailLabelTopInset, left: detailLabelLeftInset, bottom: detailLabelBottomInset, right: detailLabelRightInset)
} else {
Layout.expandToParentHorizontallyWithPad(detailLabelContainer!, child: detailLabel!, left: detailLabelLeftInset, right: detailLabelRightInset)
detailLabelContainer!.addConstraints(Layout.constraint("V:|-(detailLabelTopInset)-[detailLabel(<=maximumDetailLabelHeight)]-(detailLabelBottomInset)-|", options: [], metrics: ["detailLabelTopInset": detailLabelTopInset, "detailLabelBottomInset": detailLabelBottomInset, "maximumDetailLabelHeight": maximumDetailLabelHeight], views: ["detailLabel": detailLabel!]))
}
} }
// buttons // buttons
if nil != buttonsContainer && (nil != leftButtons || nil != rightButtons) { if nil != leftButtons || nil != rightButtons {
// clear for updated constraints // clear for updated constraints
buttonsContainer!.removeConstraints(buttonsContainer!.constraints()) buttonsContainer!.removeConstraints(buttonsContainer!.constraints)
// divider // divider
if nil != divider { if nil != divider {
layoutConstraints += Layout.constraint("H:|[divider]|", options: nil, metrics: nil, views: ["divider": divider!]) layoutConstraints += Layout.constraint("H:|[divider]|", options: [], metrics: nil, views: ["divider": divider!])
views["divider"] = divider! views["divider"] = divider!
verticalFormat += "[divider(1)]" verticalFormat += "[divider(1)]"
} }
//container //container
layoutConstraints += Layout.constraint("H:|[buttonsContainer]|", options: nil, metrics: nil, views: ["buttonsContainer": buttonsContainer!]) layoutConstraints += Layout.constraint("H:|[buttonsContainer]|", options: [], metrics: nil, views: ["buttonsContainer": buttonsContainer!])
verticalFormat += "[buttonsContainer]" verticalFormat += "[buttonsContainer]"
views["buttonsContainer"] = buttonsContainer! views["buttonsContainer"] = buttonsContainer!
...@@ -509,7 +593,7 @@ public class ImageCardView : MaterialCardView, Comparable, Equatable { ...@@ -509,7 +593,7 @@ public class ImageCardView : MaterialCardView, Comparable, Equatable {
horizontalFormat += "-(buttonLeftInset)-[button\(i)]" horizontalFormat += "-(buttonLeftInset)-[button\(i)]"
Layout.expandToParentVerticallyWithPad(buttonsContainer!, child: button, top: buttonTopInset, bottom: buttonBottomInset) Layout.expandToParentVerticallyWithPad(buttonsContainer!, child: button, top: buttonTopInset, bottom: buttonBottomInset)
} }
buttonsContainer!.addConstraints(Layout.constraint(horizontalFormat, options: nil, metrics: ["buttonLeftInset": buttonLeftInset], views: buttonViews)) buttonsContainer!.addConstraints(Layout.constraint(horizontalFormat, options: [], metrics: ["buttonLeftInset": buttonLeftInset], views: buttonViews))
} }
// rightButtons // rightButtons
...@@ -523,7 +607,7 @@ public class ImageCardView : MaterialCardView, Comparable, Equatable { ...@@ -523,7 +607,7 @@ public class ImageCardView : MaterialCardView, Comparable, Equatable {
horizontalFormat += "[button\(i)]-(buttonRightInset)-" horizontalFormat += "[button\(i)]-(buttonRightInset)-"
Layout.expandToParentVerticallyWithPad(buttonsContainer!, child: button, top: buttonTopInset, bottom: buttonBottomInset) Layout.expandToParentVerticallyWithPad(buttonsContainer!, child: button, top: buttonTopInset, bottom: buttonBottomInset)
} }
buttonsContainer!.addConstraints(Layout.constraint(horizontalFormat + "|", options: nil, metrics: ["buttonRightInset": buttonRightInset], views: buttonViews)) buttonsContainer!.addConstraints(Layout.constraint(horizontalFormat + "|", options: [], metrics: ["buttonRightInset": buttonRightInset], views: buttonViews))
} }
} }
...@@ -531,14 +615,31 @@ public class ImageCardView : MaterialCardView, Comparable, Equatable { ...@@ -531,14 +615,31 @@ public class ImageCardView : MaterialCardView, Comparable, Equatable {
// combine constraints // combine constraints
if 0 < layoutConstraints.count { if 0 < layoutConstraints.count {
layoutConstraints += Layout.constraint(verticalFormat, options: nil, metrics: nil, views: views) layoutConstraints += Layout.constraint(verticalFormat, options: [], metrics: nil, views: views)
NSLayoutConstraint.activateConstraints(layoutConstraints) NSLayoutConstraint.activateConstraints(layoutConstraints)
} }
} }
}
//
public func ==(lhs: ImageCardView, rhs: ImageCardView) -> Bool { // :name: prepareProperties
return lhs.tag == rhs.tag //
internal func prepareProperties(imageView: UIImageView?, titleLabel: UILabel?, detailLabel: UILabel?, divider: UIView?, leftButtons: Array<MaterialButton>?, rightButtons: Array<MaterialButton>?) {
self.imageView = imageView
self.titleLabel = titleLabel
self.detailLabel = detailLabel
self.divider = divider
self.leftButtons = leftButtons
self.rightButtons = rightButtons
}
//
// :name: prepareView
//
internal override func prepareView() {
super.prepareView()
prepareShadow()
backgroundColor = MaterialTheme.white.color
}
} }
public func <=(lhs: ImageCardView, rhs: ImageCardView) -> Bool { public func <=(lhs: ImageCardView, rhs: ImageCardView) -> Bool {
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string> <string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
<string>io.graphkit.$(PRODUCT_NAME:rfc1034identifier)</string> <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key> <key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string> <string>6.0</string>
<key>CFBundleName</key> <key>CFBundleName</key>
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>FMWK</string> <string>FMWK</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>1.12.0</string> <string>1.13.0</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
......
...@@ -25,7 +25,7 @@ public struct Layout { ...@@ -25,7 +25,7 @@ public struct Layout {
public static func width(parent: UIView, child: UIView, width: CGFloat = 0) { public static func width(parent: UIView, child: UIView, width: CGFloat = 0) {
let metrics: Dictionary<String, AnyObject> = ["width" : width] let metrics: Dictionary<String, AnyObject> = ["width" : width]
let views: Dictionary<String, AnyObject> = ["child" : child] let views: Dictionary<String, AnyObject> = ["child" : child]
parent.addConstraints(constraint("H:[child(width)]", options: nil, metrics: metrics, views: views)) parent.addConstraints(constraint("H:[child(width)]", options: [], metrics: metrics, views: views))
} }
/** /**
...@@ -34,7 +34,7 @@ public struct Layout { ...@@ -34,7 +34,7 @@ public struct Layout {
public static func height(parent: UIView, child: UIView, height: CGFloat = 0) { public static func height(parent: UIView, child: UIView, height: CGFloat = 0) {
let metrics: Dictionary<String, AnyObject> = ["height" : height] let metrics: Dictionary<String, AnyObject> = ["height" : height]
let views: Dictionary<String, AnyObject> = ["child" : child] let views: Dictionary<String, AnyObject> = ["child" : child]
parent.addConstraints(constraint("V:[child(height)]", options: nil, metrics: metrics, views: views)) parent.addConstraints(constraint("V:[child(height)]", options: [], metrics: metrics, views: views))
} }
/** /**
...@@ -50,8 +50,8 @@ public struct Layout { ...@@ -50,8 +50,8 @@ public struct Layout {
*/ */
public static func expandToParent(parent: UIView, child: UIView) { public static func expandToParent(parent: UIView, child: UIView) {
let views: Dictionary<String, AnyObject> = ["child" : child] let views: Dictionary<String, AnyObject> = ["child" : child]
parent.addConstraints(constraint("H:|[child]|", options: nil, metrics: nil, views: views)) parent.addConstraints(constraint("H:|[child]|", options: [], metrics: nil, views: views))
parent.addConstraints(constraint("V:|[child]|", options: nil, metrics: nil, views: views)) parent.addConstraints(constraint("V:|[child]|", options: [], metrics: nil, views: views))
} }
/** /**
...@@ -65,7 +65,7 @@ public struct Layout { ...@@ -65,7 +65,7 @@ public struct Layout {
:name: expandToParentHorizontallyWithPad :name: expandToParentHorizontallyWithPad
*/ */
public static func expandToParentHorizontallyWithPad(parent: UIView, child: UIView, left: CGFloat = 0, right: CGFloat = 0) { public static func expandToParentHorizontallyWithPad(parent: UIView, child: UIView, left: CGFloat = 0, right: CGFloat = 0) {
parent.addConstraints(constraint("H:|-(left)-[child]-(right)-|", options: nil, metrics: ["left": left, "right": right], views: ["child" : child])) parent.addConstraints(constraint("H:|-(left)-[child]-(right)-|", options: [], metrics: ["left": left, "right": right], views: ["child" : child]))
} }
/** /**
...@@ -79,7 +79,7 @@ public struct Layout { ...@@ -79,7 +79,7 @@ public struct Layout {
:name: expandToParentVerticallyWithPad :name: expandToParentVerticallyWithPad
*/ */
public static func expandToParentVerticallyWithPad(parent: UIView, child: UIView, top: CGFloat = 0, bottom: CGFloat = 0) { public static func expandToParentVerticallyWithPad(parent: UIView, child: UIView, top: CGFloat = 0, bottom: CGFloat = 0) {
parent.addConstraints(constraint("V:|-(top)-[child]-(bottom)-|", options: nil, metrics: ["bottom": bottom, "top": top], views: ["child" : child])) parent.addConstraints(constraint("V:|-(top)-[child]-(bottom)-|", options: [], metrics: ["bottom": bottom, "top": top], views: ["child" : child]))
} }
/** /**
...@@ -87,8 +87,8 @@ public struct Layout { ...@@ -87,8 +87,8 @@ public struct Layout {
*/ */
public static func expandToParentWithPad(parent: UIView, child: UIView, top: CGFloat = 0, left: CGFloat = 0, bottom: CGFloat = 0, right: CGFloat = 0) { public static func expandToParentWithPad(parent: UIView, child: UIView, top: CGFloat = 0, left: CGFloat = 0, bottom: CGFloat = 0, right: CGFloat = 0) {
let views: Dictionary<String, AnyObject> = ["child" : child] let views: Dictionary<String, AnyObject> = ["child" : child]
parent.addConstraints(constraint("H:|-(left)-[child]-(right)-|", options: nil, metrics: ["left": left, "right": right], views: views)) parent.addConstraints(constraint("H:|-(left)-[child]-(right)-|", options: [], metrics: ["left": left, "right": right], views: views))
parent.addConstraints(constraint("V:|-(top)-[child]-(bottom)-|", options: nil, metrics: ["bottom": bottom, "top": top], views: views)) parent.addConstraints(constraint("V:|-(top)-[child]-(bottom)-|", options: [], metrics: ["bottom": bottom, "top": top], views: views))
} }
/** /**
...@@ -97,8 +97,8 @@ public struct Layout { ...@@ -97,8 +97,8 @@ public struct Layout {
public static func alignFromTopLeft(parent: UIView, child: UIView, top: CGFloat = 0, left: CGFloat = 0) { public static func alignFromTopLeft(parent: UIView, child: UIView, top: CGFloat = 0, left: CGFloat = 0) {
let metrics: Dictionary<String, AnyObject> = ["top" : top, "left" : left] let metrics: Dictionary<String, AnyObject> = ["top" : top, "left" : left]
let views: Dictionary<String, AnyObject> = ["child" : child] let views: Dictionary<String, AnyObject> = ["child" : child]
parent.addConstraints(constraint("H:|-(left)-[child]", options: nil, metrics: metrics, views: views)) parent.addConstraints(constraint("H:|-(left)-[child]", options: [], metrics: metrics, views: views))
parent.addConstraints(constraint("V:|-(top)-[child]", options: nil, metrics: metrics, views: views)) parent.addConstraints(constraint("V:|-(top)-[child]", options: [], metrics: metrics, views: views))
} }
/** /**
...@@ -107,8 +107,8 @@ public struct Layout { ...@@ -107,8 +107,8 @@ public struct Layout {
public static func alignFromTopRight(parent: UIView, child: UIView, top: CGFloat = 0, right: CGFloat = 0) { public static func alignFromTopRight(parent: UIView, child: UIView, top: CGFloat = 0, right: CGFloat = 0) {
let metrics: Dictionary<String, AnyObject> = ["top" : top, "right" : right] let metrics: Dictionary<String, AnyObject> = ["top" : top, "right" : right]
let views: Dictionary<String, AnyObject> = ["child" : child] let views: Dictionary<String, AnyObject> = ["child" : child]
parent.addConstraints(constraint("H:[child]-(right)-|", options: nil, metrics: metrics, views: views)) parent.addConstraints(constraint("H:[child]-(right)-|", options: [], metrics: metrics, views: views))
parent.addConstraints(constraint("V:|-(top)-[child]", options: nil, metrics: metrics, views: views)) parent.addConstraints(constraint("V:|-(top)-[child]", options: [], metrics: metrics, views: views))
} }
/** /**
...@@ -117,8 +117,8 @@ public struct Layout { ...@@ -117,8 +117,8 @@ public struct Layout {
public static func alignFromBottomLeft(parent: UIView, child: UIView, bottom: CGFloat = 0, left: CGFloat = 0) { public static func alignFromBottomLeft(parent: UIView, child: UIView, bottom: CGFloat = 0, left: CGFloat = 0) {
let metrics: Dictionary<String, AnyObject> = ["bottom" : bottom, "left" : left] let metrics: Dictionary<String, AnyObject> = ["bottom" : bottom, "left" : left]
let views: Dictionary<String, AnyObject> = ["child" : child] let views: Dictionary<String, AnyObject> = ["child" : child]
parent.addConstraints(constraint("H:|-(left)-[child]", options: nil, metrics: metrics, views: views)) parent.addConstraints(constraint("H:|-(left)-[child]", options: [], metrics: metrics, views: views))
parent.addConstraints(constraint("V:[child]-(bottom)-|", options: nil, metrics: metrics, views: views)) parent.addConstraints(constraint("V:[child]-(bottom)-|", options: [], metrics: metrics, views: views))
} }
/** /**
...@@ -127,8 +127,36 @@ public struct Layout { ...@@ -127,8 +127,36 @@ public struct Layout {
public static func alignFromBottomRight(parent: UIView, child: UIView, bottom: CGFloat = 0, right: CGFloat = 0) { public static func alignFromBottomRight(parent: UIView, child: UIView, bottom: CGFloat = 0, right: CGFloat = 0) {
let metrics: Dictionary<String, AnyObject> = ["bottom" : bottom, "right" : right] let metrics: Dictionary<String, AnyObject> = ["bottom" : bottom, "right" : right]
let views: Dictionary<String, AnyObject> = ["child" : child] let views: Dictionary<String, AnyObject> = ["child" : child]
parent.addConstraints(constraint("H:[child]-(right)-|", options: nil, metrics: metrics, views: views)) parent.addConstraints(constraint("H:[child]-(right)-|", options: [], metrics: metrics, views: views))
parent.addConstraints(constraint("V:[child]-(bottom)-|", options: nil, metrics: metrics, views: views)) parent.addConstraints(constraint("V:[child]-(bottom)-|", options: [], metrics: metrics, views: views))
}
/**
:name: alignFromTop
*/
public static func alignFromTop(parent: UIView, child: UIView, top: CGFloat = 0) {
parent.addConstraints(constraint("V:|-(top)-[child]", options: [], metrics: ["top" : top], views: ["child" : child]))
}
/**
:name: alignFromLeft
*/
public static func alignFromLeft(parent: UIView, child: UIView, left: CGFloat = 0) {
parent.addConstraints(constraint("H:|-(left)-[child]", options: [], metrics: ["left" : left], views: ["child" : child]))
}
/**
:name: alignFromBottom
*/
public static func alignFromBottom(parent: UIView, child: UIView, bottom: CGFloat = 0) {
parent.addConstraints(constraint("V:[child]-(bottom)-|", options: [], metrics: ["bottom" : bottom], views: ["child" : child]))
}
/**
:name: alignFromRight
*/
public static func alignFromRight(parent: UIView, child: UIView, right: CGFloat = 0) {
parent.addConstraints(constraint("H:[child]-(right)-|", options: [], metrics: ["right" : right], views: ["child" : child]))
} }
/** /**
...@@ -142,14 +170,11 @@ public struct Layout { ...@@ -142,14 +170,11 @@ public struct Layout {
:name: constraint :name: constraint
*/ */
public static func constraint(format: String, options: NSLayoutFormatOptions, metrics: Dictionary<String, AnyObject>?, views: Dictionary<String, AnyObject>) -> Array<NSLayoutConstraint> { public static func constraint(format: String, options: NSLayoutFormatOptions, metrics: Dictionary<String, AnyObject>?, views: Dictionary<String, AnyObject>) -> Array<NSLayoutConstraint> {
for (_, v) in views {
v.setTranslatesAutoresizingMaskIntoConstraints(false)
}
return NSLayoutConstraint.constraintsWithVisualFormat( return NSLayoutConstraint.constraintsWithVisualFormat(
format, format,
options: options, options: options,
metrics: metrics, metrics: metrics,
views: views views: views
) as! Array<NSLayoutConstraint> )
} }
} }
...@@ -49,7 +49,7 @@ public class MaterialButton : UIButton { ...@@ -49,7 +49,7 @@ public class MaterialButton : UIButton {
/** /**
:name: init :name: init
*/ */
public required init(coder aDecoder: NSCoder) { public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder) super.init(coder: aDecoder)
prepareView() prepareView()
} }
...@@ -72,7 +72,7 @@ public class MaterialButton : UIButton { ...@@ -72,7 +72,7 @@ public class MaterialButton : UIButton {
/** /**
:name: touchesBegan :name: touchesBegan
*/ */
public override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) { public override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
super.touchesBegan(touches, withEvent: event) super.touchesBegan(touches, withEvent: event)
pulseBegan(touches, withEvent: event) pulseBegan(touches, withEvent: event)
} }
...@@ -80,7 +80,7 @@ public class MaterialButton : UIButton { ...@@ -80,7 +80,7 @@ public class MaterialButton : UIButton {
/** /**
:name: touchesEnded :name: touchesEnded
*/ */
public override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) { public override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
super.touchesEnded(touches, withEvent: event) super.touchesEnded(touches, withEvent: event)
shrink() shrink()
pulseEnded(touches, withEvent: event) pulseEnded(touches, withEvent: event)
...@@ -89,7 +89,7 @@ public class MaterialButton : UIButton { ...@@ -89,7 +89,7 @@ public class MaterialButton : UIButton {
/** /**
:name: touchesCancelled :name: touchesCancelled
*/ */
public override func touchesCancelled(touches: Set<NSObject>!, withEvent event: UIEvent!) { public override func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?) {
super.touchesCancelled(touches, withEvent: event) super.touchesCancelled(touches, withEvent: event)
shrink() shrink()
pulseEnded(touches, withEvent: event) pulseEnded(touches, withEvent: event)
...@@ -99,7 +99,6 @@ public class MaterialButton : UIButton { ...@@ -99,7 +99,6 @@ public class MaterialButton : UIButton {
:name: drawRect :name: drawRect
*/ */
final public override func drawRect(rect: CGRect) { final public override func drawRect(rect: CGRect) {
prepareContext(rect)
prepareBackgroundColorView() prepareBackgroundColorView()
prepareButton() prepareButton()
} }
...@@ -108,7 +107,7 @@ public class MaterialButton : UIButton { ...@@ -108,7 +107,7 @@ public class MaterialButton : UIButton {
// :name: prepareView // :name: prepareView
// //
internal func prepareView() { internal func prepareView() {
setTranslatesAutoresizingMaskIntoConstraints(false) translatesAutoresizingMaskIntoConstraints = false
} }
// //
...@@ -129,18 +128,18 @@ public class MaterialButton : UIButton { ...@@ -129,18 +128,18 @@ public class MaterialButton : UIButton {
// //
// :name: pulseBegan // :name: pulseBegan
// //
internal func pulseBegan(touches: Set<NSObject>, withEvent event: UIEvent) { internal func pulseBegan(touches: Set<NSObject>, withEvent event: UIEvent?) {
pulseView = UIView(frame: CGRectMake(0, 0, bounds.height, bounds.height)) pulseView = UIView(frame: CGRectMake(0, 0, bounds.height, bounds.height))
pulseView!.layer.cornerRadius = bounds.height / 2 pulseView!.layer.cornerRadius = bounds.height / 2
pulseView!.center = (touches.first as! UITouch).locationInView(self) pulseView!.center = (touches.first as! UITouch).locationInView(self)
pulseView!.backgroundColor = pulseColor?.colorWithAlphaComponent(0.5) pulseView!.backgroundColor = pulseColor?.colorWithAlphaComponent(0.3)
backgroundColorView.addSubview(pulseView!) backgroundColorView.addSubview(pulseView!)
} }
// //
// :name: pulseEnded // :name: pulseEnded
// //
internal func pulseEnded(touches: Set<NSObject>, withEvent event: UIEvent) { internal func pulseEnded(touches: Set<NSObject>?, withEvent event: UIEvent?) {
UIView.animateWithDuration(0.3, UIView.animateWithDuration(0.3,
animations: { _ in animations: { _ in
self.pulseView?.alpha = 0 self.pulseView?.alpha = 0
...@@ -152,22 +151,10 @@ public class MaterialButton : UIButton { ...@@ -152,22 +151,10 @@ public class MaterialButton : UIButton {
} }
// //
// :name: prepareContext
//
private func prepareContext(rect: CGRect) {
let context = UIGraphicsGetCurrentContext()
CGContextSaveGState(context);
CGContextAddEllipseInRect(context, rect)
CGContextSetFillColorWithColor(context, MaterialTheme.clear.color.CGColor)
CGContextFillPath(context)
CGContextRestoreGState(context);
}
//
// :name: prepareBackgroundColorView // :name: prepareBackgroundColorView
// //
private func prepareBackgroundColorView() { private func prepareBackgroundColorView() {
backgroundColorView.setTranslatesAutoresizingMaskIntoConstraints(false) backgroundColorView.translatesAutoresizingMaskIntoConstraints = false
backgroundColorView.layer.masksToBounds = true backgroundColorView.layer.masksToBounds = true
backgroundColorView.clipsToBounds = true backgroundColorView.clipsToBounds = true
backgroundColorView.userInteractionEnabled = false backgroundColorView.userInteractionEnabled = false
...@@ -183,7 +170,7 @@ public class MaterialButton : UIButton { ...@@ -183,7 +170,7 @@ public class MaterialButton : UIButton {
delay: 0, delay: 0,
usingSpringWithDamping: 0.2, usingSpringWithDamping: 0.2,
initialSpringVelocity: 10, initialSpringVelocity: 10,
options: nil, options: [],
animations: { animations: {
self.transform = CGAffineTransformIdentity self.transform = CGAffineTransformIdentity
}, },
......
...@@ -49,12 +49,12 @@ public class MaterialCardView : UIView { ...@@ -49,12 +49,12 @@ public class MaterialCardView : UIView {
/** /**
:name: pulseColor :name: pulseColor
*/ */
public var pulseColor: UIColor = MaterialTheme.blueGrey.lighten3 public var pulseColor: UIColor? = MaterialTheme.blueGrey.lighten3
/** /**
:name: init :name: init
*/ */
public required init(coder aDecoder: NSCoder) { public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder) super.init(coder: aDecoder)
prepareView() prepareView()
} }
...@@ -77,7 +77,7 @@ public class MaterialCardView : UIView { ...@@ -77,7 +77,7 @@ public class MaterialCardView : UIView {
/** /**
:name: touchesBegan :name: touchesBegan
*/ */
public override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) { public override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
super.touchesBegan(touches, withEvent: event) super.touchesBegan(touches, withEvent: event)
pulseBegan(touches, withEvent: event) pulseBegan(touches, withEvent: event)
} }
...@@ -85,7 +85,7 @@ public class MaterialCardView : UIView { ...@@ -85,7 +85,7 @@ public class MaterialCardView : UIView {
/** /**
:name: touchesEnded :name: touchesEnded
*/ */
public override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) { public override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
super.touchesEnded(touches, withEvent: event) super.touchesEnded(touches, withEvent: event)
shrink() shrink()
pulseEnded(touches, withEvent: event) pulseEnded(touches, withEvent: event)
...@@ -94,7 +94,7 @@ public class MaterialCardView : UIView { ...@@ -94,7 +94,7 @@ public class MaterialCardView : UIView {
/** /**
:name: touchesCancelled :name: touchesCancelled
*/ */
public override func touchesCancelled(touches: Set<NSObject>!, withEvent event: UIEvent!) { public override func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?) {
super.touchesCancelled(touches, withEvent: event) super.touchesCancelled(touches, withEvent: event)
shrink() shrink()
pulseEnded(touches, withEvent: event) pulseEnded(touches, withEvent: event)
...@@ -104,18 +104,12 @@ public class MaterialCardView : UIView { ...@@ -104,18 +104,12 @@ public class MaterialCardView : UIView {
// :name: prepareView // :name: prepareView
// //
internal func prepareView() { internal func prepareView() {
setTranslatesAutoresizingMaskIntoConstraints(false) translatesAutoresizingMaskIntoConstraints = false
prepareBackgroundColorView() prepareBackgroundColorView()
preparePulseViewContainer() preparePulseViewContainer()
prepareCard()
} }
// //
// :name: prepareCard
//
internal func prepareCard() {}
//
// :name: prepareShadow // :name: prepareShadow
// //
internal func prepareShadow() { internal func prepareShadow() {
...@@ -146,12 +140,12 @@ public class MaterialCardView : UIView { ...@@ -146,12 +140,12 @@ public class MaterialCardView : UIView {
// //
// :name: pulseBegan // :name: pulseBegan
// //
internal func pulseBegan(touches: Set<NSObject>, withEvent event: UIEvent) { internal func pulseBegan(touches: Set<NSObject>, withEvent event: UIEvent?) {
let width: CGFloat = bounds.size.width / 3 let width: CGFloat = bounds.size.width / 3
pulseView = UIView(frame: CGRectMake(0, 0, width, width)) pulseView = UIView(frame: CGRectMake(0, 0, width, width))
pulseView!.layer.cornerRadius = width / 2 pulseView!.layer.cornerRadius = width / 2
pulseView!.center = (touches.first as! UITouch).locationInView(self) pulseView!.center = (touches.first as! UITouch).locationInView(self)
pulseView!.backgroundColor = pulseColor.colorWithAlphaComponent(0.3) pulseView!.backgroundColor = pulseColor?.colorWithAlphaComponent(0.3)
addSubview(pulseViewContainer) addSubview(pulseViewContainer)
Layout.expandToParent(self, child: pulseViewContainer) Layout.expandToParent(self, child: pulseViewContainer)
pulseViewContainer.addSubview(pulseView!) pulseViewContainer.addSubview(pulseView!)
...@@ -164,7 +158,7 @@ public class MaterialCardView : UIView { ...@@ -164,7 +158,7 @@ public class MaterialCardView : UIView {
// //
// :name: pulseEnded // :name: pulseEnded
// //
internal func pulseEnded(touches: Set<NSObject>, withEvent event: UIEvent) { internal func pulseEnded(touches: Set<NSObject>?, withEvent event: UIEvent?) {
UIView.animateWithDuration(0.3, UIView.animateWithDuration(0.3,
animations: { _ in animations: { _ in
self.pulseView?.alpha = 0 self.pulseView?.alpha = 0
...@@ -182,7 +176,7 @@ public class MaterialCardView : UIView { ...@@ -182,7 +176,7 @@ public class MaterialCardView : UIView {
// We need this view so we can use the masksToBounds // We need this view so we can use the masksToBounds
// so the pulse doesn't animate off the button // so the pulse doesn't animate off the button
private func prepareBackgroundColorView() { private func prepareBackgroundColorView() {
backgroundColorView.setTranslatesAutoresizingMaskIntoConstraints(false) backgroundColorView.translatesAutoresizingMaskIntoConstraints = false
backgroundColorView.layer.cornerRadius = 2 backgroundColorView.layer.cornerRadius = 2
backgroundColorView.layer.masksToBounds = true backgroundColorView.layer.masksToBounds = true
backgroundColorView.clipsToBounds = true backgroundColorView.clipsToBounds = true
...@@ -197,7 +191,7 @@ public class MaterialCardView : UIView { ...@@ -197,7 +191,7 @@ public class MaterialCardView : UIView {
// We need this view so we can use the masksToBounds // We need this view so we can use the masksToBounds
// so the pulse doesn't animate off the button // so the pulse doesn't animate off the button
private func preparePulseViewContainer() { private func preparePulseViewContainer() {
pulseViewContainer.setTranslatesAutoresizingMaskIntoConstraints(false) pulseViewContainer.translatesAutoresizingMaskIntoConstraints = false
pulseViewContainer.layer.cornerRadius = 2 pulseViewContainer.layer.cornerRadius = 2
pulseViewContainer.layer.masksToBounds = true pulseViewContainer.layer.masksToBounds = true
pulseViewContainer.clipsToBounds = true pulseViewContainer.clipsToBounds = true
...@@ -212,7 +206,7 @@ public class MaterialCardView : UIView { ...@@ -212,7 +206,7 @@ public class MaterialCardView : UIView {
delay: 0, delay: 0,
usingSpringWithDamping: 0.2, usingSpringWithDamping: 0.2,
initialSpringVelocity: 10, initialSpringVelocity: 10,
options: nil, options: [],
animations: { animations: {
self.transform = CGAffineTransformIdentity self.transform = CGAffineTransformIdentity
}, },
......
...@@ -21,7 +21,7 @@ import UIKit ...@@ -21,7 +21,7 @@ import UIKit
@objc(MaterialTextDelegate) @objc(MaterialTextDelegate)
public protocol TextDelegate { public protocol TextDelegate {
optional func textStorageWillProcessEdit(text: MaterialText, textStorage: MaterialTextStorage, string: String, range: NSRange) optional func textStorageWillProcessEdit(text: MaterialText, textStorage: MaterialTextStorage, string: String, range: NSRange)
optional func textStorageDidProcessEdit(text: MaterialText, textStorage: MaterialTextStorage, string: String, result: NSTextCheckingResult, flags: NSMatchingFlags, stop: UnsafeMutablePointer<ObjCBool>) optional func textStorageDidProcessEdit(text: MaterialText, textStorage: MaterialTextStorage, string: String, result: NSTextCheckingResult?, flags: NSMatchingFlags, stop: UnsafeMutablePointer<ObjCBool>)
} }
@objc(MaterialText) @objc(MaterialText)
...@@ -34,7 +34,7 @@ public class MaterialText: NSObject { ...@@ -34,7 +34,7 @@ public class MaterialText: NSObject {
*/ */
public var searchPattern: String = "(^|\\s)#[\\d\\w_\u{203C}\u{2049}\u{20E3}\u{2122}\u{2139}\u{2194}-\u{2199}\u{21A9}-\u{21AA}\u{231A}-\u{231B}\u{23E9}-\u{23EC}\u{23F0}\u{23F3}\u{24C2}\u{25AA}-\u{25AB}\u{25B6}\u{25C0}\u{25FB}-\u{25FE}\u{2600}-\u{2601}\u{260E}\u{2611}\u{2614}-\u{2615}\u{261D}\u{263A}\u{2648}-\u{2653}\u{2660}\u{2663}\u{2665}-\u{2666}\u{2668}\u{267B}\u{267F}\u{2693}\u{26A0}-\u{26A1}\u{26AA}-\u{26AB}\u{26BD}-\u{26BE}\u{26C4}-\u{26C5}\u{26CE}\u{26D4}\u{26EA}\u{26F2}-\u{26F3}\u{26F5}\u{26FA}\u{26FD}\u{2702}\u{2705}\u{2708}-\u{270C}\u{270F}\u{2712}\u{2714}\u{2716}\u{2728}\u{2733}-\u{2734}\u{2744}\u{2747}\u{274C}\u{274E}\u{2753}-\u{2755}\u{2757}\u{2764}\u{2795}-\u{2797}\u{27A1}\u{27B0}\u{2934}-\u{2935}\u{2B05}-\u{2B07}\u{2B1B}-\u{2B1C}\u{2B50}\u{2B55}\u{3030}\u{303D}\u{3297}\u{3299}\u{1F004}\u{1F0CF}\u{1F170}-\u{1F171}\u{1F17E}-\u{1F17F}\u{1F18E}\u{1F191}-\u{1F19A}\u{1F1E7}-\u{1F1EC}\u{1F1EE}-\u{1F1F0}\u{1F1F3}\u{1F1F5}\u{1F1F7}-\u{1F1FA}\u{1F201}-\u{1F202}\u{1F21A}\u{1F22F}\u{1F232}-\u{1F23A}\u{1F250}-\u{1F251}\u{1F300}-\u{1F320}\u{1F330}-\u{1F335}\u{1F337}-\u{1F37C}\u{1F380}-\u{1F393}\u{1F3A0}-\u{1F3C4}\u{1F3C6}-\u{1F3CA}\u{1F3E0}-\u{1F3F0}\u{1F400}-\u{1F43E}\u{1F440}\u{1F442}-\u{1F4F7}\u{1F4F9}-\u{1F4FC}\u{1F500}-\u{1F507}\u{1F509}-\u{1F53D}\u{1F550}-\u{1F567}\u{1F5FB}-\u{1F640}\u{1F645}-\u{1F64F}\u{1F680}-\u{1F68A}]+" { public var searchPattern: String = "(^|\\s)#[\\d\\w_\u{203C}\u{2049}\u{20E3}\u{2122}\u{2139}\u{2194}-\u{2199}\u{21A9}-\u{21AA}\u{231A}-\u{231B}\u{23E9}-\u{23EC}\u{23F0}\u{23F3}\u{24C2}\u{25AA}-\u{25AB}\u{25B6}\u{25C0}\u{25FB}-\u{25FE}\u{2600}-\u{2601}\u{260E}\u{2611}\u{2614}-\u{2615}\u{261D}\u{263A}\u{2648}-\u{2653}\u{2660}\u{2663}\u{2665}-\u{2666}\u{2668}\u{267B}\u{267F}\u{2693}\u{26A0}-\u{26A1}\u{26AA}-\u{26AB}\u{26BD}-\u{26BE}\u{26C4}-\u{26C5}\u{26CE}\u{26D4}\u{26EA}\u{26F2}-\u{26F3}\u{26F5}\u{26FA}\u{26FD}\u{2702}\u{2705}\u{2708}-\u{270C}\u{270F}\u{2712}\u{2714}\u{2716}\u{2728}\u{2733}-\u{2734}\u{2744}\u{2747}\u{274C}\u{274E}\u{2753}-\u{2755}\u{2757}\u{2764}\u{2795}-\u{2797}\u{27A1}\u{27B0}\u{2934}-\u{2935}\u{2B05}-\u{2B07}\u{2B1B}-\u{2B1C}\u{2B50}\u{2B55}\u{3030}\u{303D}\u{3297}\u{3299}\u{1F004}\u{1F0CF}\u{1F170}-\u{1F171}\u{1F17E}-\u{1F17F}\u{1F18E}\u{1F191}-\u{1F19A}\u{1F1E7}-\u{1F1EC}\u{1F1EE}-\u{1F1F0}\u{1F1F3}\u{1F1F5}\u{1F1F7}-\u{1F1FA}\u{1F201}-\u{1F202}\u{1F21A}\u{1F22F}\u{1F232}-\u{1F23A}\u{1F250}-\u{1F251}\u{1F300}-\u{1F320}\u{1F330}-\u{1F335}\u{1F337}-\u{1F37C}\u{1F380}-\u{1F393}\u{1F3A0}-\u{1F3C4}\u{1F3C6}-\u{1F3CA}\u{1F3E0}-\u{1F3F0}\u{1F400}-\u{1F43E}\u{1F440}\u{1F442}-\u{1F4F7}\u{1F4F9}-\u{1F4FC}\u{1F500}-\u{1F507}\u{1F509}-\u{1F53D}\u{1F550}-\u{1F567}\u{1F5FB}-\u{1F640}\u{1F645}-\u{1F64F}\u{1F680}-\u{1F68A}]+" {
didSet { didSet {
textStorage.searchExpression = NSRegularExpression(pattern: searchPattern, options: nil, error: nil) textStorage.searchExpression = try? NSRegularExpression(pattern: searchPattern, options: [])
} }
} }
...@@ -56,11 +56,11 @@ public class MaterialText: NSObject { ...@@ -56,11 +56,11 @@ public class MaterialText: NSObject {
public override init() { public override init() {
textStorage = MaterialTextStorage() textStorage = MaterialTextStorage()
super.init() super.init()
textStorage.searchExpression = NSRegularExpression(pattern: searchPattern, options: nil, error: nil) textStorage.searchExpression = try? NSRegularExpression(pattern: searchPattern, options: [])
textStorage.textStorageWillProcessEdit = { (textStorage: MaterialTextStorage, string: String, range: NSRange) -> Void in textStorage.textStorageWillProcessEdit = { (textStorage: MaterialTextStorage, string: String, range: NSRange) -> Void in
self.delegate?.textStorageWillProcessEdit?(self, textStorage: textStorage, string: string, range: range) self.delegate?.textStorageWillProcessEdit?(self, textStorage: textStorage, string: string, range: range)
} }
textStorage.textStorageDidProcessEdit = { (textStorage: MaterialTextStorage, result: NSTextCheckingResult, flags: NSMatchingFlags, stop: UnsafeMutablePointer<ObjCBool>) -> Void in textStorage.textStorageDidProcessEdit = { (textStorage: MaterialTextStorage, result: NSTextCheckingResult?, flags: NSMatchingFlags, stop: UnsafeMutablePointer<ObjCBool>) -> Void in
self.delegate?.textStorageDidProcessEdit?(self, textStorage: textStorage, string: textStorage.string, result: result, flags: flags, stop: stop) self.delegate?.textStorageDidProcessEdit?(self, textStorage: textStorage, string: textStorage.string, result: result, flags: flags, stop: stop)
} }
} }
...@@ -82,8 +82,8 @@ public class MaterialText: NSObject { ...@@ -82,8 +82,8 @@ public class MaterialText: NSObject {
*/ */
public var matches: Array<String> { public var matches: Array<String> {
get { get {
let results: Array<NSTextCheckingResult> = textStorage.searchExpression!.matchesInString(string, options: nil, range: NSMakeRange(0, count(string.utf16))) as! Array<NSTextCheckingResult> let results: Array<NSTextCheckingResult> = textStorage.searchExpression!.matchesInString(string, options: [], range: NSMakeRange(0, string.utf16.count))
return unique(map(results) {(self.string as NSString).substringWithRange($0.range)}) return unique(results.map {(self.string as NSString).substringWithRange($0.range)})
} }
} }
...@@ -93,6 +93,6 @@ public class MaterialText: NSObject { ...@@ -93,6 +93,6 @@ public class MaterialText: NSObject {
*/ */
private func unique<S: SequenceType, E: Hashable where E == S.Generator.Element>(source: S) -> [E] { private func unique<S: SequenceType, E: Hashable where E == S.Generator.Element>(source: S) -> [E] {
var seen: [E:Bool] = [:] var seen: [E:Bool] = [:]
return filter(source) {nil == seen.updateValue(true, forKey: $0)} return source.filter {nil == seen.updateValue(true, forKey: $0)}
} }
} }
\ No newline at end of file
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
import UIKit import UIKit
internal typealias MaterialTextStorageWillProcessEdit = (MaterialTextStorage, String, NSRange) -> Void internal typealias MaterialTextStorageWillProcessEdit = (MaterialTextStorage, String, NSRange) -> Void
internal typealias MaterialTextStorageDidProcessEdit = (MaterialTextStorage, NSTextCheckingResult, NSMatchingFlags, UnsafeMutablePointer<ObjCBool>) -> Void internal typealias MaterialTextStorageDidProcessEdit = (MaterialTextStorage, NSTextCheckingResult?, NSMatchingFlags, UnsafeMutablePointer<ObjCBool>) -> Void
public class MaterialTextStorage: NSTextStorage { public class MaterialTextStorage: NSTextStorage {
/** /**
...@@ -48,11 +48,17 @@ public class MaterialTextStorage: NSTextStorage { ...@@ -48,11 +48,17 @@ public class MaterialTextStorage: NSTextStorage {
*/ */
internal var textStorageDidProcessEdit: MaterialTextStorageDidProcessEdit? internal var textStorageDidProcessEdit: MaterialTextStorageDidProcessEdit?
required public init(coder aDecoder: NSCoder) { /**
:name: init
*/
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder) super.init(coder: aDecoder)
} }
override public init() { /**
:name: init
*/
public override init() {
super.init() super.init()
} }
...@@ -66,25 +72,37 @@ public class MaterialTextStorage: NSTextStorage { ...@@ -66,25 +72,37 @@ public class MaterialTextStorage: NSTextStorage {
} }
} }
override public func processEditing() { /**
:name: processEditing
*/
public override func processEditing() {
let range: NSRange = (string as NSString).paragraphRangeForRange(editedRange) let range: NSRange = (string as NSString).paragraphRangeForRange(editedRange)
textStorageWillProcessEdit?(self, string, range) textStorageWillProcessEdit?(self, string, range)
searchExpression!.enumerateMatchesInString(string, options: nil, range: range) { (result: NSTextCheckingResult!, flags: NSMatchingFlags, stop: UnsafeMutablePointer<ObjCBool>) in searchExpression!.enumerateMatchesInString(string, options: [], range: range) { (result: NSTextCheckingResult?, flags: NSMatchingFlags, stop: UnsafeMutablePointer<ObjCBool>) -> Void in
self.textStorageDidProcessEdit?(self, result, flags, stop) self.textStorageDidProcessEdit?(self, result, flags, stop)
} }
super.processEditing() super.processEditing()
} }
override public func attributesAtIndex(location: Int, effectiveRange range: NSRangePointer) -> [NSObject : AnyObject] { /**
:name: attributesAtIndex
*/
public override func attributesAtIndex(location: Int, effectiveRange range: NSRangePointer) -> [String : AnyObject] {
return store.attributesAtIndex(location, effectiveRange: range) return store.attributesAtIndex(location, effectiveRange: range)
} }
override public func replaceCharactersInRange(range: NSRange, withString str: String) { /**
:name: replaceCharactersInRange
*/
public override func replaceCharactersInRange(range: NSRange, withString str: String) {
store.replaceCharactersInRange(range, withString: str) store.replaceCharactersInRange(range, withString: str)
edited(NSTextStorageEditActions.EditedCharacters, range: range, changeInLength: count(str.utf16) - range.length) edited(NSTextStorageEditActions.EditedCharacters, range: range, changeInLength: str.utf16.count - range.length)
} }
override public func setAttributes(attrs: [NSObject : AnyObject]?, range: NSRange) { /**
:name: setAttributes
*/
public override func setAttributes(attrs: [String : AnyObject]?, range: NSRange) {
store.setAttributes(attrs, range: range) store.setAttributes(attrs, range: range)
edited(NSTextStorageEditActions.EditedAttributes, range: range, changeInLength: 0) edited(NSTextStorageEditActions.EditedAttributes, range: range, changeInLength: 0)
} }
......
...@@ -20,6 +20,23 @@ import UIKit ...@@ -20,6 +20,23 @@ import UIKit
public struct MaterialTheme {} public struct MaterialTheme {}
// spacing
extension MaterialTheme {
public static var cardVerticalInset: CGFloat = 16
public static var cardHorizontalInset: CGFloat = 16
public static var buttonVerticalInset: CGFloat = 6
public static var buttonHorizontalInset: CGFloat = 16
public static var navigationVerticalInset: CGFloat = 8
public static var navigationHorizontalInset: CGFloat = 8
}
// fonts
extension MaterialTheme {
public static var textFontSize: CGFloat = 16
}
// colors // colors
extension MaterialTheme { extension MaterialTheme {
// clear // clear
...@@ -369,14 +386,3 @@ extension MaterialTheme { ...@@ -369,14 +386,3 @@ extension MaterialTheme {
public static let darken4: UIColor = UIColor(red: 38/255, green: 50/255, blue: 56/255, alpha: 1) public static let darken4: UIColor = UIColor(red: 38/255, green: 50/255, blue: 56/255, alpha: 1)
} }
} }
// spacing
extension MaterialTheme {
public static var verticalInset: CGFloat = 8
public static var horizontalInset: CGFloat = 2 * verticalInset
}
// fonts
extension MaterialTheme {
public static var textFontSize: CGFloat = 16
}
\ No newline at end of file
//
// Copyright (C) 2015 GraphKit, Inc. <http://graphkit.io> and other GraphKit contributors.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program located at the root of the software package
// in a file called LICENSE. If not, see <http://www.gnu.org/licenses/>.
//
import UIKit
public class NavigationBarView: UIView {
//
// :name: layoutConstraints
//
internal lazy var layoutConstraints: Array<NSLayoutConstraint> = Array<NSLayoutConstraint>()
//
// :name: lightContentStatusBar
//
public var lightContentStatusBar: Bool = false {
didSet {
UIApplication.sharedApplication().setStatusBarStyle(lightContentStatusBar ? .LightContent : .Default, animated: true)
}
}
//
// :name: horizontalInset
//
public var horizontalInset: CGFloat = MaterialTheme.cardHorizontalInset / 2 {
didSet {
reloadView()
}
}
/**
:name: titleLabelContainer
*/
public private(set) var titleLabelContainer: UIView?
/**
:name: titleLabel
*/
public var titleLabel: UILabel? {
didSet {
if let t = titleLabel {
// container
if nil == titleLabelContainer {
titleLabelContainer = UIView()
titleLabelContainer!.translatesAutoresizingMaskIntoConstraints = false
titleLabelContainer!.backgroundColor = MaterialTheme.clear.color
addSubview(titleLabelContainer!)
}
// text
titleLabelContainer!.addSubview(t)
t.translatesAutoresizingMaskIntoConstraints = false
t.backgroundColor = MaterialTheme.clear.color
t.font = Roboto.regular
t.numberOfLines = 1
t.lineBreakMode = .ByTruncatingTail
t.textColor = MaterialTheme.white.color
} else {
titleLabelContainer?.removeFromSuperview()
titleLabelContainer = nil
}
reloadView()
}
}
/**
:name: leftButtonsContainer
*/
public private(set) var leftButtonsContainer: UIView?
/**
:name: leftButtons
*/
public var leftButtons: Array<MaterialButton>? {
didSet {
if nil == leftButtons {
leftButtonsContainer?.removeFromSuperview()
leftButtonsContainer = nil
} else if nil == leftButtonsContainer {
leftButtonsContainer = UIView()
leftButtonsContainer!.translatesAutoresizingMaskIntoConstraints = false
leftButtonsContainer!.backgroundColor = MaterialTheme.clear.color
addSubview(leftButtonsContainer!)
} else {
for v in leftButtonsContainer!.subviews {
v.removeFromSuperview()
}
}
reloadView()
}
}
/**
:name: rightButtonsContainer
*/
public private(set) var rightButtonsContainer: UIView?
/**
:name: rightButtons
*/
public var rightButtons: Array<MaterialButton>? {
didSet {
if nil == rightButtons {
rightButtonsContainer?.removeFromSuperview()
rightButtonsContainer = nil
} else if nil == rightButtonsContainer {
rightButtonsContainer = UIView()
rightButtonsContainer!.translatesAutoresizingMaskIntoConstraints = false
rightButtonsContainer!.backgroundColor = MaterialTheme.clear.color
addSubview(rightButtonsContainer!)
} else {
for v in rightButtonsContainer!.subviews {
v.removeFromSuperview()
}
}
reloadView()
}
}
/**
:name: init
*/
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
/**
:name: init
*/
public override init(frame: CGRect) {
super.init(frame: frame)
prepareView()
}
/**
:name: init
*/
public convenience init() {
self.init(frame: CGRect.null)
}
/**
:name: init
*/
public convenience init?(titleLabel: UILabel? = nil, leftButtons: Array<MaterialButton>? = nil, rightButtons: Array<MaterialButton>? = nil) {
self.init(frame: CGRect.null)
prepareProperties(titleLabel, leftButtons: leftButtons, rightButtons: rightButtons)
}
/**
:name: reloadView
*/
public func reloadView() {
// clear all constraints
NSLayoutConstraint.deactivateConstraints(layoutConstraints)
layoutConstraints.removeAll(keepCapacity: false)
// detect all components and create constraints
var views: Dictionary<String, AnyObject> = Dictionary<String, AnyObject>()
// left buttons
if nil != leftButtons {
// clear for updated constraints
leftButtonsContainer!.removeConstraints(leftButtonsContainer!.constraints)
//container
views["leftButtonsContainer"] = leftButtonsContainer!
// leftButtons
var hFormat: String = "H:|"
var buttonViews: Dictionary<String, AnyObject> = Dictionary<String, AnyObject>()
for var i: Int = 0, l: Int = leftButtons!.count; i < l; ++i {
let button: MaterialButton = leftButtons![i]
leftButtonsContainer!.addSubview(button)
buttonViews["button\(i)"] = button
hFormat += "-(horizontalInset)-[button\(i)]"
Layout.expandToParentVerticallyWithPad(leftButtonsContainer!, child: button)
}
leftButtonsContainer!.addConstraints(Layout.constraint(hFormat + "|", options: [], metrics: ["horizontalInset": horizontalInset], views: buttonViews))
}
// title
if nil != titleLabel {
// clear for updated constraints
titleLabelContainer!.removeConstraints(titleLabelContainer!.constraints)
// container
views["titleLabelContainer"] = titleLabelContainer!
// common text
Layout.expandToParentVerticallyWithPad(titleLabelContainer!, child: titleLabel!)
Layout.expandToParentHorizontallyWithPad(titleLabelContainer!, child: titleLabel!)
}
// right buttons
if nil != rightButtons {
// clear for updated constraints
rightButtonsContainer!.removeConstraints(rightButtonsContainer!.constraints)
//container
views["rightButtonsContainer"] = rightButtonsContainer!
// leftButtons
var hFormat: String = "H:|"
var buttonViews: Dictionary<String, AnyObject> = Dictionary<String, AnyObject>()
for var i: Int = 0, l: Int = rightButtons!.count; i < l; ++i {
let button: MaterialButton = rightButtons![i]
rightButtonsContainer!.addSubview(button)
buttonViews["button\(i)"] = button
hFormat += "[button\(i)]-(horizontalInset)-"
Layout.expandToParentVerticallyWithPad(rightButtonsContainer!, child: button)
}
rightButtonsContainer!.addConstraints(Layout.constraint(hFormat + "|", options: [], metrics: ["horizontalInset": horizontalInset], views: buttonViews))
}
if nil != leftButtons && nil != titleLabel {
layoutConstraints += Layout.constraint("H:|[leftButtonsContainer]-(inset)-[titleLabelContainer]", options: [], metrics: ["inset": horizontalInset], views: ["leftButtonsContainer": leftButtonsContainer!, "titleLabelContainer": titleLabelContainer!])
Layout.alignFromBottom(self, child: leftButtonsContainer!, bottom: horizontalInset)
Layout.alignFromBottom(self, child: titleLabelContainer!, bottom: horizontalInset + MaterialTheme.buttonVerticalInset - 1)
} else if nil != leftButtons {
layoutConstraints += Layout.constraint("H:|[leftButtonsContainer]", options: [], metrics: ["inset": horizontalInset], views: ["leftButtonsContainer": leftButtonsContainer!])
Layout.alignFromBottom(self, child: leftButtonsContainer!, bottom: horizontalInset)
} else if nil != titleLabel {
layoutConstraints += Layout.constraint("H:|-(inset)-[titleLabelContainer]", options: [], metrics: ["inset": horizontalInset], views: ["titleLabelContainer": titleLabelContainer!])
Layout.alignFromBottom(self, child: titleLabelContainer!, bottom: horizontalInset + MaterialTheme.buttonVerticalInset - 1)
}
if nil != rightButtons {
layoutConstraints += Layout.constraint("H:[rightButtonsContainer]|", options: [], metrics: ["inset": horizontalInset], views: ["rightButtonsContainer": rightButtonsContainer!])
Layout.alignFromBottom(self, child: rightButtonsContainer!, bottom: horizontalInset)
}
// constraints
NSLayoutConstraint.activateConstraints(layoutConstraints)
}
//
// :name: prepareProperties
//
internal func prepareProperties(titleLabel: UILabel?, leftButtons: Array<MaterialButton>?, rightButtons: Array<MaterialButton>?) {
self.titleLabel = titleLabel
self.leftButtons = leftButtons
self.rightButtons = rightButtons
}
//
// :name: prepareView
//
private func prepareView() {
translatesAutoresizingMaskIntoConstraints = false
layer.shadowColor = MaterialTheme.blueGrey.darken4.CGColor
layer.shadowOffset = CGSizeMake(0.2, 0.2)
layer.shadowOpacity = 0.5
layer.shadowRadius = 1
clipsToBounds = false
}
}
\ No newline at end of file
...@@ -24,10 +24,10 @@ public class RaisedButton : MaterialButton { ...@@ -24,10 +24,10 @@ public class RaisedButton : MaterialButton {
// //
internal override func prepareView() { internal override func prepareView() {
super.prepareView() super.prepareView()
titleLabel!.font = Roboto.medium titleLabel!.font = Roboto.regular
setTitleColor(MaterialTheme.white.color, forState: .Normal) setTitleColor(MaterialTheme.white.color, forState: .Normal)
backgroundColor = MaterialTheme.blue.accent2 backgroundColor = MaterialTheme.blue.accent2
contentEdgeInsets = UIEdgeInsetsMake(6, 16, 6, 16) contentEdgeInsets = UIEdgeInsetsMake(MaterialTheme.buttonVerticalInset, MaterialTheme.buttonHorizontalInset, MaterialTheme.buttonVerticalInset, MaterialTheme.buttonHorizontalInset)
} }
// //
...@@ -42,7 +42,7 @@ public class RaisedButton : MaterialButton { ...@@ -42,7 +42,7 @@ public class RaisedButton : MaterialButton {
// //
// :name: pulseBegan // :name: pulseBegan
// //
internal override func pulseBegan(touches: Set<NSObject>, withEvent event: UIEvent) { internal override func pulseBegan(touches: Set<NSObject>, withEvent event: UIEvent?) {
super.pulseBegan(touches, withEvent: event) super.pulseBegan(touches, withEvent: event)
UIView.animateWithDuration(0.3, animations: { UIView.animateWithDuration(0.3, animations: {
self.pulseView!.transform = CGAffineTransformMakeScale(4, 4) self.pulseView!.transform = CGAffineTransformMakeScale(4, 4)
......
...@@ -40,7 +40,7 @@ public enum SideNavigationViewState { ...@@ -40,7 +40,7 @@ public enum SideNavigationViewState {
} }
@objc(SideNavigationViewContainer) @objc(SideNavigationViewContainer)
public class SideNavigationViewContainer : Printable { public class SideNavigationViewContainer : NSObject {
/** /**
:name: state :name: state
*/ */
...@@ -59,7 +59,7 @@ public class SideNavigationViewContainer : Printable { ...@@ -59,7 +59,7 @@ public class SideNavigationViewContainer : Printable {
/** /**
:name: description :name: description
*/ */
public var description: String { public override var description: String {
let s: String = .Opened == state ? "Opened" : "Closed" let s: String = .Opened == state ? "Opened" : "Closed"
return "(state: \(s), point: \(point), frame: \(frame))" return "(state: \(s), point: \(point), frame: \(frame))"
} }
...@@ -362,7 +362,7 @@ public class SideNavigationViewController: UIViewController, UIGestureRecognizer ...@@ -362,7 +362,7 @@ public class SideNavigationViewController: UIViewController, UIGestureRecognizer
/** /**
:name: init :name: init
*/ */
public required init(coder aDecoder: NSCoder) { public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder) super.init(coder: aDecoder)
} }
...@@ -548,28 +548,28 @@ public class SideNavigationViewController: UIViewController, UIGestureRecognizer ...@@ -548,28 +548,28 @@ public class SideNavigationViewController: UIViewController, UIGestureRecognizer
:name: toggleLeftViewContainer :name: toggleLeftViewContainer
*/ */
public func toggleLeftViewContainer(velocity: CGFloat = 0) { public func toggleLeftViewContainer(velocity: CGFloat = 0) {
isLeftContainerOpened ? closeLeftViewContainer(velocity: velocity) : openLeftViewContainer(velocity: velocity) isLeftContainerOpened ? closeLeftViewContainer(velocity) : openLeftViewContainer(velocity)
} }
/** /**
:name: toggleRightViewContainer :name: toggleRightViewContainer
*/ */
public func toggleRightViewContainer(velocity: CGFloat = 0) { public func toggleRightViewContainer(velocity: CGFloat = 0) {
isRightContainerOpened ? closeRightViewContainer(velocity: velocity) : openRightViewContainer(velocity: velocity) isRightContainerOpened ? closeRightViewContainer(velocity) : openRightViewContainer(velocity)
} }
/** /**
:name: toggleBottomViewContainer :name: toggleBottomViewContainer
*/ */
public func toggleBottomViewContainer(velocity: CGFloat = 0) { public func toggleBottomViewContainer(velocity: CGFloat = 0) {
isBottomContainerOpened ? closeBottomViewContainer(velocity: velocity) : openBottomViewContainer(velocity: velocity) isBottomContainerOpened ? closeBottomViewContainer(velocity) : openBottomViewContainer(velocity)
} }
/** /**
:name: toggleTopViewContainer :name: toggleTopViewContainer
*/ */
public func toggleTopViewContainer(velocity: CGFloat = 0) { public func toggleTopViewContainer(velocity: CGFloat = 0) {
isTopContainerOpened ? closeTopViewContainer(velocity: velocity) : openTopViewContainer(velocity: velocity) isTopContainerOpened ? closeTopViewContainer(velocity) : openTopViewContainer(velocity)
} }
/** /**
...@@ -940,7 +940,7 @@ public class SideNavigationViewController: UIViewController, UIGestureRecognizer ...@@ -940,7 +940,7 @@ public class SideNavigationViewController: UIViewController, UIGestureRecognizer
if let c = leftContainer { if let c = leftContainer {
if .Began == gesture.state { if .Began == gesture.state {
addShadow(&leftViewContainer) addShadow(&leftViewContainer)
toggleStatusBar(hide: true) toggleStatusBar(true)
c.state = isLeftContainerOpened ? .Opened : .Closed c.state = isLeftContainerOpened ? .Opened : .Closed
c.point = gesture.locationInView(view) c.point = gesture.locationInView(view)
c.frame = vc.frame c.frame = vc.frame
...@@ -959,9 +959,9 @@ public class SideNavigationViewController: UIViewController, UIGestureRecognizer ...@@ -959,9 +959,9 @@ public class SideNavigationViewController: UIViewController, UIGestureRecognizer
let x: CGFloat = c.point.x >= 1000 || c.point.x <= -1000 ? c.point.x : 0 let x: CGFloat = c.point.x >= 1000 || c.point.x <= -1000 ? c.point.x : 0
c.state = vc.frame.origin.x <= CGFloat(floor(leftOriginX)) + options.horizontalThreshold || c.point.x <= -1000 ? .Closed : .Opened c.state = vc.frame.origin.x <= CGFloat(floor(leftOriginX)) + options.horizontalThreshold || c.point.x <= -1000 ? .Closed : .Opened
if .Closed == c.state { if .Closed == c.state {
closeLeftViewContainer(velocity: x) closeLeftViewContainer(x)
} else { } else {
openLeftViewContainer(velocity: x) openLeftViewContainer(x)
} }
delegate?.sideNavDidEndLeftPan?(self, container: c) delegate?.sideNavDidEndLeftPan?(self, container: c)
} }
...@@ -991,7 +991,7 @@ public class SideNavigationViewController: UIViewController, UIGestureRecognizer ...@@ -991,7 +991,7 @@ public class SideNavigationViewController: UIViewController, UIGestureRecognizer
c.state = isRightContainerOpened ? .Opened : .Closed c.state = isRightContainerOpened ? .Opened : .Closed
c.frame = vc.frame c.frame = vc.frame
addShadow(&rightViewContainer) addShadow(&rightViewContainer)
toggleStatusBar(hide: true) toggleStatusBar(true)
delegate?.sideNavDidBeginRightPan?(self, container: c) delegate?.sideNavDidBeginRightPan?(self, container: c)
} else if .Changed == gesture.state { } else if .Changed == gesture.state {
c.point = gesture.translationInView(gesture.view!) c.point = gesture.translationInView(gesture.view!)
...@@ -1006,9 +1006,9 @@ public class SideNavigationViewController: UIViewController, UIGestureRecognizer ...@@ -1006,9 +1006,9 @@ public class SideNavigationViewController: UIViewController, UIGestureRecognizer
let x: CGFloat = c.point.x <= -1000 || c.point.x >= 1000 ? c.point.x : 0 let x: CGFloat = c.point.x <= -1000 || c.point.x >= 1000 ? c.point.x : 0
c.state = vc.frame.origin.x >= CGFloat(floor(rightOriginX) - options.horizontalThreshold) || c.point.x >= 1000 ? .Closed : .Opened c.state = vc.frame.origin.x >= CGFloat(floor(rightOriginX) - options.horizontalThreshold) || c.point.x >= 1000 ? .Closed : .Opened
if .Closed == c.state { if .Closed == c.state {
closeRightViewContainer(velocity: x) closeRightViewContainer(x)
} else { } else {
openRightViewContainer(velocity: x) openRightViewContainer(x)
} }
delegate?.sideNavDidEndRightPan?(self, container: c) delegate?.sideNavDidEndRightPan?(self, container: c)
} }
...@@ -1035,7 +1035,7 @@ public class SideNavigationViewController: UIViewController, UIGestureRecognizer ...@@ -1035,7 +1035,7 @@ public class SideNavigationViewController: UIViewController, UIGestureRecognizer
if let c = bottomContainer { if let c = bottomContainer {
if .Began == gesture.state { if .Began == gesture.state {
addShadow(&bottomViewContainer) addShadow(&bottomViewContainer)
toggleStatusBar(hide: true) toggleStatusBar(true)
c.state = isBottomContainerOpened ? .Opened : .Closed c.state = isBottomContainerOpened ? .Opened : .Closed
c.point = gesture.locationInView(view) c.point = gesture.locationInView(view)
c.frame = vc.frame c.frame = vc.frame
...@@ -1053,9 +1053,9 @@ public class SideNavigationViewController: UIViewController, UIGestureRecognizer ...@@ -1053,9 +1053,9 @@ public class SideNavigationViewController: UIViewController, UIGestureRecognizer
let y: CGFloat = c.point.y <= -1000 || c.point.y >= 1000 ? c.point.y : 0 let y: CGFloat = c.point.y <= -1000 || c.point.y >= 1000 ? c.point.y : 0
c.state = vc.frame.origin.y >= CGFloat(floor(bottomOriginY) - options.verticalThreshold) || c.point.y >= 1000 ? .Closed : .Opened c.state = vc.frame.origin.y >= CGFloat(floor(bottomOriginY) - options.verticalThreshold) || c.point.y >= 1000 ? .Closed : .Opened
if .Closed == c.state { if .Closed == c.state {
closeBottomViewContainer(velocity: y) closeBottomViewContainer(y)
} else { } else {
openBottomViewContainer(velocity: y) openBottomViewContainer(y)
} }
delegate?.sideNavDidEndBottomPan?(self, container: c) delegate?.sideNavDidEndBottomPan?(self, container: c)
} }
...@@ -1082,7 +1082,7 @@ public class SideNavigationViewController: UIViewController, UIGestureRecognizer ...@@ -1082,7 +1082,7 @@ public class SideNavigationViewController: UIViewController, UIGestureRecognizer
if let c = topContainer { if let c = topContainer {
if .Began == gesture.state { if .Began == gesture.state {
addShadow(&topViewContainer) addShadow(&topViewContainer)
toggleStatusBar(hide: true) toggleStatusBar(true)
c.state = isTopContainerOpened ? .Opened : .Closed c.state = isTopContainerOpened ? .Opened : .Closed
c.point = gesture.locationInView(view) c.point = gesture.locationInView(view)
c.frame = vc.frame c.frame = vc.frame
...@@ -1100,9 +1100,9 @@ public class SideNavigationViewController: UIViewController, UIGestureRecognizer ...@@ -1100,9 +1100,9 @@ public class SideNavigationViewController: UIViewController, UIGestureRecognizer
let y: CGFloat = c.point.y <= -1000 || c.point.y >= 1000 ? c.point.y : 0 let y: CGFloat = c.point.y <= -1000 || c.point.y >= 1000 ? c.point.y : 0
c.state = vc.frame.origin.y >= CGFloat(floor(topOriginY) + options.verticalThreshold) || c.point.y >= 1000 ? .Opened : .Closed c.state = vc.frame.origin.y >= CGFloat(floor(topOriginY) + options.verticalThreshold) || c.point.y >= 1000 ? .Opened : .Closed
if .Closed == c.state { if .Closed == c.state {
closeTopViewContainer(velocity: y) closeTopViewContainer(y)
} else { } else {
openTopViewContainer(velocity: y) openTopViewContainer(y)
} }
delegate?.sideNavDidEndTopPan?(self, container: c) delegate?.sideNavDidEndTopPan?(self, container: c)
} }
...@@ -1243,7 +1243,7 @@ public class SideNavigationViewController: UIViewController, UIGestureRecognizer ...@@ -1243,7 +1243,7 @@ public class SideNavigationViewController: UIViewController, UIGestureRecognizer
private func prepareMainContainer() { private func prepareMainContainer() {
mainViewContainer = UIView(frame: view.bounds) mainViewContainer = UIView(frame: view.bounds)
mainViewContainer!.backgroundColor = MaterialTheme.clear.color mainViewContainer!.backgroundColor = MaterialTheme.clear.color
mainViewContainer!.autoresizingMask = .FlexibleHeight | .FlexibleWidth mainViewContainer!.autoresizingMask = [.FlexibleHeight, .FlexibleWidth]
view.addSubview(mainViewContainer!) view.addSubview(mainViewContainer!)
} }
...@@ -1253,7 +1253,7 @@ public class SideNavigationViewController: UIViewController, UIGestureRecognizer ...@@ -1253,7 +1253,7 @@ public class SideNavigationViewController: UIViewController, UIGestureRecognizer
private func prepareBackdropContainer() { private func prepareBackdropContainer() {
backdropViewContainer = UIView(frame: view.bounds) backdropViewContainer = UIView(frame: view.bounds)
backdropViewContainer!.backgroundColor = options.backdropBackgroundColor backdropViewContainer!.backgroundColor = options.backdropBackgroundColor
backdropViewContainer!.autoresizingMask = .FlexibleHeight | .FlexibleWidth backdropViewContainer!.autoresizingMask = [.FlexibleHeight, .FlexibleWidth]
backdropViewContainer!.layer.opacity = 0 backdropViewContainer!.layer.opacity = 0
view.addSubview(backdropViewContainer!) view.addSubview(backdropViewContainer!)
} }
...@@ -1311,7 +1311,7 @@ public class SideNavigationViewController: UIViewController, UIGestureRecognizer ...@@ -1311,7 +1311,7 @@ public class SideNavigationViewController: UIViewController, UIGestureRecognizer
// //
private func prepareContainerToOpen(inout viewController: UIViewController?, inout viewContainer: UIView?, state: SideNavigationViewState) { private func prepareContainerToOpen(inout viewController: UIViewController?, inout viewContainer: UIView?, state: SideNavigationViewState) {
addShadow(&viewContainer) addShadow(&viewContainer)
toggleStatusBar(hide: true) toggleStatusBar(true)
} }
// //
...@@ -1327,7 +1327,7 @@ public class SideNavigationViewController: UIViewController, UIGestureRecognizer ...@@ -1327,7 +1327,7 @@ public class SideNavigationViewController: UIViewController, UIGestureRecognizer
private func prepareContainedViewController(inout viewContainer: UIView?, inout viewController: UIViewController?) { private func prepareContainedViewController(inout viewContainer: UIView?, inout viewController: UIViewController?) {
if let vc = viewController { if let vc = viewController {
if let c = viewContainer { if let c = viewContainer {
vc.view.setTranslatesAutoresizingMaskIntoConstraints(false) vc.view.translatesAutoresizingMaskIntoConstraints = false
addChildViewController(vc) addChildViewController(vc)
c.addSubview(vc.view) c.addSubview(vc.view)
Layout.expandToParent(c, child: vc.view) Layout.expandToParent(c, child: vc.view)
......
...@@ -27,7 +27,7 @@ public class TextView: UITextView { ...@@ -27,7 +27,7 @@ public class TextView: UITextView {
/** /**
:name: init :name: init
*/ */
public required init(coder aDecoder: NSCoder) { public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder) super.init(coder: aDecoder)
prepareView() prepareView()
} }
...@@ -38,7 +38,7 @@ public class TextView: UITextView { ...@@ -38,7 +38,7 @@ public class TextView: UITextView {
public override init(frame: CGRect, textContainer: NSTextContainer?) { public override init(frame: CGRect, textContainer: NSTextContainer?) {
super.init(frame: frame, textContainer: textContainer) super.init(frame: frame, textContainer: textContainer)
if CGRectZero == frame { if CGRectZero == frame {
setTranslatesAutoresizingMaskIntoConstraints(false) translatesAutoresizingMaskIntoConstraints = false
} }
prepareView() prepareView()
} }
...@@ -58,7 +58,7 @@ public class TextView: UITextView { ...@@ -58,7 +58,7 @@ public class TextView: UITextView {
public var placeholderLabel: UILabel? { public var placeholderLabel: UILabel? {
didSet { didSet {
if let p = placeholderLabel { if let p = placeholderLabel {
p.setTranslatesAutoresizingMaskIntoConstraints(false) p.translatesAutoresizingMaskIntoConstraints = false
p.font = font p.font = font
p.textAlignment = textAlignment p.textAlignment = textAlignment
p.numberOfLines = 0 p.numberOfLines = 0
...@@ -137,7 +137,7 @@ public class TextView: UITextView { ...@@ -137,7 +137,7 @@ public class TextView: UITextView {
if let p = placeholderLabel { if let p = placeholderLabel {
NSLayoutConstraint.deactivateConstraints(layoutConstraints) NSLayoutConstraint.deactivateConstraints(layoutConstraints)
layoutConstraints = Layout.constraint("H:|-(left)-[placeholderLabel]-(right)-|", layoutConstraints = Layout.constraint("H:|-(left)-[placeholderLabel]-(right)-|",
options: nil, options: [],
metrics: [ metrics: [
"left": textContainerInset.left + textContainer.lineFragmentPadding, "left": textContainerInset.left + textContainer.lineFragmentPadding,
"right": textContainerInset.right + textContainer.lineFragmentPadding "right": textContainerInset.right + textContainer.lineFragmentPadding
...@@ -146,7 +146,7 @@ public class TextView: UITextView { ...@@ -146,7 +146,7 @@ public class TextView: UITextView {
]) ])
layoutConstraints += Layout.constraint("V:|-(top)-[placeholderLabel]-(>=bottom)-|", layoutConstraints += Layout.constraint("V:|-(top)-[placeholderLabel]-(>=bottom)-|",
options: nil, options: [],
metrics: [ metrics: [
"top": textContainerInset.top, "top": textContainerInset.top,
"bottom": textContainerInset.bottom "bottom": textContainerInset.bottom
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string> <string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
<string>io.graphkit.$(PRODUCT_NAME:rfc1034identifier)</string> <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key> <key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string> <string>6.0</string>
<key>CFBundleName</key> <key>CFBundleName</key>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment