LazyKit 2.0

LazyKit 2.0

测试已测试
语言语言 SwiftSwift
许可 MIT
发布上次发布2017年1月
SwiftSwift版本3.0
SPM支持SPM

Kevin Malkic维护。



  • Kevin Malkic

LazyKit

LazyKit是一个框架,允许您编写快速且易于使用的视图。
构造视图可能很费时、无聊且重复,特别是在构建了n个视图之后。

现在您可以使用基本的CSS文件来设置元素样式。

特性

  • 映射UIView / UILabel / UIButton / UIImageView / UITextField / UITextView / UITableView / UICollectionView
  • UIViewController / UIView / UITableViewCell / UICollectionViewCell的基础类
  • CSS解析器/映射器(您将变得更加懒惰)
  • 在运行时更换CSS主题
  • 支持CSS中的边框/半径

即将推出的特性

  • CSS中支持文本装饰

要求

  • iOS 8.0+
  • Xcode 7.2+

安装

内嵌框架需要iOS 8的最小部署目标。

内嵌框架

  • 打开终端,使用cd进入您项目的顶层目录,然后运行以下命令“如果”您的项目尚未初始化为Git仓库
$ git init
  • 通过运行以下命令将LazyKit作为Git 子模块添加:
$ git submodule add https://github.com/kmalkic/LazyKit.git
  • 打开新的LazyKit文件夹,然后将LazyKit.xcodeproj拖放到应用程序的Xcode项目的Project Navigator中。

    它应该出现在应用程序蓝色项目图标之下。它是否在所有其他Xcode组之上或之下并不重要。

  • 在Project Navigator中选择LazyKit.xcodeproj并验证部署目标是否与应用程序目标匹配。

  • 接下来,在Project Navigator中选择您应用程序项目(蓝色项目图标)以进行目标配置窗口的导航,并在侧边栏的“Targets”下选择应用程序目标。
  • 在那个窗口的顶部工具栏中,打开“General”面板。
  • 在“Embedded Binaries”部分下单击+按钮。
  • 您将看到两个不同的 LazyKit.xcodeproj 文件夹,每个文件夹中包含两个不同版本的 LazyKit.framework,它们嵌套在 Products 文件夹中。

    您选择哪个 Products 文件夹都无关紧要,但是选择最上面或最下面的 LazyKit.framework 是有关系的。

  • 这样就可以了!

LazyKit.framework 会自动添加为目标依赖、链接框架和嵌入框架,这所有的要求都满足,您就可以在模拟器和设备上构建了。


使用方法

创建简单的视图配置

可以是一个结构体或类。简单加入一个标签。

import LazyKit

struct MyConfigurations: LazyViewConfigurations {

    static func elementsOptions() -> [ElementOptions]? {
        return [
            LabelOptions(identifier: "title",
                classType: CustomLabel.self,
                viewBaseOptions: ViewBaseOptions(backgroundColor: .blueColor()),
                textOptions: TextBaseOptions(text: "hello", font: .systemFontOfSize(20), textAlignment: .Center)),
        ]
    }
    //Will need some knowledges in visual format constraints
    static func visualFormatConstraintOptions() -> [VisualFormatConstraintOptions]? {
        return [
            VisualFormatConstraintOptions(string: "H:|-[title]-|"),
            VisualFormatConstraintOptions(string: "V:|-top-[title(==titleHeight)]")
        ]
    }

    static func visualFormatMetrics() -> [String: AnyObject]? {

        return ["top" : 30, "titleHeight" : 44]
    }
     //Can use this function to return the conventional way of using constraints
    static func layoutConstraints() -> [ConstraintOptions]? {
        return nil
    }
}

使用 UIViewControllers

就是这些了。

import LazyKit

class MyViewController: LazyBaseViewController <MyConfigurations> {

}

使用 UIView

就是这些了。不要同时使用 LazyBaseViewController 和具有 LazyBaseView 类型的视图。首先,这样做没有意义,其次,您最终会得到重复的视图。

import LazyKit

class MyViewController: UIViewController {

    override func loadView() {

        view = MyView()
    }
}

class MyView: LazyBaseView <MyConfigurations> {

}

不使用 CSS 创建高级视图配置

import LazyKit

struct MyConfigurations: LazyViewConfigurations {

    static func elementsOptions() -> [ElementOptions]? {

        return [
            LabelOptions(identifier: "title",
                classType: CustomLabel.self,
                viewBaseOptions: ViewBaseOptions(backgroundColor: .blueColor()),
                textOptions: TextBaseOptions(text: "hello", font: .systemFontOfSize(20), textAlignment: .Center)
            ),

            LabelOptions(identifier: "subtitle",
                viewBaseOptions: ViewBaseOptions(backgroundColor: .greenColor()),
                textOptions: TextBaseOptions(text: "hey", textAlignment: .Center)
            ),

            ButtonOptions(identifier: "button",
                viewBaseOptions: ViewBaseOptions(backgroundColor: .redColor()),
                textOptionsForType: [.Normal: TextBaseOptions(text: "button"), .Highlighted: TextBaseOptions(text: "highlighted")]
            ),

            ViewOptions(identifier: "line",
                viewBaseOptions: ViewBaseOptions(backgroundColor: .lightGrayColor())
            ),

            ImageOptions(identifier: "photo",
                viewBaseOptions: ViewBaseOptions(backgroundColor: .lightGrayColor()),
                imageBaseOptions: ImageBaseOptions(imageNamed: "image", contentMode: .ScaleAspectFill)
            ),

            TextFieldOptions(identifier: "textfield",
                viewBaseOptions: ViewBaseOptions(backgroundColor: UIColor.orangeColor()),
                textOptions: TextBaseOptions(font: .systemFontOfSize(16), textAlignment: .Center),
                placeholderOptions: TextBaseOptions(text: "placeholder", font: .systemFontOfSize(16), textColor: .redColor(), textAlignment: .Center),
                textInputOptions: TextInputBaseOptions(autocapitalizationType: .Sentences, autocorrectionType: .No, spellCheckingType: .No, keyboardType: .NumbersAndPunctuation, keyboardAppearance: .Dark, returnKeyType: .Done, enablesReturnKeyAutomatically: true, secureTextEntry: false)
            ),

            TextViewOptions(identifier: "textview",
                viewBaseOptions: ViewBaseOptions(backgroundColor: UIColor.cyanColor()),
                textOptions: TextBaseOptions(text: "TextView", font: .systemFontOfSize(14), textAlignment: .Left),
                textInputOptions: TextInputBaseOptions(autocapitalizationType: .Sentences, autocorrectionType: .No, spellCheckingType: .No, keyboardType: .EmailAddress, keyboardAppearance: .Dark, returnKeyType: .Done, enablesReturnKeyAutomatically: true, secureTextEntry: false)
            )
        ]
    }

    static func visualFormatConstraintOptions() -> [VisualFormatConstraintOptions]? {

        return [
            VisualFormatConstraintOptions(string: "H:|-[photo(==photoW)]-[title]-|"),
            VisualFormatConstraintOptions(string: "H:[subtitle(==title)]"),
            VisualFormatConstraintOptions(string: "H:[textfield(==title)]"),
            VisualFormatConstraintOptions(string: "H:|-40-[line]-40-|"),
            VisualFormatConstraintOptions(string: "H:|-[textview]-|"),
            VisualFormatConstraintOptions(string: "H:|-buttonLeft-[button]-buttonRight-|"),
            VisualFormatConstraintOptions(string: "V:|-top-[title]-[subtitle]-[textfield]", options: .AlignAllLeft),
            VisualFormatConstraintOptions(string: "V:|-top-[photo(==photoH)]"),
            VisualFormatConstraintOptions(string: "V:[line(==1)]-[textview(==200)]-200-[button(==buttonH)]-8-|")
        ]
    }

    static func visualFormatMetrics() -> [String: AnyObject]? {

        return ["top" : 30, "buttonH" : 44, "buttonLeft" : 100, "buttonRight" : 100, "photoW" : 100, "photoH" : 60]
    }

    static func layoutConstraints() -> [ConstraintOptions]? {

        return [
            ConstraintOptions(identifier: "titleHeight", itemIdentifier: "title", attribute: .Height, relatedBy: .Equal, toItemIdentifier: nil, attribute: .Height, multiplier: 1, constant: 40)
        ]
    }
}

这里的结果无需额外工作 :),但颜色很丑陋。

Advanced view configurations

使用 CSS

CSS 示例

样式声明使用类似于 CSS 的语法,并且支持变量。

@global_bold_font_name: RobotoSlab-Bold;
@global_regular_font_name: RobotoSlab-Regular;
@titleColor: #ff0000;
@tintColor: #3e8fdb;
@bodyColor: #333333;

body {
    color: @bodyColor;
    font-family: @global_bold_font_name;
    placeholder-font-family: @global_bold_font_name;
}

#title {
    color: @titleColor;
    font-size: 14px;
    text-align:left;
    text-maxline: 2;
}

.subtitle {
    font-size: 12px;
    text-align:left;
    text-indent: 5px;
    text-decoration: underline;
    text-decoration-color: rgba(0,0,0,1);
}

#photo {
    background-image-content: scaleToFill;
}

.photo {
    background-image: myimage;
}

以下是可用属性的列表。

******************************************************************
GENERAL KEYS:
  'background' 
  'background-color' 
  'tint-color' 
  'bartint-color' 
     Usage: #RBG | #ARGB | #RRGGBB | #AARRGGBB | rgb(red(0-255),green(0-255),blue(0-255)) | rgba(red(0-255),green(0-255),blue(0-255),alpha(0.0-1.0))
  'background-image' 
     Usage: image name, without ""
  'background-image-content' 
     Usage: scaleToFit | scaleToFill | center | top | left | bottom | right

TEXT KEYS:
  'color' 
     Usage: #RBG | #ARGB | #RRGGBB | #AARRGGBB | rgb(red(0-255),green(0-255),blue(0-255)) | rgba(red(0-255),green(0-255),blue(0-255),alpha(0.0-1.0))
  'font-family' 
     Usage: font name, without ""
  'font-size' 
     Usage: size in px
  'text-align' 
     Usage: left | center | right | justify
  'text-maxline' 
     Usage: number of lines max (integer)
  'line-height' 
     Usage: height in px
  'paragraph-spacing' 
     Usage: spacing in px
  'text-indent' 
     Usage: header in px. Used to indent first line of any new paragraph.
  'word-wrap' 
     Usage: word-wrapping | char-wrapping | clipping | truncating-head | truncating-tail | truncating-middle
  'text-stroke-color' 
     Usage: #RBG | #ARGB | #RRGGBB | #AARRGGBB | rgb(red(0-255),green(0-255),blue(0-255)) | rgba(red(0-255),green(0-255),blue(0-255),alpha(0.0-1.0))
  'text-stroke-width' 
     Usage: width in px
  'text-decoration' 
     Usage: none|underline|line-through
  'text-decoration-color' 
     Usage: #RBG | #ARGB | #RRGGBB | #AARRGGBB | rgb(red(0-255),green(0-255),blue(0-255)) | rgba(red(0-255),green(0-255),blue(0-255),alpha(0.0-1.0))

For placeholder styling
  'placeholder-' 
     Usage: You can add 'placeholder-' to any of the above text keys

DECORATIONS KEYS:
  'border' 
     Patterns: 
       - width color
  'border-color' 
     Usage: #RBG | #ARGB | #RRGGBB | #AARRGGBB | rgb(red(0-255),green(0-255),blue(0-255)) | rgba(red(0-255),green(0-255),blue(0-255),alpha(0.0-1.0))
  'border-width' 
     Patterns: 
       - width
  'border-radius' 
     Usage: radius in px

******************************************************************

为了增加灵活性,可以嵌套 id 和类,例如:
非常类似于在 HTML 中做的,但限制在这些。

#title.commonText {} //will apply this style to any element that as styleClass = "commonText" and styleId = "title"
#title.commonText.link {} //will apply this style to any element that as styleClass = "commonText link" and styleId = "title"
.commonText.link {} //will apply this style to any element that as styleClass = "commonText link"
UILabel.commonText.link {} //will apply this style to all UILabel that as styleClass = "commonText link"
UITextField {} //will apply this style to all UITextField

初始化 CSS 样式管理器

如果您倾向于分离 CSS 文件,可以将它们添加到数组中。
您会发现它们是 URL,因此您可以使用 http。 :)

//Load style from bundle
let defaultUrls = [
    NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("default.css", ofType: nil)!)
]
LazyStyleSheetManager.shared.setDefaultStylesFromFileAtUrls(defaultUrls)

主题切换

可以针对给定的集合名称加载一组不同的 CSS 文件。

let defaultUrls = [
    NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("default.css", ofType: nil)!)
]
LazyStyleSheetManager.shared.setDefaultStylesFromFileAtUrls(defaultUrls)

let alternativeUrls = [
    NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("alt.css", ofType: nil)!)
]
LazyStyleSheetManager.shared.setStylesFromFileAtUrls(alternativeUrls, collectionName: kAlternativeCollectionName)

在任何需要的时候,您都可以通过更改 currentCollecionName 为其他样式来交换。这将触发 kUpdateStylesNotificationKey 下的通知。它是自动的,因此不需要为它添加观察者,除非是为了其他目的。

LazyStyleSheetManager.shared.currentCollectionName = kAlternativeCollectionName

CSS 样式管理器助手

用于打印 CSS 属性和选项。

LazyStyleSheetManager.shared.help()

使用 CSS 创建高级视图配置

基本上与上面一样,但更简单。

import LazyKit

struct MyConfigurations: LazyViewConfigurations {

    static func elementsOptions() -> [ElementOptions]? {
        return [
            LabelOptions(identifier: "title",
                text: "hello",
                styleId: "title"),

            LabelOptions(identifier: "subtitle",
                text: "hey",
                styleId: "subtitle"),

            ButtonOptions(identifier: "button",
                texts: [.Normal: "button", .Highlighted: "highlighted"],
                styleId: "button"),

            ViewOptions(identifier: "line",
                viewBaseOptions: ViewBaseOptions(backgroundColor: .lightGrayColor())),

            ImageOptions(identifier: "photo",
                styleId: "photo",
                styleClass: "photo"),

            TextFieldOptions(identifier: "textfield",
                placeholderText: "placeholder",
                styleId: "textfield",
                textInputOptions: TextInputBaseOptions(autocapitalizationType: .Sentences, autocorrectionType: .No, spellCheckingType: .No, keyboardType: .NumbersAndPunctuation, keyboardAppearance: .Dark, returnKeyType: .Done)),

            TextViewOptions(identifier: "textview",
                styleId: "textview",
                textInputOptions: TextInputBaseOptions(autocapitalizationType: .Sentences, autocorrectionType: .No, spellCheckingType: .No, keyboardType: .EmailAddress, keyboardAppearance: .Dark, returnKeyType: .Done)
            )
        ]
    }

    static func visualFormatConstraintOptions() -> [VisualFormatConstraintOptions]? {

        return [
            VisualFormatConstraintOptions(string: "H:|-[photo(==photoW)]-[title]-|"),
            VisualFormatConstraintOptions(string: "H:[subtitle(==title)]"),
            VisualFormatConstraintOptions(string: "H:[textfield(==title)]"),
            VisualFormatConstraintOptions(string: "H:|-40-[line]-40-|"),
            VisualFormatConstraintOptions(string: "H:|-[textview]-|"),
            VisualFormatConstraintOptions(string: "H:|-buttonLeft-[button]-buttonRight-|"),
            VisualFormatConstraintOptions(string: "V:|-top-[title]-[subtitle]-[textfield]", options: .AlignAllLeft),
            VisualFormatConstraintOptions(string: "V:|-top-[photo(==photoH)]"),
            VisualFormatConstraintOptions(string: "V:[line(==1)]-[textview(==200)]-200-[button(==buttonH)]-8-|")
        ]
    }

    static func visualFormatMetrics() -> [String: AnyObject]? {

        return ["top" : 30, "buttonH" : 44, "buttonLeft" : 100, "buttonRight" : 100, "photoW" : 100, "photoH" : 60]
    }

    static func layoutConstraints() -> [ConstraintOptions]? {

        return [
            ConstraintOptions(identifier: "titleHeight", itemIdentifier: "title", attribute: .Height, relatedBy: .Equal, toItemIdentifier: nil, attribute: .Height, multiplier: 1, constant: 40)
        ]
    }
}

访问一个元素

各种实现方式。

viewManager.updateElement("title", elementOptions: LabelOptions(textOptions: TextBaseOptions(text: "Bonjour")))

viewManager.updateElement("title", type: UILabel.self) { (element) -> Void in

    element.text = "Bonjour"
}

if let title: UILabel = viewManager.element("title") {

    title.text = "Bonjour"
}

if let title = viewManager.label("title") {

    title.text = "Bonjour"
}

viewManager.label("title")!.text = "Bonjour"

使用新选项更新元素

您可以用任何基本选项更新元素。
请注意,新选项将替换非 nil 的属性。

viewManager.updateElement("title", elementOptions: LabelOptions(textOptions: TextBaseOptions(text: "Bonjour")))

更新元素的状态,如 UIButton

viewManager.updateElement("button", elementOptions: ButtonOptions(textOptionsForType: [.Normal: TextBaseOptions(text: "Done"), .Highlighted: TextBaseOptions(text: "Highlighted")]))

更新约束常量

针对这次特定的更改,建议不要使用视觉格式约束,因为这会创建一系列NSLayoutConstraints。最佳的做法是使用此函数创建约束。

static func layoutConstraints() -> [ConstraintOptions]? {
    return [
            ConstraintOptions(identifier: "titleHeight", itemIdentifier: "title", attribute: .Height, relatedBy: .Equal, toItemIdentifier: nil, attribute: .Height, multiplier: 1, constant: 40)
        ]

现在您将能够针对要更改的具体约束进行操作。

//This will make your title height to change from 40 to 120. Usefull if you want to expand/collapse an element.
viewManager.changeConstantOfLayoutConstaint("titleHeight", constant: 120)

常见问题解答(FAQ)

致谢

Kevin Malkic

许可协议

LazyKit是在MIT许可下发布的。有关详细信息,请参阅LICENSE文件。