Repository: akane/Gaikan Branch: master Commit: 3b9eae7d7755 Files: 77 Total size: 145.8 KB Directory structure: gitextract_7_zppekp/ ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── Cartfile ├── Cartfile.private ├── Cartfile.resolved ├── Doc/ │ └── Properties.md ├── Gaikan/ │ ├── Gaikan.h │ ├── Helpers/ │ │ ├── AssociatedObject.swift │ │ ├── CGPoint+Helpers.swift │ │ ├── Dictionary+Helpers.swift │ │ ├── NSAttributedString+Style.swift │ │ ├── NSLayoutAttribute+Helpers.swift │ │ ├── NSLayoutRelation+Helpers.swift │ │ ├── NSShadow+Helpers.swift │ │ ├── Optional+Helpers.swift │ │ ├── UIColor+Helpers.swift │ │ ├── UIImage+Helpers.swift │ │ └── UIView+Helpers.swift │ ├── Info.plist │ ├── InterfaceBuilder/ │ │ └── UIView+InterfaceBuilder.swift │ ├── Property/ │ │ ├── Gradient.swift │ │ ├── Property.swift │ │ └── Value/ │ │ ├── Background.swift │ │ ├── Border.swift │ │ ├── Constraint.swift │ │ ├── Corners.swift │ │ └── Side.swift │ ├── Style/ │ │ ├── Stylable.swift │ │ ├── Style.swift │ │ ├── StylePseudoState.swift │ │ ├── StyleRule+DirectAccess.swift │ │ ├── StyleRule.swift │ │ └── StyleState.swift │ ├── Theme/ │ │ └── Theme.swift │ └── UIKit/ │ ├── Extension/ │ │ ├── UIControl.swift │ │ ├── UILabel.swift │ │ ├── UINavigationBar.swift │ │ ├── UISwitch.swift │ │ └── UIView.swift │ ├── Layer/ │ │ ├── BackgroundLayer.swift │ │ ├── BorderLayer.swift │ │ └── Layer.swift │ ├── Renderer/ │ │ ├── ConstraintRenderer.swift │ │ └── ViewStyleRenderer.swift │ └── VirtualView.swift ├── Gaikan.podspec ├── Gaikan.xcodeproj/ │ ├── project.pbxproj │ ├── project.xcworkspace/ │ │ └── contents.xcworkspacedata │ └── xcshareddata/ │ └── xcschemes/ │ └── Gaikan.xcscheme ├── Gaikan.xcworkspace/ │ └── contents.xcworkspacedata ├── GaikanTests/ │ ├── GaikanTests.swift │ ├── Info.plist │ ├── Style/ │ │ ├── StyleRuleSpec.swift │ │ └── StyleSpec.swift │ ├── Swift/ │ │ └── Extension/ │ │ └── DictionarySpec.swift │ └── UIKit/ │ ├── Extension/ │ │ ├── UILabelSpec.swift │ │ ├── UINavigationBarSpec.swift │ │ └── UIViewSpec.swift │ └── Renderer/ │ └── ConstraintRendererSpec.swift ├── LICENSE ├── Podfile ├── README.md └── Sample/ ├── AppDelegate.swift ├── Assets.xcassets/ │ ├── AppIcon.appiconset/ │ │ └── Contents.json │ ├── Contents.json │ ├── background.imageset/ │ │ └── Contents.json │ └── logo.imageset/ │ └── Contents.json ├── Base.lproj/ │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── CustomView.swift ├── Info.plist ├── Theme/ │ ├── Gradient.swift │ ├── SampleTheme.swift │ ├── UIColor.swift │ └── UIFont.swift └── ViewController.swift ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # Xcode # build/ *.pbxuser !default.pbxuser *.mode1v3 !default.mode1v3 *.mode2v3 !default.mode2v3 *.perspectivev3 !default.perspectivev3 xcuserdata *.xccheckout *.moved-aside DerivedData *.hmap *.ipa *.xcuserstate # CocoaPods # # We recommend against adding the Pods directory to your .gitignore. However # you should judge for yourself, the pros and cons are mentioned at: # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control # Pods/ # Carthage # # Add this line if you want to avoid checking in source code from Carthage dependencies. Carthage/Checkouts Carthage/Build ================================================ FILE: .travis.yml ================================================ language: objective-c osx_image: xcode8.3 env: - > xcode_project=Gaikan.xcodeproj xcode_scheme=Gaikan xcode_sdk=iphonesimulator xcode_platform="platform=iOS Simulator,name=iPhone 6" before_install: - brew update - brew upgrade carthage install: carthage update --platform iOS script: - > set -o pipefail && xcodebuild -project ${xcode_project} -scheme ${xcode_scheme} -sdk ${xcode_sdk} -destination "${xcode_platform}" test | xcpretty -c branches: except: - /^tags.+/ ================================================ FILE: CHANGELOG.md ================================================ # CHANGELOG ## 0.9.1 ### Added - Added shadow property. ### Fixed - Border not correctly set when all sides were defined by [viteinfinite](https://github.com/viteinfinite) [#PR-16](https://github.com/akane/Gaikan/pull/16) # 0.9.0 ## Added - [Border] you can now set only one border side (top, right, bottom, left) if you want. ## Changed ## Fixed - [UIView] Fixed rendering which was triggered every time frames changed - [Background] Correctly render background when frames change # 0.8.0 ## Added - Swift 3 support! By [mcaylus](https://github.com/mcaylus) [#PR-12](https://github.com/akane/Gaikan/pull/12) # 0.7.0 ## Added - Added height, minHeight, maxHeight - Added width, minWidth, maxWidth ## Changed - Migrated to Swift 2.3 # 0.6.0 ## Added - Added Carthage support. By [siemensikkema](https://github.com/siemensikkema) [#PR-7](https://github.com/akane/Gaikan/pull/7) - [Style] Added `textAttributes` on `StyleRule` which converts a style into `NSAttributedString` style attributes. - [Style] Added `margin` attribute. - [Style] Added `textOverflow` attribute. ## Changed ## Fixed - [Property] Fixed `clip` attribute not setted correctly. # 0.5.0 ## Added ## Changed - [Theme] Themes are now simple `AnyObject`. You just define (non static) attributes on it and apply them on a `UIView` usting the new `styleClass` attribute. - [IB] To apply a style right from InterfaceBuilder, you now define `themeClassName` in addition to `styleName`. - [Style] Replaced `applyStyle` with `styleInline`. While the former was totally replacing the style, the newer appends style to `styleClass`. ## Fixed # 0.4.0 ## Added - [Property] Added `CornerRadius`. - [Property] Added `Clip`. - [Property] Added `Opacity`. - [Property] Added `Transform`. ## Changed ## Fixed - [Background] Fixed renderer positions for `Gradient`. ================================================ FILE: Cartfile ================================================ github "facebook/KVOController" ================================================ FILE: Cartfile.private ================================================ github "Quick/Nimble" github "Quick/Quick" ================================================ FILE: Cartfile.resolved ================================================ github "facebook/KVOController" "v1.1.0" github "Quick/Nimble" "v4.1.0" github "Quick/Quick" "v0.9.3" ================================================ FILE: Doc/Properties.md ================================================ # Style properties ## Background - __**since**__: 0.3 - __**description**__: Sets a background to your view (color, gradient and or image) - __**applies**__: `UIView` - __**usage**__: ```swift style.background = UIColor.redColor() style.background = Background(UIImage(named: "bg"), UIColor.orangeGradient()) ``` ## Border - __**since**__: 0.1 - __**description**__: Sets view border color and width - __**applies**__: `UIView` - __**usage**__: ```swift // until 0.7 style.border = Border(width: 1, color: UIColor.blueColor()) // after 0.7 you can define one or all borders style.border = Border(all: (width: 1, color: UIColor.blue)) style.border = Border(.left, style: (width: 1, color: UIColor.blue)) style.border = Border([.left, .right] : (width: 1, color: UIColor.blue)) ``` ## Clip - __**since**__: 0.4 - __**description**__: enables/disables view overflow clipping - __**applies**__: `UIView` - __**default**__: false - __**usage**__: ```swift style.clip = true ``` ## Color - __**since**__: 0.1 - __**description**__: Sets text color - __**applies**__: `UILabel` - __**usage**__: ```swift style.color = UIColor.redColor() ``` ## CornerRadius - __**since**__: 0.4 - __**description**__: Sets view corner radius - __**applies**__: `UIView` - __**usage**__: ```swift style.corners = Corners(radius: 4) ``` ## Font - __**since**__: 0.1 - __**description**__: Sets text font - __**applies**__: `UILabel` - __**usage**__: ```swift style.font = UIFont.systemFontOfSize(15) ``` ## Height - __**since**__: 0.7 - __**description**__: Sets a view height using AutoLayout. - __**applies**__: `UIView` - __**usage**__: ```swift style.height = 42 style.height = 50 ~ UILayoutPriorityDefaultHigh ``` ## Margin - __**since**__: 0.6 - __**description**__: Sets a view internal margin/padding, i.e. `UIView.layoutMargins` or `UIButton.contentEdgeInsets`. - __**applies**__: `UIView`, `UIButton` - __**usage**__: ```swift style.margin = UIEdgeInsetsMake(4, 4, 2, 2) /// sets UIView.layoutMargins /// or UIButton.contentEdgeInsets ``` ## MaxHeight - __**since**__: 0.7 - __**description**__: Sets a view maximum height using AutoLayout. - __**applies**__: `UIView` - __**usage**__: ```swift style.maxHeight = 42 style.maxHeight = 50 ~ UILayoutPriorityDefaultHigh ``` ## MaxWidth - __**since**__: 0.7 - __**description**__: Sets a view maximum width using AutoLayout. - __**applies**__: `UIView` - __**see**__: maxHeight ## MinHeight - __**since**__: 0.7 - __**description**__: Sets a view minimum height using AutoLayout. - __**applies**__: `UIView` - __**usage**__: ```swift style.minHeight = 42 style.minHeight = 50 ~ UILayoutPriorityDefaultHigh ``` ## MinWidth - __**since**__: 0.7 - __**description**__: Sets a view minimum width using AutoLayout. - __**applies**__: `UIView` - __**see**__: minHeight ## Opacity - __**since**__: 0.4 - __**description**__: Changes view alpha. - __**applies**__: `UIView` - __**range**__: 0 (tranparent) ... 100 (opaque) - __**usage**__: ```swift style.opacity = 80 ``` ## Shadow - __**since**__: xxx - __**description**__: Applies shadow to view. - __**applies**__: `UIView` - __**usage**__: ```swift style.shadow = NSShadow(offset: CGSize(width: 0, height: 0), radius: 4, color: UIColor.red) ``` ## TextAlign ## TextOverflow ## TintColor ## Transform ## Visible - __**since**__: 0.1 - __**description**__: Sets whether or not the view is visible by changing `hidden` property. - __**applies**__: `UIView` - __**default**__: true - __**usage**__: ```swift style.visible = false ``` ## Width - __**since**__: 0.7 - __**description**__: Sets a view width using AutoLayout. - __**applies**__: `UIView` - __**see**__: height ================================================ FILE: Gaikan/Gaikan.h ================================================ // // This file is part of Gaikan // // Created by JC on 30/08/15. // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code // #import //! Project version number for Gaikan. FOUNDATION_EXPORT double GaikanVersionNumber; //! Project version string for Gaikan. FOUNDATION_EXPORT const unsigned char GaikanVersionString[]; // In this header, you should import all the public headers of your framework using statements like #import ================================================ FILE: Gaikan/Helpers/AssociatedObject.swift ================================================ // // This file is part of Gaikan // // Created by JC on 25/10/15. // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code // import Foundation /** Code for AssociationObject using Swift types From https://wezzard.com/2015/10/09/associated-object-and-swift-struct **/ internal final class AssociatedObject : NSObject, NSCopying { typealias AssociatedType = T let value: AssociatedType required init(_ value: AssociatedType) { self.value = value } func copy(with zone: NSZone?) -> Any { return AssociatedObject(self.value) } } extension AssociatedObject where T:NSCopying { func copyWithZone(_ zone: NSZone?) -> AnyObject { return AssociatedObject(value.copy(with: zone) as! AssociatedType) } } ================================================ FILE: Gaikan/Helpers/CGPoint+Helpers.swift ================================================ // // This file is part of Gaikan // // Created by JC on 05/11/15. // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code // import Foundation extension CGPoint { public static func topLeft() -> CGPoint { return CGPoint(x: 0, y: 0) } public static func topRight() -> CGPoint { return CGPoint(x: 1, y: 1) } public static func bottomLeft() -> CGPoint { return CGPoint(x: 0, y: 1) } public static func bottomRight() -> CGPoint { return CGPoint(x: 1, y: 1) } public static func top() -> CGPoint { return CGPoint(x: 0.5, y: 0) } public static func bottom() -> CGPoint { return CGPoint(x: 0.5, y: 1) } } ================================================ FILE: Gaikan/Helpers/Dictionary+Helpers.swift ================================================ // // This file is part of Gaikan // // Created by JC on 24/06/16. // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code // import Foundation extension Dictionary where Key : Any, Value : OptionalProtocol, Value.WrappedType : AnyObject { typealias Wrapped = Value.WrappedType func trimmed() -> [Key:Wrapped] { var dict = [Key:Wrapped]() for (key, value) in self { if let value = value.value { dict[key] = value } } return dict } } extension Dictionary { @discardableResult mutating func gaikan_merge(_ dictionary: Dictionary) -> Dictionary { for (key, value) in dictionary { self.updateValue(value, forKey: key) } return self } } ================================================ FILE: Gaikan/Helpers/NSAttributedString+Style.swift ================================================ // // This file is part of Gaikan // // Created by JC on 20/06/16. // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code // import Foundation extension NSAttributedString { public convenience init(string str: String, style: StyleRule) { self.init(string: str, attributes: style.textAttributes) } } extension NSMutableAttributedString { public func setStyle(_ style: StyleRule, range: NSRange) { self.setAttributes(style.textAttributes, range: range) } } ================================================ FILE: Gaikan/Helpers/NSLayoutAttribute+Helpers.swift ================================================ // // NSLayoutAttribute+Helpers.swift // Gaikan // // Created by pjechris on 01/09/16. // Copyright © 2016 fr.akane. All rights reserved. // import Foundation extension NSLayoutAttribute { func identifier() -> String { switch(self) { case .lastBaseline: return "baseline" case .bottom: return "bottom" case .bottomMargin: return "bottomMargin" case .centerX: return "centerX" case .centerXWithinMargins: return "centerXWithinMargins" case .centerY: return "centerY" case .centerYWithinMargins: return "centerYWithinMargins" case .firstBaseline: return "firstBaseline" case .height: return "height" case .leading: return "leading" case .leadingMargin: return "leadingMargin" case .left: return "left" case .leftMargin: return "leftMargin" case .notAnAttribute: return "notAnAttribute" case .right: return "right" case .rightMargin: return "rightMargin" case .top: return "top" case .topMargin: return "topMargin" case .trailing: return "trailing" case .trailingMargin: return "trailingMargin" case .width: return "width" } } } ================================================ FILE: Gaikan/Helpers/NSLayoutRelation+Helpers.swift ================================================ // // NSLayoutRelation+Helpers.swift // Gaikan // // Created by pjechris on 01/09/16. // Copyright © 2016 fr.akane. All rights reserved. // import Foundation extension NSLayoutRelation { func identifier() -> String { switch (self) { case .equal: return "accurate" case .greaterThanOrEqual: return "min" case .lessThanOrEqual: return "max" } } } ================================================ FILE: Gaikan/Helpers/NSShadow+Helpers.swift ================================================ // // NSShadow+Helpers.swift // Gaikan // // Created by pjechris on 07/02/17. // Copyright © 2017 fr.akane. All rights reserved. // import Foundation extension NSShadow { public convenience init(offset: CGSize, radius: CGFloat, color: UIColor?) { self.init() self.shadowOffset = offset self.shadowBlurRadius = radius self.shadowColor = color } } ================================================ FILE: Gaikan/Helpers/Optional+Helpers.swift ================================================ // // This file is part of Gaikan // // Created by JC on 24/06/16. // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code // import Foundation protocol OptionalProtocol { associatedtype WrappedType var value: WrappedType? { get } } extension Optional: OptionalProtocol { var value: Wrapped? { return self } } ================================================ FILE: Gaikan/Helpers/UIColor+Helpers.swift ================================================ // // This file is part of Gaikan // // Created by JC on 05/11/15. // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code // import Foundation extension UIColor : BackgroundValue { public func draw(in ctx: CGContext) { ctx.setFillColor(self.cgColor) ctx.fill(CGRect(origin: CGPoint.zero, size: CGSize(width: ctx.width, height: ctx.height))) } } ================================================ FILE: Gaikan/Helpers/UIImage+Helpers.swift ================================================ // // This file is part of Gaikan // // Created by JC on 15/11/15. // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code // import Foundation extension UIImage : BackgroundValue { public func draw(in ctx: CGContext) { self.draw(in: CGRect(origin: CGPoint.zero, size: CGSize(width: ctx.width, height: ctx.height))) } } ================================================ FILE: Gaikan/Helpers/UIView+Helpers.swift ================================================ // // UIView+Helpers.swift // Gaikan // // Created by pjechris on 10/09/16. // Copyright © 2016 fr.akane. All rights reserved. // import Foundation extension UIView { struct Keys { static var Dimension = "DimensionAttr" } var dimensions: [NSLayoutConstraint] { get { guard let constraints = objc_getAssociatedObject(self, &Keys.Dimension) as? [NSLayoutConstraint] else { self.dimensions = [] return self.dimensions } return constraints } set { objc_setAssociatedObject(self, &Keys.Dimension, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } } ================================================ FILE: Gaikan/Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType FMWK CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion $(CURRENT_PROJECT_VERSION) NSPrincipalClass ================================================ FILE: Gaikan/InterfaceBuilder/UIView+InterfaceBuilder.swift ================================================ // // This file is part of Gaikan // // Created by JC on 03/05/16. // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code // import Foundation var ViewStyleNameAttribute = "ViewStyleNameAttribute" var ViewThemeClassAttr = "ViewThemeClassAttr" /** * Dynamically retrieves a "defined styleClass" from a storyboard/xib. */ extension UIView { /// theme class which contains desired styleClass instance. /// You need to provide both `styleName` and `themeClassName` so that style can be dynamically found @IBInspectable var themeClassName: String? { get { return objc_getAssociatedObject(self, &ViewThemeClassAttr) as? String } set { guard let newValue = newValue else { return } let moduleName = Bundle.main.infoDictionary!["CFBundleExecutable"] as! String let className = newValue.contains(".") ? newValue : moduleName + "." + newValue objc_setAssociatedObject(self, &ViewThemeClassAttr, className, .OBJC_ASSOCIATION_COPY) self.attemptLoadingStyle() } } /// styleClass attribute name into its theme class. /// You need to provide both `styleName` and `themeClassName` so that style can be dynamically found @IBInspectable var styleName: String? { get { return objc_getAssociatedObject(self, &ViewStyleNameAttribute) as? String } set { objc_setAssociatedObject(self, &ViewStyleNameAttribute, newValue, .OBJC_ASSOCIATION_COPY) self.attemptLoadingStyle() } } fileprivate func attemptLoadingStyle() { guard let styleName = self.styleName, let themeClassName = self.themeClassName else { return } let themeClass = NSClassFromString(themeClassName) as! Theme.Type let dynamicTheme = themeClass.init() let dynamicMirror = Mirror(reflecting: dynamicTheme) for (label, value) in dynamicMirror.children { if label == styleName { self.styleClass = value as? Style } } } } ================================================ FILE: Gaikan/Property/Gradient.swift ================================================ // // This file is part of Gaikan // // Created by JC on 05/11/15. // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code // import Foundation public struct Gradient { public typealias GradientItem = (color: UIColor, position: Int) let gradientValues: [(color: UIColor, position: Int)] let to: CGPoint let from: CGPoint public init(from: CGPoint, to: CGPoint, _ gradients: (color: UIColor, position: Int)...) { self.init(from: from, to: to, gradients) } public init(_ gradients: (color: UIColor, position: Int)...) { self.init(from: CGPoint.top(), to: CGPoint.bottom(), gradients) } fileprivate init(from: CGPoint, to: CGPoint, _ gradients: [(color: UIColor, position: Int)]) { self.from = from self.to = to self.gradientValues = gradients } } extension Gradient : BackgroundValue { public func draw(in ctx: CGContext) { let layer = CAGradientLayer() layer.frame = CGRect(origin: CGPoint.zero, size: CGSize(width: ctx.width, height: ctx.height)) layer.endPoint = self.to layer.startPoint = self.from layer.colors = self.gradientValues.map { $0.color.cgColor } layer.locations = self.gradientValues.map { NSNumber(value:($0.position/100)) } layer.render(in: ctx) } } ================================================ FILE: Gaikan/Property/Property.swift ================================================ // // This file is part of Gaikan // // Created by JC on 30/08/15. // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code // import Foundation /// Property that can be used to style a view /// Keep this file Alphabetic ordered public enum Property { case background case border case clip case color case cornerRadius case font case height /// view layoutMargins case margin case maxHeight case maxWidth case minHeight case minWidth case opacity case shadow case textAlign case textOverflow case textShadow case tintColor case transform case visible case width } ================================================ FILE: Gaikan/Property/Value/Background.swift ================================================ // // This file is part of Gaikan // // Created by JC on 05/11/15. // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code // import Foundation /** Any class/struct conforming to BackgroundValue can be used to represent the `Background` property */ public protocol BackgroundValue { /// Renders the object into given context func draw(in ctx: CGContext) } /** Allow to use one or more BackgroundValue objects at once as a unique background */ public struct Background : BackgroundValue { internal var backgrounds: [BackgroundValue] public init(_ backgrounds: BackgroundValue...) { self.backgrounds = backgrounds } public func draw(in ctx: CGContext) { for bg in self.backgrounds { bg.draw(in: ctx) } } } ================================================ FILE: Gaikan/Property/Value/Border.swift ================================================ // // This file is part of Gaikan // // Created by JC on 05/09/15. // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code // import Foundation public typealias BorderStyle = (width: CGFloat, color: UIColor) /** Represents `Border` property */ public struct Border : Equatable { var sides: [Side: BorderStyle] public init(_ side: Side, style: BorderStyle) { self.init(sides: [side], style: style) } public init(all style: BorderStyle) { self.init(sides: [.top, .right, .bottom, .left], style: style) } public init(sides: [Side], style: BorderStyle) { var allSides = [Side:BorderStyle]() for side in sides { allSides[side] = style } self.sides = allSides } public func stylize(_ side: Side, with style: BorderStyle) -> Border { var newBorder = self newBorder.sides[side] = style return newBorder } } public func ==(lhs: Border, rhs: Border) -> Bool { for (key, lvalue) in lhs.sides { guard let rvalue = rhs.sides[key] else { return false } guard lvalue.width == rvalue.width, lvalue.color == rvalue.color else { return false } } return true } ================================================ FILE: Gaikan/Property/Value/Constraint.swift ================================================ // // Constraint.swift // Gaikan // // Created by pjechris on 29/08/16. // Copyright © 2016 fr.akane. All rights reserved. // import Foundation public struct Constraint { public let constant: Double public var priority = UILayoutPriorityRequired public init(constant: Double) { self.constant = constant } } extension Constraint : ExpressibleByFloatLiteral, ExpressibleByIntegerLiteral { public init(floatLiteral value: FloatLiteralType) { self.init(constant: value) } public init(integerLiteral value: IntegerLiteralType) { self.init(constant: Double(value)) } } infix operator ~ public func ~ (lhs: inout Constraint, rhs: UILayoutPriority) -> Constraint { lhs.priority = rhs return lhs } ================================================ FILE: Gaikan/Property/Value/Corners.swift ================================================ // // Corners.swift // Gaikan // // Created by Brandon on 27/01/2016. // Copyright © 2016 fr.akane. All rights reserved. // import Foundation /** Represent the `Corners` property */ public struct Corners { public let radius: CGFloat public init(radius: CGFloat) { self.radius = radius } } ================================================ FILE: Gaikan/Property/Value/Side.swift ================================================ // // Side.swift // Gaikan // // Created by pjechris on 04/12/16. // Copyright © 2016 fr.akane. All rights reserved. // import Foundation public enum Side : String { case top case right case bottom case left public static var all: Set { return [.top, .right, .bottom, .left] } } extension Side : Hashable { public var hashValue: Int { return self.rawValue.hashValue } } //extension Side : Hashable { // public var hashValue: Int { // switch (self) { // case .top(_): // return "top".hashValue // case .right(_): // return "right".hashValue // case .bottom(_): // return "bottom".hashValue // case .left(_): // return "left".hashValue // } // } //} // //public func ==(lhs: Side, rhs: Side) -> Bool { // return lhs.hashValue == rhs.hashValue //} ================================================ FILE: Gaikan/Style/Stylable.swift ================================================ // // This file is part of Gaikan // // Created by JC on 30/08/15. // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code // import Foundation var ComputedStyleAttribute = "ComputedStyleAttribute" /** Applies a `Style` or a `StyleRule` on an object */ public protocol Stylable : class { /// `Style` class to apply. Calls `computeStyle` when changed var styleClass: Style? { get set } /// Style specific to the object. Has precedence over `styleClass`. /// Works like `style` in HTML. var styleInline: StyleRule? { get set } /// Custom style state. If defined `computedStyle` will merge state style with "default" state style var styleState: String? { get } /// Re-applies computed style func updateStyle() static func keyPathsAffectingStyle() -> [String] } extension Stylable { /// Result `StyleRule` from `styleClass` (including `styleState`) + `styleInline`. /// When changed it calls `updateStyle` to apply it. public internal(set) var computedStyle: StyleRule? { get { let value = objc_getAssociatedObject(self, &ComputedStyleAttribute) as? AssociatedObject return value != nil ? value!.value : nil } set { objc_setAssociatedObject(self, &ComputedStyleAttribute, newValue.map { AssociatedObject($0) }, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) self.updateStyle() } } /// Calculates style and store it into `computedStyle` internal func computeStyle() { guard let style = self.styleClass else { self.computedStyle = self.styleInline return } let states = StyleState.states(self) var computedStyle = style.style for state in states { computedStyle = style.states[state].map { return $0.extends(computedStyle) } ?? computedStyle } if let styleInline = self.styleInline { self.computedStyle = styleInline.extends(computedStyle) } else { self.computedStyle = computedStyle } } } ================================================ FILE: Gaikan/Style/Style.swift ================================================ // // This file is part of Gaikan // // Created by JC on 11/10/15. // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code // import Foundation /** * Defines a `StyleRule` on which you can apply supplemntary `StylePseudoState` states. */ public struct Style : ExpressibleByDictionaryLiteral { internal fileprivate(set) var style: StyleRule internal fileprivate(set) var states: [StyleState:StyleRule] = [:] public init(dictionaryLiteral elements: (StyleRule.Key, StyleRule.Value)...) { var attributes = Dictionary() for (attributeName, attributeValue) in elements { attributes[attributeName] = attributeValue } self.init(style: StyleRule(attributes: attributes)) } public init(_ styleBlock: (_ style: inout StyleRule) -> ()) { self.init(style: StyleRule(styleBlock)) } fileprivate init(style: StyleRule) { self.style = style } public func state(_ state: StylePseudoState, styleRule styleBlock: (_ style: inout StyleRule) -> ()) -> Style { return self.state(.pseudoState(state), styleRule: styleBlock) } internal func state(_ state: StyleState, styleRule styleBlock: (_ style: inout StyleRule) -> ()) -> Style { var style = self style.states[state] = StyleRule(styleBlock) return style } public func state(_ state: String, styleRule styleBlock: (_ style: inout StyleRule) -> ()) -> Style { return self.state(.custom(state), styleRule: styleBlock) } public func state(_ state: StylePseudoState, _ attributes: [StyleRule.Key:StyleRule.Value]) -> Style { return self.state(.pseudoState(state), attributes: attributes) } public func state(_ state: String, _ attributes: [StyleRule.Key:StyleRule.Value]) -> Style { return self.state(.custom(state), attributes: attributes) } internal func state(_ state: StyleState, attributes: [StyleRule.Key:StyleRule.Value]) -> Style { var style = self style.states[state] = StyleRule(attributes: attributes) return style } internal subscript(state: StylePseudoState) -> StyleRule? { get { return self.states[.pseudoState(state)] } } internal subscript(state: String) -> StyleRule? { get { return self.states[.custom(state)] } } } ================================================ FILE: Gaikan/Style/StylePseudoState.swift ================================================ // // This file is part of Gaikan // // Created by JC on 14/10/15. // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code // import Foundation /** Used to define a special state of an element. As element can be in multiple states at the same time (like disabled and on), pseudo classes are defined as a UInt enum. The bigger the value, the higher priority it has **/ public enum StylePseudoState : UInt { case highlighted case disabled case selected case on } ================================================ FILE: Gaikan/Style/StyleRule+DirectAccess.swift ================================================ // // This file is part of Gaikan // // Created by JC on 17/06/16. // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code // import Foundation public extension StyleRule { public var background: BackgroundValue? { get { return self[.background].map { return $0 as! BackgroundValue } } set { self.attributes[.background] = newValue } } public var border: Border? { get { return self[.border].map { return $0 as! Border } } set { self.attributes[.border] = newValue } } public var clip: Bool { get { return self[.clip].map { return $0 as! Bool } ?? false } set { self.attributes[.clip] = newValue } } public var corners: Corners? { get { return self[.cornerRadius].map { return $0 as! Corners } } set { self.attributes[.cornerRadius] = newValue } } public var color: UIColor? { get { return self[.color].map { return $0 as! UIColor } } set { self.attributes[.color] = newValue } } public var font: UIFont? { get { return self[.font].map { return $0 as! UIFont } } set { self.attributes[.font] = newValue } } public var height: Constraint? { get { return self[.height].map { $0 as! Constraint } } set { self.attributes[.height] = newValue } } public var margin: UIEdgeInsets? { get { return self[.margin].map { return $0 as! UIEdgeInsets } } set { self.attributes[.margin] = newValue } } public var maxHeight: Constraint? { get { return self[.maxHeight].map { $0 as! Constraint } } set { self.attributes[.maxHeight] = newValue } } public var maxWidth: Constraint? { get { return self[.maxWidth].map { $0 as! Constraint } } set { self.attributes[.maxWidth] = newValue } } public var minHeight: Constraint? { get { return self[.minHeight].map { $0 as! Constraint } } set { self.attributes[.minHeight] = newValue } } public var minWidth: Constraint? { get { return self[.minWidth].map { $0 as! Constraint } } set { self.attributes[.minWidth] = newValue } } /// value must be between 0 and 100 /// default is 100 public var opacity: Double { get { return self[.opacity].map { return $0 as! Double } ?? 100 } set { self.attributes[.opacity] = newValue } } public var shadow: NSShadow? { get { return self[.shadow].map { return $0 as! NSShadow } } set { self.attributes[.shadow] = newValue } } public var textAlign: NSTextAlignment? { get { return self[.textAlign].map { return $0 as! NSTextAlignment } } set { self.attributes[.textAlign] = newValue } } public var textOverflow: NSLineBreakMode? { get { return self[.textOverflow].map { return $0 as! NSLineBreakMode } } set { self.attributes[.textOverflow] = newValue } } public var tintColor: UIColor? { get { return self[.tintColor].map { return $0 as! UIColor } } set { self.attributes[.tintColor] = newValue } } public var transform: CATransform3D { get { return self[.transform].map { return $0 as! CATransform3D } ?? CATransform3DIdentity } set { self.attributes[.transform] = newValue } } public var visible: Bool? { get { return self[.visible].map { return $0 as! Bool } } set { self.attributes[.visible] = newValue } } public var width: Constraint? { get { return self[.width].map { $0 as! Constraint } } set { self.attributes[.width] = newValue } } public var textShadow: NSShadow? { get { return self[.textShadow].map { return $0 as! NSShadow } } set { self.attributes[.textShadow] = newValue } } } ================================================ FILE: Gaikan/Style/StyleRule.swift ================================================ // // This file is part of Gaikan // // Created by JC on 30/08/15. // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code // import Foundation /** * Defines design properties with their values. */ public struct StyleRule : ExpressibleByDictionaryLiteral { public typealias Key = Property public typealias Value = Any? var attributes : Dictionary public init(attributes: [Key:Value]) { self.attributes = attributes } public init(dictionaryLiteral elements: (Key, Value)...) { var attributes = Dictionary() for (attributeName, attributeValue) in elements { attributes[attributeName] = attributeValue } self.attributes = attributes } public init(_ styleBlock: (_ style: inout StyleRule) -> ()) { self.init(attributes: [:]) styleBlock(&self) } public func extends(_ styles: StyleRule?...) -> StyleRule { var composedAttributes: [Key:Value] = [:] for style in styles { if let styleAttributes = style?.attributes { composedAttributes.gaikan_merge(styleAttributes) } } return StyleRule(attributes: composedAttributes.gaikan_merge(self.attributes)) } subscript(keyname: Property) -> Value { get { return self.attributes[keyname] != nil ? self.attributes[keyname]! : nil } } } extension StyleRule { public var textAttributes: [String:AnyObject] { let attributes: [String:AnyObject?] = [ NSForegroundColorAttributeName: self.color, NSFontAttributeName: self.font, NSShadowAttributeName: self.textShadow ] return attributes.trimmed() } } ================================================ FILE: Gaikan/Style/StyleState.swift ================================================ // // This file is part of Gaikan // // Created by JC on 10/12/15. // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code // import Foundation enum StyleState { case pseudoState(StylePseudoState) case custom(String) } extension StyleState { static func states(_ element: T) -> [StyleState] { var states: [StyleState] = [] if let control = element as? UIControl { if (control.isHighlighted) { states.append(.pseudoState(.highlighted)) } if (!control.isEnabled) { states.append(.pseudoState(.disabled)) } if (control.isSelected) { states.append(.pseudoState(.selected)) } if let switcher = control as? UISwitch { if (switcher.isOn) { states.append(.pseudoState(.on)) } } } if let customState = element.styleState { states.append(.custom(customState)) } return states } } extension StyleState : Hashable { var hashValue: Int { switch (self) { case .pseudoState(let state): return state.hashValue case .custom(let state): return state.hashValue } } } func ==(lhs: StyleState, rhs: StyleState) -> Bool { switch (lhs, rhs) { case (.pseudoState(let state1), .pseudoState(let state2)): return state1 == state2 case (.custom(let state1), .custom(let state2)): return state1 == state2 default: return false } } ================================================ FILE: Gaikan/Theme/Theme.swift ================================================ // // This file is part of Gaikan // // Created by JC on 14/10/15. // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code // import Foundation open class Theme : AnyObject { required public init() { } } ================================================ FILE: Gaikan/UIKit/Extension/UIControl.swift ================================================ // // This file is part of Gaikan // // Created by JC on 24/10/15. // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code // import Foundation public extension UIControl { public override func updateStyle() { super.updateStyle() guard let computedStyle = self.computedStyle else { return } ViewStyleRenderer.render(self, styleRule: computedStyle) } public override class func keyPathsAffectingStyle() -> [String] { return ["enabled", "selected", "highlighted"] } } ================================================ FILE: Gaikan/UIKit/Extension/UILabel.swift ================================================ // // This file is part of Gaikan // // Created by JC on 26/10/15. // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code // import Foundation public extension UILabel { public override func updateStyle() { super.updateStyle() guard let computedStyle = self.computedStyle else { return } ViewStyleRenderer.render(self, styleRule: computedStyle) } } ================================================ FILE: Gaikan/UIKit/Extension/UINavigationBar.swift ================================================ // // This file is part of Gaikan // // Created by JC on 26/10/15. // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code // import Foundation public extension UINavigationBar { fileprivate struct AssociatedKeys { static var VirtualView = "gaikan_virtualView" } public override func updateStyle() { super.updateStyle() if let computedStyle = self.computedStyle { ViewStyleRenderer.render(self, styleRule: computedStyle) } if let titleStyle = self.titleStyle.computedStyle { ViewStyleRenderer.render(self, titleStyle: titleStyle) } } public var titleStyle: VirtualView { guard let title = objc_getAssociatedObject(self, &AssociatedKeys.VirtualView) as? VirtualView else { let title = VirtualView(targetView: self) objc_setAssociatedObject(self, &AssociatedKeys.VirtualView, title as VirtualView?, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) return title } return title } } ================================================ FILE: Gaikan/UIKit/Extension/UISwitch.swift ================================================ // // This file is part of Gaikan // // Created by JC on 24/10/15. // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code // import Foundation public extension UIButton { public override func updateStyle() { super.updateStyle() guard let computedStyle = self.computedStyle else { return } ViewStyleRenderer.render(self, styleRule: computedStyle) } } ================================================ FILE: Gaikan/UIKit/Extension/UIView.swift ================================================ // // This file is part of Gaikan // // Created by JC on 30/08/15. // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code // import Foundation import KVOController var ViewStyleStateAttribute = "ViewStyleStateAttribute" var ViewStyleClassAttr = "ViewStyleClassAttr" var ViewStyleInlineAttr = "ViewStyleInlineAttr" extension UIView : Stylable { public var styleClass: Style? { get { let value = objc_getAssociatedObject(self, &ViewStyleClassAttr) as? AssociatedObject return value.map { $0.value } ?? nil } set { if (newValue == nil) { self.unregisterStyleKeyPaths() } objc_setAssociatedObject(self, &ViewStyleClassAttr, AssociatedObject(newValue), .OBJC_ASSOCIATION_RETAIN_NONATOMIC) self.addStyleLayerIfNeeded() self.computeStyle() if (newValue != nil) { self.registerStyleKeyPaths() } } } public var styleInline: StyleRule? { get { let value = objc_getAssociatedObject(self, &ViewStyleInlineAttr) as? AssociatedObject return value.map { $0.value } } set { objc_setAssociatedObject(self, &ViewStyleInlineAttr, newValue.map { AssociatedObject($0) }, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) self.addStyleLayerIfNeeded() self.computeStyle() } } public var styleState: String? { get { return objc_getAssociatedObject(self, &ViewStyleStateAttribute) as? String } set { objc_setAssociatedObject(self, &ViewStyleStateAttribute, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) self.computeStyle() } } public func updateStyle() { guard let computedStyle = self.computedStyle else { return } ViewStyleRenderer.render(self, styleRule: computedStyle) } public class func keyPathsAffectingStyle() -> [String] { return [] } } internal extension UIView { func registerStyleKeyPaths() { let keyPaths = type(of: self).keyPathsAffectingStyle() if keyPaths.count > 0 { self.kvoController.observe(self, keyPaths: keyPaths, options: .new) { [weak self] _ in self?.computeStyle() } } } func unregisterStyleKeyPaths() { type(of: self).keyPathsAffectingStyle().map { self.kvoController.unobserve(self, keyPath: $0) } } } var StyleKeyAttribute = "StyleLayer" extension UIView { var styleLayer: StyleLayer! { get { return objc_getAssociatedObject(self, &StyleKeyAttribute) as? StyleLayer } set { objc_setAssociatedObject(self, &StyleKeyAttribute, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } func addStyleLayerIfNeeded() { if let _ = self.styleLayer { return } let styleLayer = StyleLayer() self.styleLayer = styleLayer self.layer.insertSublayer(styleLayer, at: 0) styleLayer.frame = self.layer.bounds self.registerBounds() } func registerBounds() { self.kvoController.observe(self.layer, keyPath: "bounds", options: .new) { [weak self] _ in guard let weakSelf = self else { return } weakSelf.styleLayer?.frame = weakSelf.layer.bounds } } } ================================================ FILE: Gaikan/UIKit/Layer/BackgroundLayer.swift ================================================ // // BackgroundLayer.swift // Gaikan // // Created by pjechris on 01/12/16. // Copyright © 2016 fr.akane. All rights reserved. // import Foundation class BackgroundLayer : CALayer { var background: Background? { didSet { self.setNeedsDisplay() } } override func draw(in ctx: CGContext) { guard let background = self.background else { return } UIGraphicsPushContext(ctx) background.draw(in: ctx) UIGraphicsPopContext() } } ================================================ FILE: Gaikan/UIKit/Layer/BorderLayer.swift ================================================ // // BorderLayer.swift // Gaikan // // Created by pjechris on 30/11/16. // Copyright © 2016 fr.akane. All rights reserved. // import Foundation class BorderLayer : CALayer { var border: Border? { didSet { self.setNeedsDisplay() } } override func draw(in ctx: CGContext) { guard let border = self.border else { return } guard border.sides.keys.count != Side.all.count else { drawAllSides(for: border, in: ctx) return } drawSingleSides(for: border, in: ctx) } @inline(__always) func drawAllSides(for border: Border, in ctx: CGContext) { UIGraphicsPushContext(ctx) let topBorder = border.sides[.top]! // Any border here would suffice let path = UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius) topBorder.color.setStroke() path.lineWidth = CGFloat(topBorder.width) path.addClip() path.stroke() UIGraphicsPopContext() } private func drawSingleSides(for border: Border, in ctx: CGContext) { let path = UIBezierPath() UIGraphicsPushContext(ctx) for (side, border) in border.sides { switch(side) { case .top: self.draw(border: border, path: path, points: [ CGPoint(x: 0, y: 0), CGPoint(x: bounds.width, y: 0), CGPoint(x: bounds.width, y: border.width), CGPoint(x: 0, y: border.width) ]) case .right: self.draw(border: border, path: path, points: [CGPoint(x: bounds.width, y: 0), CGPoint(x: bounds.width, y: bounds.height), CGPoint(x: bounds.width - border.width, y: bounds.height), CGPoint(x: bounds.width - border.width, y: 0)]) case .bottom: self.draw(border: border, path: path, points: [CGPoint(x: bounds.width, y: bounds.height), CGPoint(x: 0, y: bounds.height), CGPoint(x: 0, y: bounds.height - border.width), CGPoint(x: bounds.width, y: bounds.height - border.width)]) case .left: self.draw(border: border, path: path, points: [CGPoint(x: 0, y: bounds.height), CGPoint(x: 0, y: 0), CGPoint(x: Int(border.width), y: 0), CGPoint(x: border.width, y: bounds.height)]) } } UIGraphicsPopContext() } func draw(border: BorderStyle, path: UIBezierPath, points: [CGPoint]) { guard let first = points.first else { return } path.move(to: first) for point in points { path.addLine(to: point) } path.close() border.color.setFill() path.fill() } } ================================================ FILE: Gaikan/UIKit/Layer/Layer.swift ================================================ // // Layer.swift // Gaikan // // Created by pjechris on 22/11/16. // Copyright © 2016 fr.akane. All rights reserved. // import Foundation import UIKit class StyleLayer : CALayer { let backgroundLayer = BackgroundLayer() let borderLayer = BorderLayer() var styleClass: StyleRule! { didSet { self.applyStyle() } } override var frame: CGRect { get { return super.frame } set { super.frame = newValue self.applyStyle() } } override init() { super.init() self.setupLayers() } override init(layer: Any) { super.init(layer: layer) self.setupLayers() } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) self.setupLayers() } private func setupLayers() { self.contentsScale = UIScreen.main.scale self.backgroundLayer.contentsScale = UIScreen.main.scale self.borderLayer.contentsScale = UIScreen.main.scale self.addSublayer(self.backgroundLayer) self.addSublayer(self.borderLayer) } override func layoutSublayers() { self.backgroundLayer.frame = self.frame self.borderLayer.frame = self.frame } func applyStyle() { guard let style = self.styleClass else { return } self.backgroundLayer.background = style.background.map { Background($0) } self.borderLayer.border = style.border } override var cornerRadius: CGFloat { didSet { self.backgroundLayer.cornerRadius = cornerRadius self.borderLayer.cornerRadius = cornerRadius } } } ================================================ FILE: Gaikan/UIKit/Renderer/ConstraintRenderer.swift ================================================ // // ConstraintRenderer.swift // Gaikan // // Created by pjechris on 30/08/16. // Copyright © 2016 fr.akane. All rights reserved. // import Foundation class ConstraintRenderer { class func render(_ view: UIView, styleRule: StyleRule) { self.addDimension(styleRule.height, to: view, attribute: .height, relation: .equal) self.addDimension(styleRule.maxHeight, to: view, attribute: .height, relation: .lessThanOrEqual) self.addDimension(styleRule.minHeight, to: view, attribute: .height, relation: .greaterThanOrEqual) self.addDimension(styleRule.width, to: view, attribute: .width, relation: .equal) self.addDimension(styleRule.maxWidth, to: view, attribute: .width, relation: .lessThanOrEqual) self.addDimension(styleRule.minWidth, to: view, attribute: .width, relation: .greaterThanOrEqual) } fileprivate class func addDimension(_ constraint: Constraint?, to view: UIView, attribute: NSLayoutAttribute, relation: NSLayoutRelation) { let identifier = "gaikan-\(relation.identifier())\(attribute.identifier().capitalized))" let index = view.dimensions.index { $0.identifier == identifier } var layoutConstraint: NSLayoutConstraint // Remove layoutConstraint if constraint nil but already active (from a previously pass) guard let constraint = constraint else { if let index = index { view.dimensions[index].isActive = false } return } if let index = index { layoutConstraint = view.dimensions[index] } else { layoutConstraint = NSLayoutConstraint(item: view, attribute: attribute, relatedBy: relation, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 0) layoutConstraint.identifier = identifier view.dimensions.append(layoutConstraint) } // Deactivate constraint if priority changing from/to required if (layoutConstraint.priority != constraint.priority) { if (constraint.priority == UILayoutPriorityRequired || layoutConstraint.priority == UILayoutPriorityRequired) { layoutConstraint.isActive = false } } layoutConstraint.constant = CGFloat(constraint.constant) layoutConstraint.priority = constraint.priority layoutConstraint.isActive = true } } ================================================ FILE: Gaikan/UIKit/Renderer/ViewStyleRenderer.swift ================================================ // // This file is part of Gaikan // // Created by JC on 30/08/15. // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code // import Foundation internal class ViewStyleRenderer { final class func render(_ stylable: UIView, styleRule: StyleRule) { let visible = styleRule.visible ?? true let corners = styleRule.corners ?? Corners(radius: 0) stylable.layer.cornerRadius = corners.radius stylable.clipsToBounds = styleRule.clip stylable.layer.masksToBounds = styleRule.clip stylable.layer.transform = styleRule.transform stylable.tintColor = styleRule.tintColor stylable.isHidden = !visible stylable.alpha = CGFloat(styleRule.opacity / 100) stylable.layoutMargins = styleRule.margin ?? stylable.layoutMargins if let shadow = styleRule.shadow { stylable.layer.shadowOpacity = 1 stylable.layer.shadowOffset = shadow.shadowOffset stylable.layer.shadowRadius = shadow.shadowBlurRadius stylable.layer.shadowColor = (shadow.shadowColor as? UIColor)?.cgColor } else { stylable.layer.shadowOpacity = 0 } stylable.styleLayer.styleClass = styleRule stylable.styleLayer.cornerRadius = corners.radius ConstraintRenderer.render(stylable, styleRule: styleRule) } final class func render(_ stylable: UILabel, styleRule: StyleRule) { stylable.font = styleRule.font stylable.textColor = styleRule.color stylable.lineBreakMode = styleRule.textOverflow ?? .byTruncatingTail } final class func render(_ button: UIButton, styleRule: StyleRule) { if let titleLabel = button.titleLabel { self.render(titleLabel, styleRule: styleRule) } button.setTitleColor(styleRule.color, for: UIControlState()) button.contentEdgeInsets = styleRule.margin ?? button.contentEdgeInsets } final class func render(_ textField: UITextField, styleRule: StyleRule) { textField.textAlignment = styleRule.textAlign ?? .natural } final class func render(_ navigationBar: UINavigationBar, titleStyle: StyleRule) { navigationBar.titleTextAttributes = titleStyle.textAttributes } } ================================================ FILE: Gaikan/UIKit/VirtualView.swift ================================================ // // VirtualView.swift // Gaikan // // Created by Simone Civetta on 17/03/16. // Copyright © 2016 fr.akane. All rights reserved. // import Foundation open class VirtualView : NSObject, Stylable { fileprivate let targetView: TargetView init(targetView: TargetView) { self.targetView = targetView } open var styleClass: Style? { didSet { self.computeStyle() } } open var styleInline: StyleRule? { didSet { self.computeStyle() } } open var styleState: String? { didSet { self.computeStyle() } } open func updateStyle() { self.targetView.updateStyle() } open static func keyPathsAffectingStyle() -> [String] { return [] } } ================================================ FILE: Gaikan.podspec ================================================ Pod::Spec.new do |s| s.name = "Gaikan" s.version = "0.9.1" s.source = { :git => "https://github.com/akane/Gaikan.git", :tag => s.version.to_s } s.summary = "Declarative view styling in Swift." s.description = "Powerful styling capabilities using a declarative DSL in Swift. Inspired by ReactJS and CSS modules." s.homepage = s.source[:git].to_s s.license = { :type => "MIT", :file => "LICENSE" } s.author = 'pjechris', 'akane' s.ios.deployment_target = "8.0" s.source_files = "Gaikan/**/*.{swift}" s.requires_arc = true s.dependency 'KVOController', '~> 1.x' end ================================================ FILE: Gaikan.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 46; objects = { /* Begin PBXBuildFile section */ 420190841C80B4BB00B70169 /* UINavigationBarSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 420190831C80B4BB00B70169 /* UINavigationBarSpec.swift */; }; 420190861C80B60F00B70169 /* UINavigationBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 420190851C80B60F00B70169 /* UINavigationBar.swift */; }; 42087B221C9AE4B8000606A7 /* VirtualView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42087B211C9AE4B8000606A7 /* VirtualView.swift */; }; 490DA08D1D1853FD00A79778 /* Dictionary+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 490DA08C1D1853FD00A79778 /* Dictionary+Helpers.swift */; }; 4912E9821BEB905A00C0DA4A /* Background.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4912E9811BEB905A00C0DA4A /* Background.swift */; }; 4912E9861BEB9A9800C0DA4A /* Gradient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4912E9851BEB9A9800C0DA4A /* Gradient.swift */; }; 4917A4C01D17EE1A008B5CB2 /* NSAttributedString+Style.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4917A4BF1D17EE1A008B5CB2 /* NSAttributedString+Style.swift */; }; 492C1B571BE0C9DF000515B9 /* UIControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 492C1B501BE0C9DF000515B9 /* UIControl.swift */; }; 492C1B591BE0C9DF000515B9 /* UISwitch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 492C1B521BE0C9DF000515B9 /* UISwitch.swift */; }; 492C1B5A1BE0C9DF000515B9 /* UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 492C1B531BE0C9DF000515B9 /* UIView.swift */; }; 492C1B5B1BE0C9DF000515B9 /* ViewStyleRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 492C1B561BE0C9DF000515B9 /* ViewStyleRenderer.swift */; }; 492C1B601BE0CA59000515B9 /* UILabelSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 492C1B5E1BE0CA59000515B9 /* UILabelSpec.swift */; }; 492C1B611BE0CA59000515B9 /* UIViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 492C1B5F1BE0CA59000515B9 /* UIViewSpec.swift */; }; 492C1B631BE0CAD0000515B9 /* UILabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 492C1B621BE0CAD0000515B9 /* UILabel.swift */; }; 4932871A1EFA7655009D1326 /* NSShadow+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 493287191EFA7655009D1326 /* NSShadow+Helpers.swift */; }; 493BB1DC1DF094FC004906C4 /* BackgroundLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 493BB1DB1DF094FC004906C4 /* BackgroundLayer.swift */; }; 493C1B921D7479E500861CEA /* Constraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 493C1B911D7479E500861CEA /* Constraint.swift */; }; 495DE8FC1BA580D100DA97BF /* DictionarySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 495DE8FB1BA580D100DA97BF /* DictionarySpec.swift */; }; 496D38A11CD8EDCB00C8B4EC /* UIColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 496D389A1CD8EDCB00C8B4EC /* UIColor.swift */; }; 496D38A21CD8EDCB00C8B4EC /* UIFont.swift in Sources */ = {isa = PBXBuildFile; fileRef = 496D389B1CD8EDCB00C8B4EC /* UIFont.swift */; }; 496D38A61CD8EE2800C8B4EC /* SampleTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 496D38A51CD8EE2800C8B4EC /* SampleTheme.swift */; }; 497032FB1D75E6410014E0C2 /* ConstraintRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 497032FA1D75E6410014E0C2 /* ConstraintRenderer.swift */; }; 4970A8171BD4459300BCB3BA /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4970A8161BD4459300BCB3BA /* Property.swift */; }; 4970A81A1BD445DB00BCB3BA /* Border.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4970A8191BD445DB00BCB3BA /* Border.swift */; }; 4970A81C1BD4484F00BCB3BA /* StyleSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4970A81B1BD4484F00BCB3BA /* StyleSpec.swift */; }; 497AD8981D19339A00FF5498 /* Optional+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 497AD8971D19339A00FF5498 /* Optional+Helpers.swift */; }; 498E05391B98362100B4E26B /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 498E05381B98362100B4E26B /* AppDelegate.swift */; }; 498E053B1B98362100B4E26B /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 498E053A1B98362100B4E26B /* ViewController.swift */; }; 498E053E1B98362100B4E26B /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 498E053C1B98362100B4E26B /* Main.storyboard */; }; 498E05401B98362100B4E26B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 498E053F1B98362100B4E26B /* Assets.xcassets */; }; 498E05431B98362100B4E26B /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 498E05411B98362100B4E26B /* LaunchScreen.storyboard */; }; 499081B31DE4EF78000F8855 /* Layer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 499081B21DE4EF78000F8855 /* Layer.swift */; }; 499943581D7497AB003C510E /* AssociatedObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 499943531D7497AB003C510E /* AssociatedObject.swift */; }; 499943601D7497FA003C510E /* CGPoint+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4999435D1D7497FA003C510E /* CGPoint+Helpers.swift */; }; 499943611D7497FA003C510E /* UIColor+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4999435E1D7497FA003C510E /* UIColor+Helpers.swift */; }; 499943621D7497FA003C510E /* UIImage+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4999435F1D7497FA003C510E /* UIImage+Helpers.swift */; }; 49A4F5581C1A3C7600D2A392 /* StylePseudoState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49A4F5571C1A3C7600D2A392 /* StylePseudoState.swift */; }; 49B9A23B1D14589200E4DE52 /* StyleRule+DirectAccess.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49B9A23A1D14589200E4DE52 /* StyleRule+DirectAccess.swift */; }; 49BE40B51D78565B00FE0818 /* NSLayoutAttribute+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49BE40B41D78565B00FE0818 /* NSLayoutAttribute+Helpers.swift */; }; 49BE40B71D78566400FE0818 /* NSLayoutRelation+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49BE40B61D78566400FE0818 /* NSLayoutRelation+Helpers.swift */; }; 49C25A8C1D845A6900E3735C /* ConstraintRendererSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49C25A8B1D845A6900E3735C /* ConstraintRendererSpec.swift */; }; 49C306551CD8FD2900CABDF4 /* Gradient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49C306541CD8FD2900CABDF4 /* Gradient.swift */; }; 49C306581CD902EC00CABDF4 /* UIView+InterfaceBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49C306571CD902EC00CABDF4 /* UIView+InterfaceBuilder.swift */; }; 49C8F7E91C1A33F400FF33DC /* StyleState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49C8F7E81C1A33F400FF33DC /* StyleState.swift */; }; 49CF3E291D0EF48200AF89E6 /* KVOController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 49CF3E281D0EF48200AF89E6 /* KVOController.framework */; }; 49CF3E2D1D0EF81B00AF89E6 /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 49CF3E2B1D0EF81B00AF89E6 /* Nimble.framework */; }; 49CF3E2E1D0EF81B00AF89E6 /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 49CF3E2C1D0EF81B00AF89E6 /* Quick.framework */; }; 49CF3E2F1D0EF93900AF89E6 /* KVOController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 49CF3E281D0EF48200AF89E6 /* KVOController.framework */; }; 49CF5F991BA4866B00216E6D /* StyleRuleSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49CF5F981BA4866B00216E6D /* StyleRuleSpec.swift */; }; 49D1BF831BDD4A8700348D3D /* CustomView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49D1BF821BDD4A8700348D3D /* CustomView.swift */; }; 49D389A51D84272800D72B2E /* UIView+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49D389A41D84272800D72B2E /* UIView+Helpers.swift */; }; 49DBD9021BCAAF110069B0C2 /* Style.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49DBD9011BCAAF110069B0C2 /* Style.swift */; }; 49E99D491DEF3A77003BF8B4 /* BorderLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49E99D481DEF3A77003BF8B4 /* BorderLayer.swift */; }; 49EC39681B92FC7D002938E1 /* Gaikan.h in Headers */ = {isa = PBXBuildFile; fileRef = 49EC39671B92FC7D002938E1 /* Gaikan.h */; settings = {ATTRIBUTES = (Public, ); }; }; 49EC396F1B92FC7D002938E1 /* Gaikan.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 49EC39641B92FC7D002938E1 /* Gaikan.framework */; }; 49EC39741B92FC7D002938E1 /* GaikanTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49EC39731B92FC7D002938E1 /* GaikanTests.swift */; }; 49EC398B1B93085E002938E1 /* StyleRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49EC398A1B93085E002938E1 /* StyleRule.swift */; }; 49EC39931B9373B2002938E1 /* Stylable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49EC39921B9373B2002938E1 /* Stylable.swift */; }; 49F78A671DF4245300866F72 /* Side.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49F78A661DF4245300866F72 /* Side.swift */; }; 49FD8A651CD16652004F2979 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49FD8A631CD16652004F2979 /* Theme.swift */; }; CAFCDA4F1C592BBC004513D3 /* Corners.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAFCDA4E1C592BBC004513D3 /* Corners.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ 498E05481B98362800B4E26B /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 49EC395B1B92FC7D002938E1 /* Project object */; proxyType = 1; remoteGlobalIDString = 49EC39631B92FC7D002938E1; remoteInfo = Gaikan; }; 49EC39701B92FC7D002938E1 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 49EC395B1B92FC7D002938E1 /* Project object */; proxyType = 1; remoteGlobalIDString = 49EC39631B92FC7D002938E1; remoteInfo = Gaikan; }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ 420190831C80B4BB00B70169 /* UINavigationBarSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UINavigationBarSpec.swift; sourceTree = ""; }; 420190851C80B60F00B70169 /* UINavigationBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UINavigationBar.swift; sourceTree = ""; }; 42087B211C9AE4B8000606A7 /* VirtualView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VirtualView.swift; sourceTree = ""; }; 490DA08C1D1853FD00A79778 /* Dictionary+Helpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Dictionary+Helpers.swift"; sourceTree = ""; }; 4912E9811BEB905A00C0DA4A /* Background.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Background.swift; sourceTree = ""; }; 4912E9851BEB9A9800C0DA4A /* Gradient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Gradient.swift; sourceTree = ""; }; 4917A4BF1D17EE1A008B5CB2 /* NSAttributedString+Style.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSAttributedString+Style.swift"; sourceTree = ""; }; 492C1B501BE0C9DF000515B9 /* UIControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIControl.swift; sourceTree = ""; }; 492C1B521BE0C9DF000515B9 /* UISwitch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UISwitch.swift; sourceTree = ""; }; 492C1B531BE0C9DF000515B9 /* UIView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIView.swift; sourceTree = ""; }; 492C1B561BE0C9DF000515B9 /* ViewStyleRenderer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewStyleRenderer.swift; sourceTree = ""; }; 492C1B5E1BE0CA59000515B9 /* UILabelSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UILabelSpec.swift; sourceTree = ""; }; 492C1B5F1BE0CA59000515B9 /* UIViewSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewSpec.swift; sourceTree = ""; }; 492C1B621BE0CAD0000515B9 /* UILabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UILabel.swift; sourceTree = ""; }; 493287191EFA7655009D1326 /* NSShadow+Helpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSShadow+Helpers.swift"; sourceTree = ""; }; 493BB1DB1DF094FC004906C4 /* BackgroundLayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BackgroundLayer.swift; sourceTree = ""; }; 493C1B911D7479E500861CEA /* Constraint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Constraint.swift; sourceTree = ""; }; 495DE8FB1BA580D100DA97BF /* DictionarySpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DictionarySpec.swift; sourceTree = ""; }; 496D389A1CD8EDCB00C8B4EC /* UIColor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIColor.swift; sourceTree = ""; }; 496D389B1CD8EDCB00C8B4EC /* UIFont.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIFont.swift; sourceTree = ""; }; 496D38A51CD8EE2800C8B4EC /* SampleTheme.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SampleTheme.swift; sourceTree = ""; }; 497032FA1D75E6410014E0C2 /* ConstraintRenderer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConstraintRenderer.swift; sourceTree = ""; }; 4970A8161BD4459300BCB3BA /* Property.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Property.swift; sourceTree = ""; }; 4970A8191BD445DB00BCB3BA /* Border.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Border.swift; sourceTree = ""; }; 4970A81B1BD4484F00BCB3BA /* StyleSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StyleSpec.swift; sourceTree = ""; }; 497AD8971D19339A00FF5498 /* Optional+Helpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Optional+Helpers.swift"; sourceTree = ""; }; 498E05361B98362100B4E26B /* Sample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Sample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 498E05381B98362100B4E26B /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 498E053A1B98362100B4E26B /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 498E053D1B98362100B4E26B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 498E053F1B98362100B4E26B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 498E05421B98362100B4E26B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 498E05441B98362100B4E26B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 499081B21DE4EF78000F8855 /* Layer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Layer.swift; sourceTree = ""; }; 499943531D7497AB003C510E /* AssociatedObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AssociatedObject.swift; sourceTree = ""; }; 4999435D1D7497FA003C510E /* CGPoint+Helpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CGPoint+Helpers.swift"; sourceTree = ""; }; 4999435E1D7497FA003C510E /* UIColor+Helpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIColor+Helpers.swift"; sourceTree = ""; }; 4999435F1D7497FA003C510E /* UIImage+Helpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImage+Helpers.swift"; sourceTree = ""; }; 49A4F5571C1A3C7600D2A392 /* StylePseudoState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StylePseudoState.swift; sourceTree = ""; }; 49B9A23A1D14589200E4DE52 /* StyleRule+DirectAccess.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "StyleRule+DirectAccess.swift"; sourceTree = ""; }; 49BE40B41D78565B00FE0818 /* NSLayoutAttribute+Helpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSLayoutAttribute+Helpers.swift"; sourceTree = ""; }; 49BE40B61D78566400FE0818 /* NSLayoutRelation+Helpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSLayoutRelation+Helpers.swift"; sourceTree = ""; }; 49C25A8B1D845A6900E3735C /* ConstraintRendererSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConstraintRendererSpec.swift; sourceTree = ""; }; 49C306541CD8FD2900CABDF4 /* Gradient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Gradient.swift; sourceTree = ""; }; 49C306571CD902EC00CABDF4 /* UIView+InterfaceBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+InterfaceBuilder.swift"; sourceTree = ""; }; 49C8F7E81C1A33F400FF33DC /* StyleState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StyleState.swift; sourceTree = ""; }; 49CF3E281D0EF48200AF89E6 /* KVOController.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = KVOController.framework; path = Carthage/Build/iOS/KVOController.framework; sourceTree = ""; }; 49CF3E2B1D0EF81B00AF89E6 /* Nimble.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Nimble.framework; path = Carthage/Build/iOS/Nimble.framework; sourceTree = ""; }; 49CF3E2C1D0EF81B00AF89E6 /* Quick.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Quick.framework; path = Carthage/Build/iOS/Quick.framework; sourceTree = ""; }; 49CF5F981BA4866B00216E6D /* StyleRuleSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StyleRuleSpec.swift; sourceTree = ""; }; 49D1BF821BDD4A8700348D3D /* CustomView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomView.swift; sourceTree = ""; }; 49D389A41D84272800D72B2E /* UIView+Helpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+Helpers.swift"; sourceTree = ""; }; 49DBD9011BCAAF110069B0C2 /* Style.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Style.swift; sourceTree = ""; }; 49E99D481DEF3A77003BF8B4 /* BorderLayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BorderLayer.swift; sourceTree = ""; }; 49EC39641B92FC7D002938E1 /* Gaikan.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Gaikan.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 49EC39671B92FC7D002938E1 /* Gaikan.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Gaikan.h; sourceTree = ""; }; 49EC39691B92FC7D002938E1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 49EC396E1B92FC7D002938E1 /* GaikanTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = GaikanTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 49EC39731B92FC7D002938E1 /* GaikanTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GaikanTests.swift; sourceTree = ""; }; 49EC39751B92FC7D002938E1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 49EC398A1B93085E002938E1 /* StyleRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StyleRule.swift; sourceTree = ""; }; 49EC39921B9373B2002938E1 /* Stylable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Stylable.swift; sourceTree = ""; }; 49F78A661DF4245300866F72 /* Side.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Side.swift; sourceTree = ""; }; 49FD8A631CD16652004F2979 /* Theme.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = ""; }; CAFCDA4E1C592BBC004513D3 /* Corners.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Corners.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 498E05331B98362100B4E26B /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 49CF3E291D0EF48200AF89E6 /* KVOController.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 49EC39601B92FC7D002938E1 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; 49EC396B1B92FC7D002938E1 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 49CF3E2F1D0EF93900AF89E6 /* KVOController.framework in Frameworks */, 49CF3E2D1D0EF81B00AF89E6 /* Nimble.framework in Frameworks */, 49CF3E2E1D0EF81B00AF89E6 /* Quick.framework in Frameworks */, 49EC396F1B92FC7D002938E1 /* Gaikan.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 490AD3DA1BA1CD4A0078F2DB /* Style */ = { isa = PBXGroup; children = ( 49CF5F981BA4866B00216E6D /* StyleRuleSpec.swift */, 4970A81B1BD4484F00BCB3BA /* StyleSpec.swift */, ); path = Style; sourceTree = ""; }; 4917A4BE1D17EE08008B5CB2 /* Helpers */ = { isa = PBXGroup; children = ( 493287191EFA7655009D1326 /* NSShadow+Helpers.swift */, 499943531D7497AB003C510E /* AssociatedObject.swift */, 4999435D1D7497FA003C510E /* CGPoint+Helpers.swift */, 490DA08C1D1853FD00A79778 /* Dictionary+Helpers.swift */, 4917A4BF1D17EE1A008B5CB2 /* NSAttributedString+Style.swift */, 49BE40B41D78565B00FE0818 /* NSLayoutAttribute+Helpers.swift */, 49BE40B61D78566400FE0818 /* NSLayoutRelation+Helpers.swift */, 497AD8971D19339A00FF5498 /* Optional+Helpers.swift */, 4999435E1D7497FA003C510E /* UIColor+Helpers.swift */, 4999435F1D7497FA003C510E /* UIImage+Helpers.swift */, 49D389A41D84272800D72B2E /* UIView+Helpers.swift */, ); path = Helpers; sourceTree = ""; }; 492C1B421BE0C81E000515B9 /* UIKit */ = { isa = PBXGroup; children = ( 499081B11DE4EF6F000F8855 /* Layer */, 492C1B4F1BE0C9DF000515B9 /* Extension */, 492C1B551BE0C9DF000515B9 /* Renderer */, 42087B211C9AE4B8000606A7 /* VirtualView.swift */, ); path = UIKit; sourceTree = ""; }; 492C1B4F1BE0C9DF000515B9 /* Extension */ = { isa = PBXGroup; children = ( 492C1B501BE0C9DF000515B9 /* UIControl.swift */, 492C1B621BE0CAD0000515B9 /* UILabel.swift */, 420190851C80B60F00B70169 /* UINavigationBar.swift */, 492C1B521BE0C9DF000515B9 /* UISwitch.swift */, 492C1B531BE0C9DF000515B9 /* UIView.swift */, ); path = Extension; sourceTree = ""; }; 492C1B551BE0C9DF000515B9 /* Renderer */ = { isa = PBXGroup; children = ( 492C1B561BE0C9DF000515B9 /* ViewStyleRenderer.swift */, 497032FA1D75E6410014E0C2 /* ConstraintRenderer.swift */, ); path = Renderer; sourceTree = ""; }; 492C1B5C1BE0CA59000515B9 /* UIKit */ = { isa = PBXGroup; children = ( 492C1B5D1BE0CA59000515B9 /* Extension */, 49C25A8A1D845A4F00E3735C /* Renderer */, ); path = UIKit; sourceTree = ""; }; 492C1B5D1BE0CA59000515B9 /* Extension */ = { isa = PBXGroup; children = ( 492C1B5E1BE0CA59000515B9 /* UILabelSpec.swift */, 420190831C80B4BB00B70169 /* UINavigationBarSpec.swift */, 492C1B5F1BE0CA59000515B9 /* UIViewSpec.swift */, ); path = Extension; sourceTree = ""; }; 495DE8F91BA580C200DA97BF /* Swift */ = { isa = PBXGroup; children = ( 495DE8FA1BA580C200DA97BF /* Extension */, ); path = Swift; sourceTree = ""; }; 495DE8FA1BA580C200DA97BF /* Extension */ = { isa = PBXGroup; children = ( 495DE8FB1BA580D100DA97BF /* DictionarySpec.swift */, ); path = Extension; sourceTree = ""; }; 496D38991CD8EDCB00C8B4EC /* Theme */ = { isa = PBXGroup; children = ( 496D389A1CD8EDCB00C8B4EC /* UIColor.swift */, 496D389B1CD8EDCB00C8B4EC /* UIFont.swift */, 496D38A51CD8EE2800C8B4EC /* SampleTheme.swift */, 49C306541CD8FD2900CABDF4 /* Gradient.swift */, ); path = Theme; sourceTree = ""; }; 4970A8151BD4455900BCB3BA /* Property */ = { isa = PBXGroup; children = ( 4912E9851BEB9A9800C0DA4A /* Gradient.swift */, 4970A8161BD4459300BCB3BA /* Property.swift */, 4970A8181BD445DB00BCB3BA /* Value */, ); path = Property; sourceTree = ""; }; 4970A8181BD445DB00BCB3BA /* Value */ = { isa = PBXGroup; children = ( 4970A8191BD445DB00BCB3BA /* Border.swift */, 4912E9811BEB905A00C0DA4A /* Background.swift */, CAFCDA4E1C592BBC004513D3 /* Corners.swift */, 493C1B911D7479E500861CEA /* Constraint.swift */, 49F78A661DF4245300866F72 /* Side.swift */, ); path = Value; sourceTree = ""; }; 498E05371B98362100B4E26B /* Sample */ = { isa = PBXGroup; children = ( 496D38991CD8EDCB00C8B4EC /* Theme */, 498E05381B98362100B4E26B /* AppDelegate.swift */, 498E053A1B98362100B4E26B /* ViewController.swift */, 498E053C1B98362100B4E26B /* Main.storyboard */, 498E053F1B98362100B4E26B /* Assets.xcassets */, 498E05411B98362100B4E26B /* LaunchScreen.storyboard */, 498E05441B98362100B4E26B /* Info.plist */, 49D1BF821BDD4A8700348D3D /* CustomView.swift */, ); path = Sample; sourceTree = ""; }; 499081B11DE4EF6F000F8855 /* Layer */ = { isa = PBXGroup; children = ( 499081B21DE4EF78000F8855 /* Layer.swift */, 49E99D481DEF3A77003BF8B4 /* BorderLayer.swift */, 493BB1DB1DF094FC004906C4 /* BackgroundLayer.swift */, ); path = Layer; sourceTree = ""; }; 49C25A8A1D845A4F00E3735C /* Renderer */ = { isa = PBXGroup; children = ( 49C25A8B1D845A6900E3735C /* ConstraintRendererSpec.swift */, ); path = Renderer; sourceTree = ""; }; 49C306561CD902D800CABDF4 /* InterfaceBuilder */ = { isa = PBXGroup; children = ( 49C306571CD902EC00CABDF4 /* UIView+InterfaceBuilder.swift */, ); path = InterfaceBuilder; sourceTree = ""; }; 49EC395A1B92FC7D002938E1 = { isa = PBXGroup; children = ( 49CF3E2B1D0EF81B00AF89E6 /* Nimble.framework */, 49CF3E2C1D0EF81B00AF89E6 /* Quick.framework */, 49CF3E281D0EF48200AF89E6 /* KVOController.framework */, 49EC39661B92FC7D002938E1 /* Gaikan */, 49EC39721B92FC7D002938E1 /* GaikanTests */, 498E05371B98362100B4E26B /* Sample */, 49EC39651B92FC7D002938E1 /* Products */, ); sourceTree = ""; }; 49EC39651B92FC7D002938E1 /* Products */ = { isa = PBXGroup; children = ( 49EC39641B92FC7D002938E1 /* Gaikan.framework */, 49EC396E1B92FC7D002938E1 /* GaikanTests.xctest */, 498E05361B98362100B4E26B /* Sample.app */, ); name = Products; sourceTree = ""; }; 49EC39661B92FC7D002938E1 /* Gaikan */ = { isa = PBXGroup; children = ( 49EC39671B92FC7D002938E1 /* Gaikan.h */, 49EC39691B92FC7D002938E1 /* Info.plist */, 4917A4BE1D17EE08008B5CB2 /* Helpers */, 49C306561CD902D800CABDF4 /* InterfaceBuilder */, 4970A8151BD4455900BCB3BA /* Property */, 49EC397E1B92FF2F002938E1 /* Style */, 49FD8A611CD16652004F2979 /* Theme */, 492C1B421BE0C81E000515B9 /* UIKit */, ); path = Gaikan; sourceTree = ""; }; 49EC39721B92FC7D002938E1 /* GaikanTests */ = { isa = PBXGroup; children = ( 49EC39731B92FC7D002938E1 /* GaikanTests.swift */, 49EC39751B92FC7D002938E1 /* Info.plist */, 490AD3DA1BA1CD4A0078F2DB /* Style */, 495DE8F91BA580C200DA97BF /* Swift */, 492C1B5C1BE0CA59000515B9 /* UIKit */, ); path = GaikanTests; sourceTree = ""; }; 49EC397E1B92FF2F002938E1 /* Style */ = { isa = PBXGroup; children = ( 49EC39921B9373B2002938E1 /* Stylable.swift */, 49DBD9011BCAAF110069B0C2 /* Style.swift */, 49A4F5571C1A3C7600D2A392 /* StylePseudoState.swift */, 49EC398A1B93085E002938E1 /* StyleRule.swift */, 49C8F7E81C1A33F400FF33DC /* StyleState.swift */, 49B9A23A1D14589200E4DE52 /* StyleRule+DirectAccess.swift */, ); path = Style; sourceTree = ""; }; 49FD8A611CD16652004F2979 /* Theme */ = { isa = PBXGroup; children = ( 49FD8A631CD16652004F2979 /* Theme.swift */, ); path = Theme; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ 49EC39611B92FC7D002938E1 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( 49EC39681B92FC7D002938E1 /* Gaikan.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ 498E05351B98362100B4E26B /* Sample */ = { isa = PBXNativeTarget; buildConfigurationList = 498E05451B98362100B4E26B /* Build configuration list for PBXNativeTarget "Sample" */; buildPhases = ( 498E05321B98362100B4E26B /* Sources */, 498E05331B98362100B4E26B /* Frameworks */, 498E05341B98362100B4E26B /* Resources */, 49CF3E271D0EF41A00AF89E6 /* Carthage */, ); buildRules = ( ); dependencies = ( 498E05491B98362800B4E26B /* PBXTargetDependency */, ); name = Sample; productName = Sample; productReference = 498E05361B98362100B4E26B /* Sample.app */; productType = "com.apple.product-type.application"; }; 49EC39631B92FC7D002938E1 /* Gaikan */ = { isa = PBXNativeTarget; buildConfigurationList = 49EC39781B92FC7D002938E1 /* Build configuration list for PBXNativeTarget "Gaikan" */; buildPhases = ( 49EC395F1B92FC7D002938E1 /* Sources */, 49EC39601B92FC7D002938E1 /* Frameworks */, 49EC39611B92FC7D002938E1 /* Headers */, 49EC39621B92FC7D002938E1 /* Resources */, ); buildRules = ( ); dependencies = ( ); name = Gaikan; productName = Gaikan; productReference = 49EC39641B92FC7D002938E1 /* Gaikan.framework */; productType = "com.apple.product-type.framework"; }; 49EC396D1B92FC7D002938E1 /* GaikanTests */ = { isa = PBXNativeTarget; buildConfigurationList = 49EC397B1B92FC7D002938E1 /* Build configuration list for PBXNativeTarget "GaikanTests" */; buildPhases = ( 49EC396A1B92FC7D002938E1 /* Sources */, 49EC396B1B92FC7D002938E1 /* Frameworks */, 49EC396C1B92FC7D002938E1 /* Resources */, 49CF3E2A1D0EF7E900AF89E6 /* Carthage */, ); buildRules = ( ); dependencies = ( 49EC39711B92FC7D002938E1 /* PBXTargetDependency */, ); name = GaikanTests; productName = GaikanTests; productReference = 49EC396E1B92FC7D002938E1 /* GaikanTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 49EC395B1B92FC7D002938E1 /* Project object */ = { isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0710; LastUpgradeCheck = 0810; ORGANIZATIONNAME = fr.akane; TargetAttributes = { 498E05351B98362100B4E26B = { CreatedOnToolsVersion = 7.0; LastSwiftMigration = 0800; }; 49EC39631B92FC7D002938E1 = { CreatedOnToolsVersion = 7.0; LastSwiftMigration = 0810; }; 49EC396D1B92FC7D002938E1 = { CreatedOnToolsVersion = 7.0; LastSwiftMigration = 0810; }; }; }; buildConfigurationList = 49EC395E1B92FC7D002938E1 /* Build configuration list for PBXProject "Gaikan" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 49EC395A1B92FC7D002938E1; productRefGroup = 49EC39651B92FC7D002938E1 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 49EC39631B92FC7D002938E1 /* Gaikan */, 49EC396D1B92FC7D002938E1 /* GaikanTests */, 498E05351B98362100B4E26B /* Sample */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 498E05341B98362100B4E26B /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 498E05431B98362100B4E26B /* LaunchScreen.storyboard in Resources */, 498E05401B98362100B4E26B /* Assets.xcassets in Resources */, 498E053E1B98362100B4E26B /* Main.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; 49EC39621B92FC7D002938E1 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; 49EC396C1B92FC7D002938E1 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ 49CF3E271D0EF41A00AF89E6 /* Carthage */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( "$(SRCROOT)/Carthage/Build/iOS/KVOController.framework", ); name = Carthage; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "/usr/local/bin/carthage copy-frameworks"; }; 49CF3E2A1D0EF7E900AF89E6 /* Carthage */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( "$(SRCROOT)/Carthage/Build/iOS/Nimble.framework", "$(SRCROOT)/Carthage/Build/iOS/Quick.framework", "$(SRCROOT)/Carthage/Build/iOS/KVOController.framework", ); name = Carthage; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "/usr/local/bin/carthage copy-frameworks"; }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 498E05321B98362100B4E26B /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 49D1BF831BDD4A8700348D3D /* CustomView.swift in Sources */, 496D38A61CD8EE2800C8B4EC /* SampleTheme.swift in Sources */, 496D38A21CD8EDCB00C8B4EC /* UIFont.swift in Sources */, 49C306551CD8FD2900CABDF4 /* Gradient.swift in Sources */, 498E053B1B98362100B4E26B /* ViewController.swift in Sources */, 498E05391B98362100B4E26B /* AppDelegate.swift in Sources */, 496D38A11CD8EDCB00C8B4EC /* UIColor.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 49EC395F1B92FC7D002938E1 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 499943601D7497FA003C510E /* CGPoint+Helpers.swift in Sources */, 4970A81A1BD445DB00BCB3BA /* Border.swift in Sources */, 4932871A1EFA7655009D1326 /* NSShadow+Helpers.swift in Sources */, 49F78A671DF4245300866F72 /* Side.swift in Sources */, 49EC398B1B93085E002938E1 /* StyleRule.swift in Sources */, 49C306581CD902EC00CABDF4 /* UIView+InterfaceBuilder.swift in Sources */, 49C8F7E91C1A33F400FF33DC /* StyleState.swift in Sources */, 49EC39931B9373B2002938E1 /* Stylable.swift in Sources */, 492C1B5B1BE0C9DF000515B9 /* ViewStyleRenderer.swift in Sources */, 49BE40B51D78565B00FE0818 /* NSLayoutAttribute+Helpers.swift in Sources */, 497032FB1D75E6410014E0C2 /* ConstraintRenderer.swift in Sources */, 493C1B921D7479E500861CEA /* Constraint.swift in Sources */, 490DA08D1D1853FD00A79778 /* Dictionary+Helpers.swift in Sources */, 492C1B631BE0CAD0000515B9 /* UILabel.swift in Sources */, 492C1B571BE0C9DF000515B9 /* UIControl.swift in Sources */, 493BB1DC1DF094FC004906C4 /* BackgroundLayer.swift in Sources */, 499081B31DE4EF78000F8855 /* Layer.swift in Sources */, 49B9A23B1D14589200E4DE52 /* StyleRule+DirectAccess.swift in Sources */, 49FD8A651CD16652004F2979 /* Theme.swift in Sources */, 49A4F5581C1A3C7600D2A392 /* StylePseudoState.swift in Sources */, 49E99D491DEF3A77003BF8B4 /* BorderLayer.swift in Sources */, 4917A4C01D17EE1A008B5CB2 /* NSAttributedString+Style.swift in Sources */, 499943581D7497AB003C510E /* AssociatedObject.swift in Sources */, 4970A8171BD4459300BCB3BA /* Property.swift in Sources */, 49BE40B71D78566400FE0818 /* NSLayoutRelation+Helpers.swift in Sources */, 4912E9821BEB905A00C0DA4A /* Background.swift in Sources */, 420190861C80B60F00B70169 /* UINavigationBar.swift in Sources */, 492C1B5A1BE0C9DF000515B9 /* UIView.swift in Sources */, 49DBD9021BCAAF110069B0C2 /* Style.swift in Sources */, 492C1B591BE0C9DF000515B9 /* UISwitch.swift in Sources */, 499943611D7497FA003C510E /* UIColor+Helpers.swift in Sources */, 497AD8981D19339A00FF5498 /* Optional+Helpers.swift in Sources */, 49D389A51D84272800D72B2E /* UIView+Helpers.swift in Sources */, 42087B221C9AE4B8000606A7 /* VirtualView.swift in Sources */, 499943621D7497FA003C510E /* UIImage+Helpers.swift in Sources */, 4912E9861BEB9A9800C0DA4A /* Gradient.swift in Sources */, CAFCDA4F1C592BBC004513D3 /* Corners.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 49EC396A1B92FC7D002938E1 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 49CF5F991BA4866B00216E6D /* StyleRuleSpec.swift in Sources */, 492C1B601BE0CA59000515B9 /* UILabelSpec.swift in Sources */, 495DE8FC1BA580D100DA97BF /* DictionarySpec.swift in Sources */, 492C1B611BE0CA59000515B9 /* UIViewSpec.swift in Sources */, 420190841C80B4BB00B70169 /* UINavigationBarSpec.swift in Sources */, 4970A81C1BD4484F00BCB3BA /* StyleSpec.swift in Sources */, 49C25A8C1D845A6900E3735C /* ConstraintRendererSpec.swift in Sources */, 49EC39741B92FC7D002938E1 /* GaikanTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ 498E05491B98362800B4E26B /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 49EC39631B92FC7D002938E1 /* Gaikan */; targetProxy = 498E05481B98362800B4E26B /* PBXContainerItemProxy */; }; 49EC39711B92FC7D002938E1 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 49EC39631B92FC7D002938E1 /* Gaikan */; targetProxy = 49EC39701B92FC7D002938E1 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ 498E053C1B98362100B4E26B /* Main.storyboard */ = { isa = PBXVariantGroup; children = ( 498E053D1B98362100B4E26B /* Base */, ); name = Main.storyboard; sourceTree = ""; }; 498E05411B98362100B4E26B /* LaunchScreen.storyboard */ = { isa = PBXVariantGroup; children = ( 498E05421B98362100B4E26B /* Base */, ); name = LaunchScreen.storyboard; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ 498E05461B98362100B4E26B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Carthage/Build/iOS", ); INFOPLIST_FILE = Sample/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = Akane.Sample; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 2.3; }; name = Debug; }; 498E05471B98362100B4E26B /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Carthage/Build/iOS", ); INFOPLIST_FILE = Sample/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = Akane.Sample; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 2.3; }; name = Release; }; 49EC39761B92FC7D002938E1 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/Carthage/Build/iOS"; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Debug; }; 49EC39771B92FC7D002938E1 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/Carthage/Build/iOS"; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Release; }; 49EC39791B92FC7D002938E1 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Gaikan/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = Akane.Gaikan; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 3.0; }; name = Debug; }; 49EC397A1B92FC7D002938E1 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Gaikan/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = Akane.Gaikan; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_VERSION = 3.0; }; name = Release; }; 49EC397C1B92FC7D002938E1 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Carthage/Build/iOS", ); INFOPLIST_FILE = GaikanTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = Akane.GaikanTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 3.0; }; name = Debug; }; 49EC397D1B92FC7D002938E1 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Carthage/Build/iOS", ); INFOPLIST_FILE = GaikanTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = Akane.GaikanTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 3.0; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 498E05451B98362100B4E26B /* Build configuration list for PBXNativeTarget "Sample" */ = { isa = XCConfigurationList; buildConfigurations = ( 498E05461B98362100B4E26B /* Debug */, 498E05471B98362100B4E26B /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 49EC395E1B92FC7D002938E1 /* Build configuration list for PBXProject "Gaikan" */ = { isa = XCConfigurationList; buildConfigurations = ( 49EC39761B92FC7D002938E1 /* Debug */, 49EC39771B92FC7D002938E1 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 49EC39781B92FC7D002938E1 /* Build configuration list for PBXNativeTarget "Gaikan" */ = { isa = XCConfigurationList; buildConfigurations = ( 49EC39791B92FC7D002938E1 /* Debug */, 49EC397A1B92FC7D002938E1 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 49EC397B1B92FC7D002938E1 /* Build configuration list for PBXNativeTarget "GaikanTests" */ = { isa = XCConfigurationList; buildConfigurations = ( 49EC397C1B92FC7D002938E1 /* Debug */, 49EC397D1B92FC7D002938E1 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 49EC395B1B92FC7D002938E1 /* Project object */; } ================================================ FILE: Gaikan.xcodeproj/project.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: Gaikan.xcodeproj/xcshareddata/xcschemes/Gaikan.xcscheme ================================================ ================================================ FILE: Gaikan.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: GaikanTests/GaikanTests.swift ================================================ // // GaikanTests.swift // GaikanTests // // Created by JC on 30/08/15. // Copyright © 2015 fr.akane. All rights reserved. // import XCTest @testable import Gaikan class GaikanTests: XCTestCase { override func setUp() { super.setUp() // Put setup code here. This method is called before the invocation of each test method in the class. } override func tearDown() { // Put teardown code here. This method is called after the invocation of each test method in the class. super.tearDown() } func testExample() { // This is an example of a functional test case. // Use XCTAssert and related functions to verify your tests produce the correct results. } func testPerformanceExample() { // This is an example of a performance test case. self.measure { // Put the code you want to measure the time of here. } } } ================================================ FILE: GaikanTests/Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType BNDL CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion 1 ================================================ FILE: GaikanTests/Style/StyleRuleSpec.swift ================================================ // // This file is part of Gaikan // // Created by JC on 12/09/15. // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code // import Foundation import Quick import Nimble import Gaikan class StyleRuleSpec: QuickSpec { override func spec() { var style: StyleRule! describe("init") { context("when passing block") { beforeEach { style = StyleRule() { (style: inout StyleRule) -> () in style.color = UIColor.blue } } it("should initialize color") { expect(style.color) == UIColor.blue } } } describe("extends") { beforeEach { style = [ .color: UIColor.blue, .tintColor: nil ] } context("when extending properties") { it("it should take new value") { let newStyle: StyleRule = [ .color: UIColor.black ] expect(newStyle.extends(style).color) == UIColor.black } } context("when adding properties") { it("should be added to style") { let font = UIFont.systemFont(ofSize:22) let newStyle: StyleRule = [ .font: font ] expect(newStyle.extends(style).font) == font } } context("when nullyfing a property") { it("it should be nil") { var newStyle: StyleRule = [ .color: nil ] newStyle.extends(style) expect(newStyle.color).to(beNil()) } } context("multiple styles") { context("with a nil style") { it("should merge valid ones") { var newStyle: StyleRule = [:] newStyle = newStyle.extends(nil, style) expect(newStyle.color) == UIColor.blue } it("should add all properties") { var newStyle: StyleRule = [:] newStyle = newStyle.extends(style, [ .tintColor: UIColor.green ]) expect(newStyle.color) == UIColor.blue expect(newStyle.tintColor) == UIColor.green } } } } } } ================================================ FILE: GaikanTests/Style/StyleSpec.swift ================================================ // // This file is part of Gaikan // // Created by JC on 18/10/15. // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code // import Foundation import Quick import Nimble @testable import Gaikan class StyleSpec: QuickSpec { override func spec() { var style: Style! beforeEach { style = Style() { (style: inout StyleRule) in style.color = UIColor.blue } } describe("state") { context("when pseudo class does not exist yet") { it("should be nil") { expect(style[.highlighted]).to(beNil()) } it("should add it") { style = style.state(.highlighted) { (style: inout StyleRule) -> () in } expect(style[.highlighted]).toNot(beNil()) } it("should not be merged with .Normal") { style = style.state(.highlighted) { (style: inout StyleRule) -> () in } expect(style[.highlighted]?.color).to(beNil()) } } context("when pseudo class exist") { beforeEach { style = style.state(.highlighted) { (style: inout StyleRule) -> () in style.color = .green } } it("should replace it") { style = style.state(.highlighted) { (style: inout StyleRule) -> () in } expect(style[.highlighted]?.color).to(beNil()) } } } } } ================================================ FILE: GaikanTests/Swift/Extension/DictionarySpec.swift ================================================ // // This file is part of Gaikan // // Created by JC on 12/09/15. // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code // import Foundation import Quick import Nimble @testable import Gaikan class DictionarySpec: QuickSpec { override func spec() { describe("merge") { var dictionary1: [String:String?]! context("when value is replaced") { context("with nil") { beforeEach { let dictionary2: [String:String?] = [ "hello": nil ] dictionary1 = [ "foo": "bar", "hello": "world" ] dictionary1.gaikan_merge(dictionary2) } it("should exist") { expect(dictionary1["hello"]).toNot(beNil()) } it("should CONTAIN nil") { expect(dictionary1["hello"]!).to(beNil()) } } } context("when value is added") { context("with nil") { beforeEach { let dictionary2: [String:String?] = [ "hello": nil ] dictionary1 = [ "foo": "bar" ] dictionary1.gaikan_merge(dictionary2) } it("should exist") { expect(dictionary1["hello"]).toNot(beNil()) } it("should CONTAIN nil") { expect(dictionary1["hello"]!).to(beNil()) } } } } } } ================================================ FILE: GaikanTests/UIKit/Extension/UILabelSpec.swift ================================================ // // This file is part of Gaikan // // Created by JC on 11/09/15. // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code // import Foundation import Quick import Nimble import Gaikan class UILabelSpec: QuickSpec { override func spec() { var label: UILabel! beforeEach { label = UILabel() } describe("styleInline") { var style: StyleRule! context("when giving style") { it("should inherit view styles") { style = [ .tintColor: UIColor.blue ] label.styleInline = style expect(label.tintColor) == UIColor.blue } it("should apply color") { style = [ .color: UIColor.red ] label.styleInline = style expect(label.textColor) == UIColor.red } } } } } ================================================ FILE: GaikanTests/UIKit/Extension/UINavigationBarSpec.swift ================================================ // // This file is part of Gaikan // // Created by Simone Civetta on 26/02/16. // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code // import Foundation import Quick import Nimble import Gaikan class UINavigationBarSpec: QuickSpec { override func spec() { var navigationBar: UINavigationBar! beforeEach { navigationBar = UINavigationBar() } describe("styleInline") { var style: StyleRule! context("when giving style") { it("should inherit view styles") { style = [ .tintColor: UIColor.blue ] navigationBar.styleInline = style expect(navigationBar.tintColor) == UIColor.blue } it("should apply a title font") { style = [ .font: UIFont.systemFont(ofSize: 42.0) ] navigationBar.titleStyle.styleInline = style expect(navigationBar.titleTextAttributes![NSFontAttributeName] as? UIFont) == UIFont.systemFont(ofSize: 42.0) } it("should apply a foreground color to the title") { style = [ .color: UIColor.blue ] navigationBar.titleStyle.styleInline = style expect(navigationBar.titleTextAttributes![NSForegroundColorAttributeName] as? UIColor) == UIColor.blue } it("should apply a shadow to the title") { let shadow = NSShadow() shadow.shadowOffset = CGSize(width: 2, height: 3) style = [ .textShadow: shadow ] navigationBar.titleStyle.styleInline = style expect(navigationBar.titleTextAttributes![NSShadowAttributeName] as? NSShadow) == shadow } } } } } ================================================ FILE: GaikanTests/UIKit/Extension/UIViewSpec.swift ================================================ // // This file is part of Gaikan // // Created by JC on 10/09/15. // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code // import Foundation import Quick import Nimble @testable import Gaikan class UIViewSpec: QuickSpec { override func spec() { var view: UIView! beforeEach { view = UIView() } describe("styleInline") { var style: StyleRule! context("when giving style") { it("should apply tintColor") { style = [ .tintColor: UIColor.red ] view.styleInline = style expect(view.tintColor) == UIColor.red } it("should apply border") { let border = Border(all: (width: 3, color: UIColor.black)) style = [ .border: border] view.styleInline = style expect(view.styleLayer.borderLayer.border) == border } it("should apply visible") { style = [ .visible: false ] view.styleInline = style expect(view.isHidden) == true } } } describe("styleState") { var style: Style! beforeEach { style = Style() { (styleRule:inout StyleRule) in styleRule.tintColor = UIColor.red } .state("newState", [.tintColor: UIColor.blue]) } context("when setting") { beforeEach { view.styleClass = style view.styleState = "newState" } it("should apply state tintColor") { expect(view.tintColor) == UIColor.blue } } } } } ================================================ FILE: GaikanTests/UIKit/Renderer/ConstraintRendererSpec.swift ================================================ // // ConstraintRendererSpec.swift // Gaikan // // Created by pjechris on 10/09/16. // Copyright © 2016 fr.akane. All rights reserved. // import Foundation import Nimble import Quick @testable import Gaikan class ConstraintRendererSpec : QuickSpec { override func spec() { var view: UIView! beforeEach { view = UIView() } describe("render") { var style: StyleRule! context("style") { beforeEach { style = StyleRule() style.width = 42 ConstraintRenderer.render(view, styleRule: style) } it("adds constraint") { expect(view.constraints.count) == 1 } context("changed") { var newStyle = StyleRule() beforeEach { newStyle.width = 32 ConstraintRenderer.render(view, styleRule: newStyle) } it("keeps constraint") { expect(view.constraints.count) == 1 } it("applies changed constraint") { expect(view.constraints.first?.constant) == CGFloat(newStyle.width!.constant) } } context("removed") { beforeEach { ConstraintRenderer.render(view, styleRule: StyleRule()) } it("removes constraint") { expect(view.constraints.count) == 0 } } } context("removed constraint") { beforeEach { style = StyleRule() style.width = 50 } } } } } ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2015 akane Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: Podfile ================================================ platform :ios, '9.0' target 'Gaikan' do use_frameworks! pod 'KVOController' target 'GaikanTests' do inherit! :search_paths pod 'Nimble' pod 'Quick' end end ================================================ FILE: README.md ================================================

Gaikan

[![CocoaPods Compatible](https://img.shields.io/cocoapods/v/Gaikan.svg)](https://img.shields.io/cocoapods/v/Gaikan.svg) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) [![Build Status](https://travis-ci.org/akane/Gaikan.svg?branch=travis)](https://travis-ci.org/akane/Gaikan) Gaikan gives you powerful styling capabilities using a declarative DSL in Swift. _Inspired by_ React: CSS in JS and CSS modules. To style UIView(s) just give them a ```StyleRule``` object: ```Swift let myLabelStyle = StyleRule() { (inout style: StyleRule) -> () in style.color = UIColor.redColor() style.border = Border(width: 1, color: UIColor.greenColor()) style.font = UIFont(name: "Courier", size: 24) } self.label.styleInline = myLabelStyle ``` Check out the sample to see how to well integrate Gaikan into a project. ## Features * Apply a simple style using ```styleInline```.... * ... or make theming using ```styleClass``` (see below for more information). * You can share styles using ```extends``` method to avoid repeating yourself. * You can style depending on your control state (enabled, highlighted, ...). * You can style `NSAttributedString`! ## Properties Check out our page on [properties](Doc/Properties.md) to find which one to use to style your views. Starting with 0.4, we also added `VirtualView`. This allow you to apply style effects on some non `UIView` attributes/objects: | NSObject | Virtual view | Description | Available since | ------------------|---------------|------------------------|------------------ | UINavigationBar | titleStyle | Sets title navigation bar style | 0.4 ## Advanced usage ### NSAttributedString Just call `NSAttributedString(string:"Hello", style: yourStyle)` to get a styled `NSAttributedString`. Alternatively you can use `style.textAttributes` to transform `StyleRule` into any `NSAttributedString` compatible dictionary. ### Theme ```Theme``` packages together multiple ```Style``` definitions to make a whole set. ```Swift public class SampleTheme : Theme { /// Non static is important if you want to reference them from InterfaceBuilder lazy let logo = Style(...) lazy let title = Style(...) } class CustomView: UIView { typealias ThemeType = RailTheme @IBOutlet weak var title: UILabel! @IBOutlet weak var footnote: UILabel! @IBOutlet weak var logo: UIImageView! @IBOutlet weak var button: UIButton! override func awakeFromNib() { self.title.styleClass = SampleTheme().logo self.logo.styleClass = SampleTheme().title } } ``` You can also apply themes right from InterfaceBuilder: ### Extends You can extend your styles to reuse common features: ```Swift func primary() -> Style { return [ .Color: UIColor.greenColor() ] } func large() -> Style { return [ .Font: UIFont.systemFontOfSize(18) ] } func merged() -> Style { return Style().extends(primary(), large()) // color: green, font: 18 } ``` ### States You can define styles for states. They'll extend from the default state : ```Swift func style() -> Style { return Style() { (inout style: StyleRule) -> Void in style.tintColor = UIColor.whiteColor() } .state(.Selected, attributes: [ .TintColor: UIColor.grayColor().colorWithAlphaComponent(0.6) ] } ``` Don't hesitate to take a look at the sample to better understand how it works. ## Contributing This project was first developed by [Xebia IT Architects](http://xebia.fr) in Paris, France. We will continue working and investing on it. We encourage the community to contribute to the project by opening tickets and/or pull requests. Here a some of the subjects we are interested in: - Add UIStackView attributes (distribution, spacing, ...). - Support for multiple layout engines (AutoLayout, LayoutKit, Flexbox, ...) - Allow to define 1+ borders and corner radius. Currently defining border and radius set the 4 of them (top, bottom, left, right). - Better support for IBDesignable (if possible). - Add debugging information (style name, inheritance, ...). - Better integration with NSAttributedString. - Support for traits. - Anything you have in mind! ## License Gaikan is released under the MIT License. Please see the LICENSE file for details. ================================================ FILE: Sample/AppDelegate.swift ================================================ // // This file is part of Gaikan // // Created by JC on 03/09/15. // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code // import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. return true } func applicationWillResignActive(_ application: UIApplication) { // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. } func applicationDidEnterBackground(_ application: UIApplication) { // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. } func applicationWillEnterForeground(_ application: UIApplication) { // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. } func applicationDidBecomeActive(_ application: UIApplication) { // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. } func applicationWillTerminate(_ application: UIApplication) { // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. } } ================================================ FILE: Sample/Assets.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "idiom" : "iphone", "size" : "29x29", "scale" : "2x" }, { "idiom" : "iphone", "size" : "29x29", "scale" : "3x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "2x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "3x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "2x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: Sample/Assets.xcassets/Contents.json ================================================ { "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: Sample/Assets.xcassets/background.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "scale" : "1x" }, { "idiom" : "universal", "filename" : "bg.jpg", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: Sample/Assets.xcassets/logo.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "scale" : "1x" }, { "idiom" : "universal", "filename" : "logo.png", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" }, "properties" : { "template-rendering-intent" : "template" } } ================================================ FILE: Sample/Base.lproj/LaunchScreen.storyboard ================================================ ================================================ FILE: Sample/Base.lproj/Main.storyboard ================================================ ================================================ FILE: Sample/CustomView.swift ================================================ // // This file is part of Gaikan // // Created by JC on 16/11/15. // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code // import Foundation import Gaikan class CustomView : UIView { @IBOutlet weak var title: UILabel! @IBOutlet weak var footnote: UILabel! @IBOutlet weak var logo: UIImageView! @IBOutlet weak var button: UIButton! @IBAction func gotIt() { self.button.setTitle("Great!", for: .normal) self.button.styleClass = SampleTheme().gotIt } } ================================================ FILE: Sample/Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType APPL CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion 1 LSRequiresIPhoneOS UILaunchStoryboardName LaunchScreen UIMainStoryboardFile Main UIRequiredDeviceCapabilities armv7 UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight ================================================ FILE: Sample/Theme/Gradient.swift ================================================ // // This file is part of Gaikan // // Created by JC on 09/05/16. // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code // import Foundation import Gaikan extension Gradient { static func orangeGradient() -> Gradient { return Gradient( (color: UIColor(red: 251/255, green: 208/255, blue: 176/255, alpha: 0.5), position: 0), (color: UIColor(red: 137/255, green: 106/255, blue: 111/255, alpha: 0.5), position: 100) ) } } ================================================ FILE: Sample/Theme/SampleTheme.swift ================================================ // // This file is part of Gaikan // // Created by JC on 09/05/16. // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code // import Foundation import Gaikan class SampleTheme : Theme { var home = Style() { (style: inout StyleRule) -> () in style.background = Background(UIImage(named: "background")!, Gradient.orangeGradient()) } var logo = Style() { (style: inout StyleRule) -> () in style.tintColor = UIColor.logoColor() style.width = 90 style.height = style.width } var title: Style = [ .color: UIColor.white, .font: UIFont.titleFont() ] var footnote = Style() { (style: inout StyleRule) -> () in style.font = UIFont.footnoteFont() style.color = UIColor.orangeRail() } var getIt: Style = [ .border: Border(all: (width: 1, color: UIColor.white)), .color: UIColor.white ] var gotIt: Style = [ .color: UIColor.orange ] } ================================================ FILE: Sample/Theme/UIColor.swift ================================================ // // This file is part of Gaikan // // Created by JC on 09/05/16. // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code // import Foundation import UIKit extension UIColor { static func orangeRail() -> UIColor { return UIColor(red: 251/255, green: 208/255, blue: 176/255, alpha: 1) } class func logoColor() -> UIColor { return UIColor.white } } ================================================ FILE: Sample/Theme/UIFont.swift ================================================ // // This file is part of Gaikan // // Created by JC on 09/05/16. // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code // import Foundation import UIKit extension UIFont { class func bodyFont() -> UIFont? { return UIFont(name: "Avenir", size: 12) } class func titleFont() -> UIFont? { return UIFont(name: "AvenirNext-Bold", size: 38) } class func footnoteFont() -> UIFont? { return UIFont(name: "AvenirNext-Italic", size: 18) } } ================================================ FILE: Sample/ViewController.swift ================================================ // // This file is part of Gaikan // // Created by JC on 03/09/15. // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code // import UIKit import Gaikan class ViewController: UIViewController { var customView: CustomView! override func viewDidLoad() { let titleStyle: StyleRule = [ .font: UIFont(name: "AvenirNext-Italic", size: 16), .color: UIColor.purple ] super.viewDidLoad() self.customView = self.view as! CustomView self.navigationController?.navigationBar.titleStyle.styleInline = titleStyle } }