diff --git a/TVOSButton.podspec b/TVOSButton.podspec index 4f4fe49..bf272e7 100644 --- a/TVOSButton.podspec +++ b/TVOSButton.podspec @@ -16,8 +16,8 @@ Pod::Spec.new do |s| # s.name = "TVOSButton" - s.version = "0.0.1" - s.summary = "A short description of TVOSButton." + s.version = "0.2" + s.summary = "Missing button component for tvOS." # This description is used to generate tags and improve search results. # * Think: What does it do? Why did you write it? What is the focus? @@ -25,9 +25,10 @@ Pod::Spec.new do |s| # * Write the description between the DESC delimiters below. # * Finally, don't worry about the indent, CocoaPods strips it! s.description = <<-DESC + Missing button component for tvos. DESC - s.homepage = "http://EXAMPLE/TVOSButton" + s.homepage = "https://github.com/movielala/TVOSButton" # s.screenshots = "www.example.com/screenshots_1.gif", "www.example.com/screenshots_2.gif" @@ -38,7 +39,7 @@ Pod::Spec.new do |s| # Popular ones are 'MIT', 'BSD' and 'Apache License, Version 2.0'. # - s.license = "MIT (example)" + s.license = "Apache" # s.license = { :type => "MIT", :file => "FILE_LICENSE" } @@ -70,7 +71,7 @@ Pod::Spec.new do |s| # s.ios.deployment_target = "5.0" # s.osx.deployment_target = "10.7" # s.watchos.deployment_target = "2.0" - # s.tvos.deployment_target = "9.0" + s.tvos.deployment_target = "9.0" # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # @@ -79,7 +80,7 @@ Pod::Spec.new do |s| # Supports git, hg, bzr, svn and HTTP. # - s.source = { :git => "http://EXAMPLE/TVOSButton.git", :tag => "0.0.1" } + s.source = { :git => "https://github.com/movielala/TVOSButton.git", :tag => "v0.2" } # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # @@ -90,8 +91,8 @@ Pod::Spec.new do |s| # Not including the public_header_files will make all headers public. # - s.source_files = "Classes", "Classes/**/*.{h,m}" - s.exclude_files = "Classes/Exclude" + s.source_files = "TVOSButton" + # s.exclude_files = "Classes/Exclude" # s.public_header_files = "Classes/**/*.h" @@ -129,9 +130,9 @@ Pod::Spec.new do |s| # where they will only apply to your library. If you depend on other Podspecs # you can include multiple dependencies to ensure it works. - # s.requires_arc = true + s.requires_arc = true # s.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" } - # s.dependency "JSONKit", "~> 1.4" + s.dependency "ALKit" end diff --git a/TVOSButton.xcodeproj/project.pbxproj b/TVOSButton.xcodeproj/project.pbxproj index ee02c90..f2cdb60 100644 --- a/TVOSButton.xcodeproj/project.pbxproj +++ b/TVOSButton.xcodeproj/project.pbxproj @@ -10,6 +10,7 @@ B238BC0B1C6E63CB003160F5 /* TVOSButtonShadow.swift in Sources */ = {isa = PBXBuildFile; fileRef = B238BC0A1C6E63CA003160F5 /* TVOSButtonShadow.swift */; }; B238BC101C6E63E1003160F5 /* TVOSButtonImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = B238BC0F1C6E63E1003160F5 /* TVOSButtonImage.swift */; }; B238BC121C6E640D003160F5 /* TVOSButtonLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B238BC111C6E640D003160F5 /* TVOSButtonLabel.swift */; }; + B25ADB1B1C72D8CD00735E04 /* TVOSToggleButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B25ADB1A1C72D8CD00735E04 /* TVOSToggleButton.swift */; }; B25D2F691C6C54E800C3DAE4 /* TVOSButton.h in Headers */ = {isa = PBXBuildFile; fileRef = B25D2F681C6C54E800C3DAE4 /* TVOSButton.h */; settings = {ATTRIBUTES = (Public, ); }; }; B25D2F701C6C54E900C3DAE4 /* TVOSButton.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B25D2F651C6C54E800C3DAE4 /* TVOSButton.framework */; }; B25D2F751C6C54E900C3DAE4 /* TVOSButtonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B25D2F741C6C54E900C3DAE4 /* TVOSButtonTests.swift */; }; @@ -18,27 +19,6 @@ /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ - B24AE6EA1C6D53F80099207D /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = B24AE6E41C6D53F70099207D /* TVOSButtonExample.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = B24AE6B61C6D53F70099207D; - remoteInfo = TVOSButtonExample; - }; - B24AE6EC1C6D53F80099207D /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = B24AE6E41C6D53F70099207D /* TVOSButtonExample.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = B24AE6C71C6D53F70099207D; - remoteInfo = TVOSButtonExampleTests; - }; - B24AE6EE1C6D53F80099207D /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = B24AE6E41C6D53F70099207D /* TVOSButtonExample.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = B24AE6D21C6D53F70099207D; - remoteInfo = TVOSButtonExampleUITests; - }; B25D2F711C6C54E900C3DAE4 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = B25D2F5C1C6C54E800C3DAE4 /* Project object */; @@ -53,7 +33,7 @@ B238BC0A1C6E63CA003160F5 /* TVOSButtonShadow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TVOSButtonShadow.swift; sourceTree = ""; }; B238BC0F1C6E63E1003160F5 /* TVOSButtonImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TVOSButtonImage.swift; sourceTree = ""; }; B238BC111C6E640D003160F5 /* TVOSButtonLabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TVOSButtonLabel.swift; sourceTree = ""; }; - B24AE6E41C6D53F70099207D /* TVOSButtonExample.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = TVOSButtonExample.xcodeproj; path = TVOSButtonExample/TVOSButtonExample.xcodeproj; sourceTree = ""; }; + B25ADB1A1C72D8CD00735E04 /* TVOSToggleButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TVOSToggleButton.swift; sourceTree = ""; }; B25D2F651C6C54E800C3DAE4 /* TVOSButton.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TVOSButton.framework; sourceTree = BUILT_PRODUCTS_DIR; }; B25D2F681C6C54E800C3DAE4 /* TVOSButton.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TVOSButton.h; sourceTree = ""; }; B25D2F6A1C6C54E800C3DAE4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -102,20 +82,9 @@ name = Pods; sourceTree = ""; }; - B24AE6E51C6D53F70099207D /* Products */ = { - isa = PBXGroup; - children = ( - B24AE6EB1C6D53F80099207D /* TVOSButtonExample.app */, - B24AE6ED1C6D53F80099207D /* TVOSButtonExampleTests.xctest */, - B24AE6EF1C6D53F80099207D /* TVOSButtonExampleUITests.xctest */, - ); - name = Products; - sourceTree = ""; - }; B25D2F5B1C6C54E800C3DAE4 = { isa = PBXGroup; children = ( - B24AE6E41C6D53F70099207D /* TVOSButtonExample.xcodeproj */, B25D2F671C6C54E800C3DAE4 /* TVOSButton */, B25D2F731C6C54E900C3DAE4 /* TVOSButtonTests */, B25D2F661C6C54E800C3DAE4 /* Products */, @@ -142,6 +111,7 @@ B238BC0A1C6E63CA003160F5 /* TVOSButtonShadow.swift */, B238BC111C6E640D003160F5 /* TVOSButtonLabel.swift */, B238BC0F1C6E63E1003160F5 /* TVOSButtonImage.swift */, + B25ADB1A1C72D8CD00735E04 /* TVOSToggleButton.swift */, ); path = TVOSButton; sourceTree = ""; @@ -235,12 +205,6 @@ mainGroup = B25D2F5B1C6C54E800C3DAE4; productRefGroup = B25D2F661C6C54E800C3DAE4 /* Products */; projectDirPath = ""; - projectReferences = ( - { - ProductGroup = B24AE6E51C6D53F70099207D /* Products */; - ProjectRef = B24AE6E41C6D53F70099207D /* TVOSButtonExample.xcodeproj */; - }, - ); projectRoot = ""; targets = ( B25D2F641C6C54E800C3DAE4 /* TVOSButton */, @@ -249,30 +213,6 @@ }; /* End PBXProject section */ -/* Begin PBXReferenceProxy section */ - B24AE6EB1C6D53F80099207D /* TVOSButtonExample.app */ = { - isa = PBXReferenceProxy; - fileType = wrapper.application; - path = TVOSButtonExample.app; - remoteRef = B24AE6EA1C6D53F80099207D /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - B24AE6ED1C6D53F80099207D /* TVOSButtonExampleTests.xctest */ = { - isa = PBXReferenceProxy; - fileType = wrapper.cfbundle; - path = TVOSButtonExampleTests.xctest; - remoteRef = B24AE6EC1C6D53F80099207D /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - B24AE6EF1C6D53F80099207D /* TVOSButtonExampleUITests.xctest */ = { - isa = PBXReferenceProxy; - fileType = wrapper.cfbundle; - path = TVOSButtonExampleUITests.xctest; - remoteRef = B24AE6EE1C6D53F80099207D /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; -/* End PBXReferenceProxy section */ - /* Begin PBXResourcesBuildPhase section */ B25D2F631C6C54E800C3DAE4 /* Resources */ = { isa = PBXResourcesBuildPhase; @@ -330,6 +270,7 @@ files = ( B238BC121C6E640D003160F5 /* TVOSButtonLabel.swift in Sources */, B25D2F801C6C551100C3DAE4 /* TVOSButton.swift in Sources */, + B25ADB1B1C72D8CD00735E04 /* TVOSToggleButton.swift in Sources */, B238BC101C6E63E1003160F5 /* TVOSButtonImage.swift in Sources */, B238BC0B1C6E63CB003160F5 /* TVOSButtonShadow.swift in Sources */, ); diff --git a/TVOSButton.xcworkspace/contents.xcworkspacedata b/TVOSButton.xcworkspace/contents.xcworkspacedata index fc88bc6..c89ffca 100644 --- a/TVOSButton.xcworkspace/contents.xcworkspacedata +++ b/TVOSButton.xcworkspace/contents.xcworkspacedata @@ -1,6 +1,9 @@ + + diff --git a/TVOSButton/TVOSButton.swift b/TVOSButton/TVOSButton.swift index 84df39a..5e403e3 100644 --- a/TVOSButton/TVOSButton.swift +++ b/TVOSButton/TVOSButton.swift @@ -58,23 +58,24 @@ public struct TVOSButtonStyle { public func applyStyle(onButton button: TVOSButton) { // button - button.tvosButton.backgroundColor = backgroundColor - button.tvosButtonBackgroundImageView.image = backgroundImage - button.tvosButton.layer.cornerRadius = cornerRadius ?? 0 - button.tvosButton.transform = CGAffineTransformMakeScale(scale ?? 1, scale ?? 1) + button.tvosButton?.backgroundColor = backgroundColor + button.tvosButtonBackgroundImageView?.image = backgroundImage + button.tvosButton?.layer.cornerRadius = cornerRadius ?? 0 + button.tvosButton?.layer.masksToBounds = true + button.tvosButton?.transform = CGAffineTransformMakeScale(scale ?? 1, scale ?? 1) // shadow if let shadow = shadow { - shadow.applyStyle(onLayer: button.tvosButton.layer) + shadow.applyStyle(onLayer: button.layer) } else { - TVOSButtonShadow.resetStyle(onLayer: button.tvosButton.layer) + TVOSButtonShadow.resetStyle(onLayer: button.layer) } // content view if let contentView = contentView { - button.tvosCustomContentView.addSubview(contentView) + button.tvosCustomContentView?.addSubview(contentView) } else { - for subview in button.tvosCustomContentView.subviews { + for subview in button.tvosCustomContentView?.subviews ?? [] { subview.removeFromSuperview() } } @@ -82,25 +83,25 @@ public struct TVOSButtonStyle { // badge if let badge = badgeStyle { badge.applyStyle(onImageView: button.tvosBadge) - button.tvosBadge.image = button.badgeImage + button.tvosBadge?.image = button.badgeImage } else { TVOSButtonImage.resetStyle(onImageView: button.tvosBadge) - button.tvosBadge.image = nil + button.tvosBadge?.image = nil } // text if let text = textStyle { text.applyStyle(onLabel: button.tvosTextLabel) - button.tvosTextLabel.text = button.textLabelText + button.tvosTextLabel?.text = button.textLabelText } else { TVOSButtonLabel.resetStyle(onLabel: button.tvosTextLabel) - button.tvosTextLabel.text = nil + button.tvosTextLabel?.text = nil } // title if let title = titleStyle { title.applyStyle(onLabel: button.tvosTitleLabel) - button.tvosTitleLabel.text = button.titleLabelText + button.tvosTitleLabel?.text = button.titleLabelText } else { TVOSButtonLabel.resetStyle(onLabel: button.tvosTitleLabel) } @@ -134,13 +135,13 @@ public class TVOSButton: UIButton { // MARK: Private Properties - private var tvosButton: UIView! - private var tvosButtonBackgroundImageView: UIImageView! - private var tvosCustomContentView: UIView! - private var tvosBadge: UIImageView! - private var tvosTextLabel: UILabel! - private var tvosTitleLabel: UILabel! - private var tvosTitleLabelTopConstraint: NSLayoutConstraint! + private var tvosButton: UIView? + private var tvosButtonBackgroundImageView: UIImageView? + private var tvosCustomContentView: UIView? + private var tvosBadge: UIImageView? + private var tvosTextLabel: UILabel? + private var tvosTitleLabel: UILabel? + private var tvosTitleLabelTopConstraint: NSLayoutConstraint? private(set) var tvosButtonState: TVOSButtonState = .Normal { didSet { @@ -150,9 +151,31 @@ public class TVOSButton: UIButton { // MARK: Public Properties - public var textLabelText: String? - public var titleLabelText: String? - public var badgeImage: UIImage? + public var badgeImage: UIImage? { + didSet { + if tvosButtonStyleForState(tvosButtonState).badgeStyle != nil { + tvosBadge?.image = badgeImage + } + } + } + + public var textLabelText: String? { + didSet { + if tvosButtonStyleForState(tvosButtonState).textStyle != nil { + tvosTextLabel?.text = textLabelText + } + } + } + + public var titleLabelText: String? { + didSet { + if tvosButtonStyleForState(tvosButtonState).titleStyle != nil { + tvosTitleLabel?.text = titleLabelText + } + } + } + + public var titleLabelPaddingYOnFocus: CGFloat = 10 public override var enabled: Bool { didSet { @@ -166,6 +189,14 @@ public class TVOSButton: UIButton { } } + public override var backgroundColor: UIColor? { + get { + return super.backgroundColor + } set { + self.tvosButton?.backgroundColor = newValue + } + } + // MARK: Init public override init(frame: CGRect) { @@ -182,48 +213,57 @@ public class TVOSButton: UIButton { // remove super's subviews if set imageView?.image = nil titleLabel?.text = nil + // tvosButton tvosButton = UIView() - tvosButton.translatesAutoresizingMaskIntoConstraints = false - addSubview(tvosButton) + tvosButton?.translatesAutoresizingMaskIntoConstraints = false + addSubview(tvosButton!) + // tvosButtonBackgroundImage tvosButtonBackgroundImageView = UIImageView() - tvosButtonBackgroundImageView.translatesAutoresizingMaskIntoConstraints = false - tvosButton.addSubview(tvosButtonBackgroundImageView) + tvosButtonBackgroundImageView?.translatesAutoresizingMaskIntoConstraints = false + tvosButton?.addSubview(tvosButtonBackgroundImageView!) + // tvosCustomContentView tvosCustomContentView = UIView() - tvosCustomContentView.translatesAutoresizingMaskIntoConstraints = false - tvosButton.addSubview(tvosCustomContentView) + tvosCustomContentView?.translatesAutoresizingMaskIntoConstraints = false + tvosButton?.addSubview(tvosCustomContentView!) + // tvosBadge tvosBadge = UIImageView() - tvosBadge.translatesAutoresizingMaskIntoConstraints = false - tvosButton.addSubview(tvosBadge) + tvosBadge?.translatesAutoresizingMaskIntoConstraints = false + tvosButton?.addSubview(tvosBadge!) + // tvosTextLabel tvosTextLabel = UILabel() - tvosTextLabel.translatesAutoresizingMaskIntoConstraints = false - tvosButton.addSubview(tvosTextLabel) + tvosTextLabel?.translatesAutoresizingMaskIntoConstraints = false + tvosButton?.addSubview(tvosTextLabel!) + // tvosTitleLabel tvosTitleLabel = UILabel() - tvosTitleLabel.translatesAutoresizingMaskIntoConstraints = false - addSubview(tvosTitleLabel) + tvosTitleLabel?.translatesAutoresizingMaskIntoConstraints = false + addSubview(tvosTitleLabel!) + // add button constraints - tvosButton.fill(toView: self) - tvosButtonBackgroundImageView.fill(toView: tvosButton) - tvosCustomContentView.fill(toView: tvosButton) - tvosBadge.fill(toView: tvosButton) - tvosTextLabel.fill(toView: tvosButton) + tvosButton?.fill(toView: self) + tvosButtonBackgroundImageView?.fill(toView: tvosButton!) + tvosCustomContentView?.fill(toView: tvosButton!) + tvosBadge?.fill(toView: tvosButton!) + tvosTextLabel?.fill(toView: tvosButton!) + // add title constraints - tvosTitleLabel.fillHorizontal(toView: self) - tvosTitleLabel.pinHeight(50) + tvosTitleLabel?.fillHorizontal(toView: self) + tvosTitleLabel?.pinHeight(50) tvosTitleLabelTopConstraint = NSLayoutConstraint( - item: tvosTitleLabel, + item: tvosTitleLabel!, attribute: .Top, relatedBy: .Equal, toItem: self, attribute: .Bottom, multiplier: 1, constant: 0) - addConstraint(tvosTitleLabelTopConstraint) + addConstraint(tvosTitleLabelTopConstraint!) + // finalize layer.masksToBounds = false handleStateDidChange() @@ -245,25 +285,58 @@ public class TVOSButton: UIButton { public func tvosButtonStateDidChange(tvosButtonState: TVOSButtonState) { } /// Override this function for style your subclass TVOSButton. - public func tvosButtonStyleForState(tvosButtonState: TVOSButtonState) -> TVOSButtonStyle? { - return nil + public func tvosButtonStyleForState(tvosButtonState: TVOSButtonState) -> TVOSButtonStyle { + switch tvosButtonState { + case .Focused: + return TVOSButtonStyle( + backgroundColor: UIColor.whiteColor(), + backgroundImage: nil, + cornerRadius: 10, + scale: 1.1, + shadow: TVOSButtonShadow.Focused, + contentView: nil, + badgeStyle: TVOSButtonImage.Fit, + textStyle: TVOSButtonLabel.DefaultText(color: UIColor.blackColor()), + titleStyle: TVOSButtonLabel.DefaultTitle(color: UIColor.whiteColor())) + + case .Highlighted: + return TVOSButtonStyle( + backgroundColor: UIColor.whiteColor(), + backgroundImage: nil, + cornerRadius: 10, + scale: 0.95, + shadow: TVOSButtonShadow.Highlighted, + contentView: nil, + badgeStyle: TVOSButtonImage.Fit, + textStyle: TVOSButtonLabel.DefaultText(color: UIColor.blackColor()), + titleStyle: TVOSButtonLabel.DefaultTitle(color: UIColor.whiteColor())) + + default: + return TVOSButtonStyle( + backgroundColor: UIColor.grayColor(), + cornerRadius: 10, + badgeStyle: TVOSButtonImage.Fit, + textStyle: TVOSButtonLabel.DefaultText(color: UIColor.whiteColor()), + titleStyle: TVOSButtonLabel.DefaultTitle(color: UIColor.whiteColor())) + } } - private func handleStateDidChange() { + public func handleStateDidChange() { tvosButtonStateDidChange(tvosButtonState) - if let style = tvosButtonStyleForState(tvosButtonState) { - layoutIfNeeded() - UIView.animateWithDuration(0.3, - delay: 0, - usingSpringWithDamping: 1, - initialSpringVelocity: 0, - options: .AllowAnimatedContent, - animations: { - self.tvosTitleLabelTopConstraint.constant = self.tvosButtonState == .Focused ? 20 : 0 - style.applyStyle(onButton: self) - self.layoutIfNeeded() - }, - completion: nil) - } + let style = tvosButtonStyleForState(tvosButtonState) + layoutIfNeeded() + UIView.animateWithDuration(0.2, + delay: 0, + usingSpringWithDamping: 1, + initialSpringVelocity: 0, + options: .AllowAnimatedContent, + animations: { + // animate title label down because of button scales up on focus + self.tvosTitleLabelTopConstraint?.constant = self.tvosButtonState == .Focused ? self.titleLabelPaddingYOnFocus : 0 + // handle styling + style.applyStyle(onButton: self) + self.layoutIfNeeded() + }, + completion: nil) } } diff --git a/TVOSButton/TVOSButtonImage.swift b/TVOSButton/TVOSButtonImage.swift index 74a5ea0..c1ae33d 100644 --- a/TVOSButton/TVOSButtonImage.swift +++ b/TVOSButton/TVOSButtonImage.swift @@ -44,7 +44,8 @@ public enum TVOSButtonImage { } } - public func applyStyle(onImageView imageView: UIImageView) { + public func applyStyle(onImageView imageView: UIImageView?) { + guard let imageView = imageView else { return } let style = getStyle() imageView.backgroundColor = style.backgroundColor imageView.layer.cornerRadius = style.cornerRadius ?? 0 @@ -57,7 +58,8 @@ public enum TVOSButtonImage { } } - public static func resetStyle(onImageView imageView: UIImageView) { + public static func resetStyle(onImageView imageView: UIImageView?) { + guard let imageView = imageView else { return } imageView.image = nil imageView.backgroundColor = nil imageView.layer.cornerRadius = 0 diff --git a/TVOSButton/TVOSButtonLabel.swift b/TVOSButton/TVOSButtonLabel.swift index 6d05ff8..9bc4a1e 100644 --- a/TVOSButton/TVOSButtonLabel.swift +++ b/TVOSButton/TVOSButtonLabel.swift @@ -23,8 +23,8 @@ public enum TVOSButtonLabel { font: font, alignment: alignment, shadow: shadow) - case .DefaultText(let color): + case .DefaultText(let color): return TVOSButtonLabel.Custom( color: color, font: nil, @@ -35,14 +35,15 @@ public enum TVOSButtonLabel { case .DefaultTitle(let color): return TVOSButtonLabel.Custom( color: color, - font: nil, + font: UIFont.preferredFontForTextStyle(UIFontTextStyleCaption1), alignment: nil, - shadow: TVOSButtonShadow.Title) + shadow: TVOSButtonShadow.TitleLabel) .getStyle() } } - public func applyStyle(onLabel label: UILabel) { + public func applyStyle(onLabel label: UILabel?) { + guard let label = label else { return } let style = getStyle() style.shadow?.applyStyle(onLayer: label.layer) label.textColor = style.color ?? UIColor.whiteColor() @@ -50,7 +51,8 @@ public enum TVOSButtonLabel { label.textAlignment = style.alignment ?? .Center } - public static func resetStyle(onLabel label: UILabel) { + public static func resetStyle(onLabel label: UILabel?) { + guard let label = label else { return } TVOSButtonShadow.resetStyle(onLayer: label.layer) label.textColor = UIColor.whiteColor() label.font = UIFont.preferredFontForTextStyle(UIFontTextStyleHeadline) diff --git a/TVOSButton/TVOSButtonShadow.swift b/TVOSButton/TVOSButtonShadow.swift index e94847e..d94ae2c 100644 --- a/TVOSButton/TVOSButtonShadow.swift +++ b/TVOSButton/TVOSButtonShadow.swift @@ -13,9 +13,9 @@ public enum TVOSButtonShadow { case Default(offsetX: CGFloat, offsetY: CGFloat, radius: CGFloat) case Focused case Highlighted - case Title + case TitleLabel - public func getStyle() -> TVOSButtonShadowStyle { + public func getStyle(withHeight height: CGFloat) -> TVOSButtonShadowStyle { switch self { case .Custom(let color, let offset, let opacity, let radius, let path): return TVOSButtonShadowStyle( @@ -29,35 +29,36 @@ public enum TVOSButtonShadow { return TVOSButtonShadowStyle( color: UIColor.blackColor(), offset: CGSize(width: x, height: y), - opacity: 0.4, + opacity: 0.2, radius: r, path: nil) case .Focused: return TVOSButtonShadow.Default( offsetX: 0, - offsetY: 5, + offsetY: 25, radius: 10) - .getStyle() + .getStyle(withHeight: height) case .Highlighted: return TVOSButtonShadow.Default( offsetX: 0, - offsetY: 0, - radius: 5) - .getStyle() + offsetY: 5, + radius: 10) + .getStyle(withHeight: height) - case .Title: + case .TitleLabel: return TVOSButtonShadow.Default( - offsetX: 0, + offsetX: 0, offsetY: 2, radius: 3) - .getStyle() + .getStyle(withHeight: height) } } - public func applyStyle(onLayer layer: CALayer) { - let style = getStyle() + public func applyStyle(onLayer layer: CALayer?) { + guard let layer = layer else { return } + let style = getStyle(withHeight: layer.frame.height) layer.shadowColor = style.color?.CGColor layer.shadowOffset = style.offset ?? CGSize.zero layer.shadowOpacity = style.opacity ?? 1 @@ -65,7 +66,8 @@ public enum TVOSButtonShadow { layer.shadowRadius = style.radius ?? 0 } - public static func resetStyle(onLayer layer: CALayer) { + public static func resetStyle(onLayer layer: CALayer?) { + guard let layer = layer else { return } layer.shadowColor = nil layer.shadowOffset = CGSize.zero layer.shadowOpacity = 0 diff --git a/TVOSButton/TVOSToggleButton.swift b/TVOSButton/TVOSToggleButton.swift new file mode 100644 index 0000000..59482fd --- /dev/null +++ b/TVOSButton/TVOSToggleButton.swift @@ -0,0 +1,74 @@ +// +// TVOSToggleButton.swift +// TVOSButton +// +// Created by Cem Olcay on 15/02/16. +// Copyright © 2016 MovieLaLa. All rights reserved. +// + +import UIKit + +public typealias TVOSToggleButtonDidToggledAction = (currentState: TVOSToggleButtonState, newState: (newState: TVOSToggleButtonState) -> Void) -> Void + +public enum TVOSToggleButtonState { + case Waiting(text: String?) + case On(text: String?) + case Off(text: String?) +} + +public class TVOSToggleButton: TVOSButton { + + // MARK; Properties + + public var didToggledAction: TVOSToggleButtonDidToggledAction? + public var toggleState: TVOSToggleButtonState = .Waiting(text: nil) { + didSet { + toggleStateDidChange() + } + } + + // MARK: Init + + public init(initialState: TVOSToggleButtonState, didToggledAction: TVOSToggleButtonDidToggledAction?) { + super.init(frame: CGRect.zero) + self.toggleState = initialState + self.didToggledAction = didToggledAction + commonInit() + } + + public override init(frame: CGRect) { + super.init(frame: frame) + commonInit() + } + + public required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + commonInit() + } + + public func commonInit() { + addTarget(self, action: "didToggled:", forControlEvents: .PrimaryActionTriggered) + toggleStateDidChange() + } + + // MARK: Toggle + + public func didToggled(sender: TVOSToggleButton) { + didToggledAction?( + currentState: toggleState, + newState: { state in + self.toggleState = state + }) + } + + private func toggleStateDidChange() { + switch toggleState { + case .Waiting(let text): + textLabelText = text + case .On(let text): + textLabelText = text + case .Off(let text): + textLabelText = text + } + } +} diff --git a/TVOSButtonExample/TVOSButtonExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/TVOSButtonExample/TVOSButtonExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/TVOSButtonExample/TVOSButtonExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/TVOSButtonExample/TVOSButtonExample/Base.lproj/Main.storyboard b/TVOSButtonExample/TVOSButtonExample/Base.lproj/Main.storyboard index bce46da..9c4d2b6 100644 --- a/TVOSButtonExample/TVOSButtonExample/Base.lproj/Main.storyboard +++ b/TVOSButtonExample/TVOSButtonExample/Base.lproj/Main.storyboard @@ -16,20 +16,25 @@ - - - + + diff --git a/TVOSButtonExample/TVOSButtonExample/ViewController.swift b/TVOSButtonExample/TVOSButtonExample/ViewController.swift index 4d892a3..068cd92 100644 --- a/TVOSButtonExample/TVOSButtonExample/ViewController.swift +++ b/TVOSButtonExample/TVOSButtonExample/ViewController.swift @@ -9,65 +9,73 @@ import UIKit import TVOSButton -// Just override `tvosButtonStateDidChange` and `tvosButtonStyleForState:tvosButtonState:` functions +class ViewController: UIViewController { -class ExampleButton: TVOSButton { + @IBOutlet var button: TVOSButton! + @IBOutlet var toggleButton: TVOSToggleButton! - override func tvosButtonStateDidChange(tvosButtonState: TVOSButtonState) { - print("\(tvosButtonState)") + override func viewDidLoad() { + super.viewDidLoad() + + // Setup button + button.textLabelText = "TVOSButton" + button.titleLabelText = "Title" + button.addTarget(self, action: "tvosButtonPressed", forControlEvents: .PrimaryActionTriggered) + + // Setup toggleButton + toggleButton.didToggledAction = toggleButtonDidToggledActionHandler + toggleButton.toggleState = .Waiting(text: "...") + requestSomething({ + self.toggleButton.toggleState = .On(text: "Add") + }, failure: { + self.toggleButton.toggleState = .Off(text: "Remove") + }) } - override func tvosButtonStyleForState(tvosButtonState: TVOSButtonState) -> TVOSButtonStyle? { - switch tvosButtonState { - case .Focused: - return TVOSButtonStyle( - backgroundColor: UIColor.whiteColor(), - backgroundImage: nil, - cornerRadius: 10, - scale: 1.1, - shadow: TVOSButtonShadow.Focused, - contentView: nil, - badgeStyle: TVOSButtonImage.Fit, - textStyle: TVOSButtonLabel.DefaultText(color: UIColor.blackColor()), - titleStyle: nil) + func toggleButtonDidToggledActionHandler( + currentState: TVOSToggleButtonState, + updateNewState: (newState: TVOSToggleButtonState) -> Void) { + switch currentState { + case .Waiting(let text): + toggleButton.textLabelText = text - case .Highlighted: - return TVOSButtonStyle( - backgroundColor: UIColor.whiteColor(), - backgroundImage: nil, - cornerRadius: 10, - scale: 0.9, - shadow: TVOSButtonShadow.Highlighted, - contentView: nil, - badgeStyle: TVOSButtonImage.Fit, - textStyle: TVOSButtonLabel.DefaultText(color: UIColor.blackColor()), - titleStyle: nil) + case .On(let text): + toggleButton.textLabelText = text + updateNewState(newState: .Waiting(text: "Adding")) + removeSomething({ + updateNewState(newState: .Off(text: "Remove")) + }, failure: { + updateNewState(newState: .On(text: "Add")) + }) - default: - return TVOSButtonStyle( - backgroundColor: UIColor.redColor(), - backgroundImage: nil, - cornerRadius: 10, - scale: nil, - shadow: nil, - contentView: nil, - badgeStyle: TVOSButtonImage.Fit, - textStyle: TVOSButtonLabel.DefaultText(color: UIColor.whiteColor()), - titleStyle: TVOSButtonLabel.DefaultTitle(color: UIColor.blackColor())) - } + case .Off(let text): + toggleButton.textLabelText = text + updateNewState(newState: .Waiting(text: "Removing")) + addSomethingToServer({ + updateNewState(newState: .On(text: "Add")) + }, failure: { + updateNewState(newState: .Off(text: "Remove")) + }) + } } -} -class ViewController: UIViewController { + // Example request methods for simulate waiting for network - @IBOutlet var button: ExampleButton! + func addSomethingToServer(success: () -> Void, failure: () -> Void) { + requestSomething(success, failure: failure) + } - override func viewDidLoad() { - super.viewDidLoad() - // Setup button - button.textLabelText = "Button" - button.titleLabelText = "Title" - button.addTarget(self, action: "tvosButtonPressed", forControlEvents: .PrimaryActionTriggered) + func removeSomething(success: () -> Void, failure: () -> Void) { + requestSomething(success, failure: failure) + } + + func requestSomething(success: () -> Void, failure: () -> Void) { + dispatch_after( + dispatch_time( + DISPATCH_TIME_NOW, + Int64(0.5 * Double(NSEC_PER_SEC)) + ), + dispatch_get_main_queue(), success) } // Event handler