Localizer 1.0.2

Localizer 1.0.2

Mihael Isaev 维护。



Localizer 1.0.2

  • 作者
  • MihaelIsaev

MIT License Swift 4.2 Cocoapod

该项目处于积极开发状态,直到 1.0.0

在 Xcode10+ 现在以 SwiftUI 类似的方式构建 UI,适用于 iOS9 及更高版本!😺

// NOTE:
// For now it's written for Swift 4.2-5.0
// but closer to 5.1 release it will support 5.1 features like `@State` and `@Binding`
// and became look more like SwiftUI cause `functionBuilder` became available.

通过给予⭐️来支持这个库⭐️!

主要功能

您可以在任何地方构建包含所有约束(甚至到其他观点)的视图,然后一旦您将其添加到父视图中,所有约束都将被激活。

重用视图非常简单,只需在扩展中声明它们即可!

非常简短的介绍

// black 100x100 view in center of future superview
lazy var view1 = View().background(.black).size(100).centerInSuperview()
// red 30x20 view in horizontal center of future superview and with vertical spacing to view1
lazy var view2 = View().background(.red)
                       .size(30, 20)
                       .centerXInSuperview()
//                      yes! you can declare constraints before adding to superivew
                       .top(to: .bottom, of: view1, 16)
// view with view1 and view2 as subviews
let awesomeView = View.subviews { [view1, view2] }

func viewDidLoad() {
    super.viewDidLoad()
    view.addSubview(awesomeView)
    // and yes! you can reach and change declared constraints easily!
    UIView.animate(duration: 0.5) {
        view2.centerX = 30
        view2.outer[.top, view1] = 16
        awesomeView.layoutIfNeeded()
    }
}

长介绍

import UIKit
import UIKitPlus

// Just feel how easy you could build & declare your views
// with all needed constraints, properties and actions
// even before adding them to superview!
class LoginViewController: ViewController {
    lazy var backButton = Button.back.tapAction { print("back tapped") }

    lazy var titleLabel = Label.welcome.text("Welcome").centerXInSuperview().topToSuperview(62)

    lazy var contentView = View.subviews { [fieldsView] }
                               .edgesToSuperview(top: 120, leading: 16, trailing: -16, bottom: 0)
                               .background(.white)
                               .corners(20, .topLeft, .topRight)

    lazy var fieldsView = VStackView { [emailField, passwordField, signInButton] }
                              .edgesToSuperview(top: 10, leading: 8, trailing: -8)

    // NOTE: WrapperView needed just to add padding since we're using these views inside VStackView
    lazy var emailField = WrapperView {
        TextField().welcome.placeholder("Email").keyboard(.emailAddress).content(.emailAddress)
    }.padding(x: 10)

    lazy var passwordField = WrapperView {
        TextField.welcome.placeholder("Password").content(.password).secure()
    }.padding(x: 10)

    lazy var signInButton = WrapperView {
        Button.bigBottomGreen.title("Sign In").tapAction(signIn)
    }.padding(top: 10, left: 16, right: 16)

    override func loadView() {
        super.loadView()
        view.backgroundColor = .black
        view.addSubview(backButton, titleLabel, contentView)
    }

    func signIn() {
        guard let email = emailField.innerView.text,
              let password = passwordField.innerView.text else { return }
        // do an API call to your server with CodyFire 😉
    }
}

就这样!是的!您只需几个扩展即可使之工作😍

// PRO-TIP:
// To avoid mess declare reusable views in extensions like this
extension FontIdentifier {
    static var sfProRegular = FontIdentifier("SFProDisplay-Regular")
    static var sfProMedium = FontIdentifier("SFProDisplay-Medium")
}
extension Label {
    static var title: Label { return Label().color(.white).font(.sfProMedium, 18) }
}
extension TextField {
    static var welcome: TextField {
        return TextField().height(40)
                          .background(.clear)
                          .color(.black)
                          .tint(.mainGreen)
                          .border(.bottom, 1, .gray)
                          .font(.sfProRegular, 16)
    }
}
extension Button {
    static var back: Button { return Button("backIcon").topToSuperview(64).leadingToSuperview(24) }
    static var bigBottomGreen: Button {
        return Button().color(.white)
                       .font(.sfProMedium, 15)
                       .background(.green)
                       .height(50)
                       .circle()
                       .shadow(.gray, opacity: 1, offset: .init(width: 0, height: -1), radius: 10)
    }
}

// PRO-TIP2:
// I'd suggest you to use extensions for everything: fonts, images, labels, buttons, colors, etc.

顺便说一句,用我们的 Example 项目的示例与 Playground 玩玩吧🎮

// - you need CocoaPods installed on your Mac
// - clone or download this repo
// - go to Example folder in terminal and execute `pod install`
// - go to Example folder in Finder and open Example.xcworkspace
// - Find Example playground at the left top corner in Xcode and start playing

好奇?❤️自己试试!😃现在!🚀

安装

使用 CocoaPods

将以下行添加到您的 Podfile 中

pod 'UIKit-Plus', '~> 0.14.1'

使用 Swift Package Manager

在 Xcode11 中,前往 文件 -> Swift 包 -> 添加包依赖 并在此处输入此仓库的 URL

https://github.com/MihaelIsaev/UIKitPlus

使用 Carthage

尚不支持。请随意发送 PR 以支持此功能。

使用方法

import UIKit
import UIKitPlus

快捷表

UIKitPlus UIKit
视图 UIView
WrapperView UIView
滚动视图 UIScrollView
集合视图 UICollectionView
表格视图 UITableView
图片 UIImageView
按钮 UIButton
标签 UILabel
文本框 UITextField
分段控制器 UISegmentedControl
视觉效果视图 UIVisualEffectView
堆叠视图 UIStackView
水平堆叠视图 UIStackView
垂直堆叠视图 UIStackView
验证码视图
AttributedString(又称AttrStr) NSAttributedString
视图控制器 UIViewController
导航控制器 UINavigationController
表单控制器
🛠 日期选择器 UIDatePicker
🛠 步进器 UIStepper
🛠 滑块 UISlider
🛠 开关 UISwitch

视图

这是一个简单的视图,可以通过声明式方式来自定义它。

View().background(.red).shadow().edgesToSuperview()

您还可以使用预定义的子视图来初始化它。

View.subviews {
    let avatar = Image("some").size(100)
//                             stick to top, leading and trailing of superview
                              .edgesToSuperview(top: 0, leading: 0, trailing: 0)

//                                 stick top to bottom of avatar view with 16pt
    let name = Label("John Smith").top(to: .bottom, of: avatar, 8)
//                                 stick to leading, trailing and bottom of superview
                                  .edgesToSuperview(leading: 0, trailing: 0, bottom: 0)
    return [avatar, name]
}

WrapperView

这是一个简单的View,但具有初始化内部视图的能力。

WrapperView {
  View().background(.red).shadow()
}.background(.green).shadow()

并且您可以在这里指定内部视图的右边距。

// to the same padding for all sides
WrapperView {
  View()
}.padding(10)
// or to specific padding for each side
WrapperView {
  View()
}.padding(top: 10, left: 5, right: 10, bottom: 5)
// or even like this
WrapperView {
  View()
}.padding(top: 10, right: 10)

ScrollView

目前没有什么有趣的功能,但您可以在声明式方式中指定一些设置。

ScrollView().paging(true).scrolling(false).hideIndicator(.horizontal)
ScrollView().paging(true).scrolling(false).hideAllIndicators()
ScrollView().contentInset(.zero)
ScrollView().contentInset(top: 10, left: 5, right: 5, bottom: 10)
ScrollView().contentInset(top: 10, bottom: 10)
ScrollView().scrollIndicatorInsets(.zero)
ScrollView().scrollIndicatorInsets(top: 10, left: 5, right: 5, bottom: 10)
ScrollView().scrollIndicatorInsets(top: 10, bottom: 10)

CollectionView

目前没有什么有趣的功能。

CollectionView().paging(true)

TableView

目前没有什么有趣的功能。

Image

Image("someImage").mode(.scaleAspectFill).clipsToBounds(true)

Button

Button()
Button("Tap me")
Button().title("Tap me") // useful if you declared Button from extension like below
Button.mySuperButton.title("Tap me")

设置背景和突出状态下的背景。

Button("Tap me").background(.white).backgroundHighlighted(.darkGray)

设置不同状态下的标题颜色。

Button("Tap me").color(.black).color(.lightGray, .disabled)

从已声明的标识符或使用系统字体设置一些字体。

Button("Tap me").font(v: .systemFont(ofSize: 15))
Button("Tap me").font(.sfProBold, 15)

添加图片。

Button("Tap me").image(UIImage(named: "cat"))
Button("Tap me").image("cat")

您可以轻松处理点击事件。

Button("Tap me").tapAction { print("button tapped") }
Button("Tap me").tapAction { button in
    print("button tapped")
}

或者像这样

func tapped() { print("button tapped") }
Button("Tap me").tapAction(tapped)

func tapped(_ button: Button) { print("button tapped") }
Button("Tap me").tapAction(tapped)

标签

它可以初始化为 String 或任意数量的 AttributedString

Label("hello 👋 ")
Label().text("hello") // useful if declare label in extension like below
Label.mySuperLabel.text("hello")
Label(AttributedString("hello").foreground(.red), AttributedString("world").foreground(.green))

从已声明的标识符或使用系统字体设置一些字体。

Label("hello").font(v: .systemFont(ofSize: 15))
Label("hello").font(.sfProBold, 15)

设置文本颜色

Label("hello").color(.red)

设置文本对齐

Label("hello").alignment(.center)

设置行数

Label("hello").lines(1)
Label("hello\nworld").lines(0)
Label("hello\nworld").lines(2)
Label("hello\nworld").multiline()

文本框

TextField()
TextField("some text")
TextField().text("some text")
TextField.mySuperDuperTextField.text("some text")

从已声明的标识符或使用系统字体设置一些字体。

TextField().font(v: .systemFont(ofSize: 15))
TextField().font(.sfProBold, 15)

设置文本颜色

TextField().color(.red)

设置文本对齐

TextField().alignment(.center)

占位符

TextField().placeholder("email")
// or use AttributedString to make it colored
TextField().placeholder(AttributedString("email").foreground(.green))

安全类型

TextField().secure()

轻松从字段中删除任何文本

TextField().cleanup()

设置键盘和内容类型

TextField().keyboard(.emailAddress).content(.emailAddress)

设置代理

TextField().delegate(self)

或以声明方式获取所需的事件

TextField().shouldBeginEditing { tf in return true }
           .didBeginEditing { tf in }
           .shouldEndEditing { tf in return true }
           .didEndEditing { tf in }
           .shouldChangeCharacters { tf, range, replacement in return true }
           .shouldClear { tf in return true }
           .shouldReturn { tf in return true }
           .editingDidBegin { tf in }
           .editingChanged { tf in }
           .editingDidEnd { tf in }

分段控制

SegmentedControl("One", "Two").select(1).changed { print("segment changed to \($0)") }

视觉效果视图

VisualEffectView(.darkBlur)
VisualEffectView(.lightBlur)
VisualEffectView(.extraLightBlur)
// iOS10+
VisualEffectView(.prominent)
VisualEffectView(.regular)

为您的自定义效果创建扩展,以便像上面示例一样轻松使用

extension UIVisualEffect {
    public static var darkBlur: UIVisualEffect { return UIBlurEffect(style: .dark) }
}

堆叠视图

StackView().axis(.vertical)
           .alignment(.fill)
           .distribution(.fillEqually)
           .spacing(16)

水平堆叠视图

StackView 相同,但具有预定义的轴和轻松添加排列视图的能力

HStackView (
  Label("hello world").background(.green),
  Label("hello world").background(.red)
).spacing(10)

VStackView

StackView 相同,但具有预定义的轴和轻松添加排列视图的能力

VStackView (
  Label("hello world").background(.green),
  Label("hello world").background(.red)
).spacing(10)

VerificationCodeView

这真是一个bonus视图!:D 现在几乎每个应用程序都使用验证码进行登录,现在你可以轻松地使用UIKitPlus实现该代码视图!:)

VerificationCodeField().digitWidth(64)
                       .digitsMargin(25)
                       .digitBorder(.bottom, 1, 0xC6CBD3)
                       .digitColor(0x171A1D)
                       .font(.sfProRegular, 32)
                       .entered(verify)

func verify(_ code: String) {
  print("entered code: " + code)
}

任何视图

背景

View().background(.red)
View().background(0xff0000)

色调

View().tint(.red)
View().tint(0xff0000)

角落

设置所有角落的半径

View().corners(10)

设置特定角落的定制半径

View().corners(10, .topLeft, topRight)
View().corners(10, .topLeft, .bottomRight)
View().corners(10, .topLeft, topRight, .bottomLeft, .bottomRight)

通过较小的边自动将视图的角落设置为圆角

View().circle()

边框

设置所有边的边框

View().border(1, .black)
View().border(1, 0x000)

在特定边上设置边框

View().border(.top, 1, .black)
View().border(.left, 1, .black)
View().border(.right, 1, .black)
View().border(.bottom, 1, .black)

从特定边移除边框

.removeBorder(.top)

Alpha

View().alpha(0)

不透明度

View().opacity(0)

隐藏

View().hidden() // true by default
View().hidden(true)
View().hidden(false)

栅格化

为了更好地实现阴影性能,可以将图层栅格化,例如:

View().rasterize() // true by default
View().rasterize(true)
View().rasterize(false)

阴影

View().shadow() // by default it's black, opacity 1, zero offset, radius 10
View().shadow(.gray, opacity: 0.8, offset: .zero, radius: 5)
View().shadow(0x000000, opacity: 0.8, offset: .zero, radius: 5)

晃动

您只需调用即可晃动任何视图:

View().shake()

您还可以自定义晃动效果

View().shake(values: [-20, 20, -20, 20, -10, 10, -5, 5, 0],
             duration: 0.6,
             axis: .horizontal,
             timing: .easeInEaseOut)
View().shake(-20, 20, -20, 20, -10, 10, -5, 5, 0,
             duration: 0.6,
             axis: .horizontal,
             timing: .easeInEaseOut)

甚至创建一个扩展

import UIKitPlus

extension DeclarativeProtocol {
  func myShake() {
      View().shake(-20, 20, -20, 20, -10, 10, -5, 5, 0,
                   duration: 0.6,
                   axis: .horizontal,
                   timing: .easeInEaseOut)
  }
}

AttributedString

您可以轻松以声明式方式创建具有属性的字符串

AttributedString("hello").background(.gray)
                         .foreground(.red)
                         .font(.sfProBold, 15)
                         .paragraphStyle(.default)
                         .ligature(1)
                         .kern(1)
                         .strikethroughStyle(1)
                         .underlineStyle(.patternDash)
                         .strokeColor(.purple)
                         .strokeWidth(1)
                         .shadow()
                   // or .shadow(offset: .zero, blur: 1, color: .lightGray)
                         .textEffect("someEffect")
                         .attachment(someAttachment)
                         .link("http://github.com")
                         .baselineOffset(1)
                         .underlineColor(.cyan)
                         .strikethroughColor(.magenta)
                         .obliqueness(1)
                         .expansion(1)
                         .glyphForm(.horizontal)
                         .writingDirection(.rightToLeft)

约束

大小

您可以通过传递 宽度高度 值来设置视图大小,如下所示

View().size(100, 200)

或者通过传递单个值来设置正方形的大小

View().size(100)

或者视图的大小可以等于其他视图的大小,因此当您更改一个视图的大小时,另一个视图也会更改其大小

let view1 = View().size(100, 200)
let view2 = View().equalSize(to: view1)

当然,您可以只指定宽度或高度,或者分别通过单独的方法指定两者

View().width(100)
View().height(200)
View().width(100).height(200)
父视图

您的视图可以粘附到其父视图的任何一边,甚至所有边

// this way it will stick with 0 constant value
View().edgesToSuperview()
// this way it will stick with 10 constant value for all sides
View().edgesToSuperview(10)
// also you could specify some values manually, but all the rest values are 0 by default
View().edgesToSuperview(top: 16, leading: 16, trailing: -16)
View().edgesToSuperview(trailing: -16, bottom: -16)

您可以将视图粘附到父视图的一边,如下所示

// empty argument means 0 constant value
View()topToSuperview()
View()topToSuperview(16)
View()leadingToSuperview()
View()leadingToSuperview(16)
View()trailingToSuperview()
View()trailingToSuperview(16)
View()bottomToSuperview()
View()bottomToSuperview(16)
View()centerXToSuperview()
View()centerXToSuperview(16)
View()centerYToSuperview()
View()centerYToSuperview(16)
View()widthToSuperview()
View()heightToSuperview()
相对位置

您的视图的任意一边也可以粘附到其他视图的任意一边

// Sides to superview
View().top(to: .bottom, of: someView, 16) // stick view's top to someView`s bottom with 16pt (by default 0pt)
View().leading(to: .trailing, of: someView)
View().trailing(to: .leading, of: someView)
View().bottom(to: .top, of: someView)
// Center to superview
View().centerX(to: .centerX, of: someView)
View().centerY(to: .centerY, of: someView)
// Dimension Superview
View().width(to: .width, of: someView)
View().height(to: .height, of: someView)

或者这样

// Sides to superview
View().edge(.top, toSuperview: someView, .bottom)
View().edge(.leading, toSuperview: someView, .trailing)
View().edge(.trailing, toSuperview: someView, .leading)
View().edge(.bottom, toSuperview: someView, .top)
// Center to superview
View().edge(.centerX, toSuperview: someView, .centerX)
View().edge(.centerY, toSuperview: someView, .centerY)
// Dimension Superview
View().edge(.width, toSuperview: someView, .width)
View().edge(.height, toSuperview: someView, .height)

要为两个相对视图创建约束

// Sides to another views
View().spacing(.leading, to: relativeView, toSide: .trailing, 16) // last parameter is optional, 0 by default
View().spacing(.trailing, to: relativeView, toSide: .leading)
View().spacing(.top, to: relativeView, toSide: .bottom)
View().spacing(.bottom, to: relativeView, toSide: .top)
// Center to another relative views
View().center(.x, to: relativeView, toSide: .x)
View().center(.y, to: relativeView, toSide: .y)
// Dimension Relative
View().dimension(.width, to: relativeView, toSide: .width)
View().dimension(.height, to: relativeView, toSide: .height)
居中

您的视图可以位于其父视图的中央

View().centerInSuperview() // exact center
View().centerInSuperview(10) // exact center +10 by x-axis, and +10 by y-axis
View().centerInSuperview(x: 5, y: 10) // exact center +5 by x-axis, and +10 by y-axis

或者可以在其他视图的中央

View().center(to: anotherView) // exact center
View().center(to: anotherView, 10) // exact center +10 by x-axis, and +10 by y-axis
View().center(to: anotherView, x: 5, y: 10) // exact center +5 by x-axis, and +10 by y-axis
直接访问约束

好的,让我们想象一下,你有一个与父视图粘合的视图

let view = View().edgesToSuperview()

现在,你的视图对父视图有顶部、leading(左边缘)、trailing(右边缘)和底部的约束,例如,你想改变top约束,可以这样操作

view.top = 16

或者

view.declarativeConstraints.top?.constant = 16

所有视图的约束都可以用同样的方式处理,所以你可以改变它们,甚至可以设置它们为nil来删除它们。

另一种情况,如果你有一个设置了相对于另一个视图的约束的视图

let centerView = View().background(.black).size(100).centerInSuperview()
let secondView = View().background(.green).size(100).centerXInSuperview().top(to: .bottom, of: centerView, 16)

例如,你想获取centerView相对于secondView的底部约束,可以这样操作

// short way
centerView.outer[.bottom, secondView] = 32 // changes their vertical spacing from 16 to 32
// long way
centerView.declarativeConstraints.outer[.bottom, secondView]?.constant = 32 // changes their vertical spacing from 16 to 32
布局边距
// to all sides
View().layoutMargin(10)
// optional sides
View().layoutMargin(top: 10)
View().layoutMargin(left: 10, bottom: 5)
View().layoutMargin(top: 10, right: 5)
// vertical and horizontal
View().layoutMargin(x: 10, y: 5) // top: 5, left: 10, right: 10, bottom: 5
View().layoutMargin(x: 10) // left: 10, right: 10
View().layoutMargin(y: 5) // top: 5, bottom: 5
安全区域

你可以在任何视图上安全地不使用#available(iOS 11.0, *)检查来获取safeArea

someView.safeArea.topAnchor
约束值

任何约束值都可以设置为CGFloat,或者与Relation一起,甚至包含Multiplier

// just equal to 10
View().leading(to: .trailing, of: anotherView, 10)

// greaterThanOrEqual to 10
View().leading(to: .trailing, of: anotherView, >=10)

// lessThanOrEqual to 10
View().leading(to: .trailing, of: anotherView, <=10)

// equal to 10 with 1.5 multiplier
View().leading(to: .trailing, of: anotherView, 10 ~ 1.5)

// equal to 10 with 1.5 multiplier and 999 priority
View().leading(to: .trailing, of: anotherView, 10 ~ 1.5 ! 999)

// equal to 10 with 1.5 multiplier and `.defaultLow` priority
View().leading(to: .trailing, of: anotherView, 10 ~ 1.5 ! .defaultLow)

// equal to 10 with 999 priority
View().leading(to: .trailing, of: anotherView, 10 ! 999)

颜色

使用UIKitPlus,你可以通过在前面加上0x前缀来直接使用十六进制颜色,例如,使用0x000表示黑色或使用0xfff表示白色。

扩展

字体

将自定义字体添加到项目中,然后按照以下方式进行声明

import UIKitPlus

extension FontIdentifier {
    public static var sfProBold = FontIdentifier("SFProDisplay-Bold")
    public static var sfProRegular = FontIdentifier("SFProDisplay-Regular")
    public static var sfProMedium = FontIdentifier("SFProDisplay-Medium")
}

然后像这样使用它们

Button().font(.sfProMedium, 15)

颜色

像这样声明自定义颜色

import UIKit
import UIKitPlus

extension UIColor {
    static var mainBlack: UIColor { return .black  }
    static var otherGreen: UIColor { return 0x3D7227.color  } // 61 114 39
}

然后像这样使用它们

Label("Hello world").color(.otherGreen).background(.mainBlack)

按钮

像这样声明自定义按钮

import UIKitPlus

extension Button {
    static var bigBottomWhite: Button {
        return Button().color(.darkGray).color(.black, .highlighted).font(.sfProMedium, 15).background(.white).backgroundHighlighted(.lightGray).circle()
    }
    static var bigBottomGreen: Button {
        return Button().color(.white).font(.sfProMedium, 15).background(.mainGreen).circle()
    }
}

然后像这样使用它们

Button.bigBottomWhite.size(300, 50).bottomToSuperview(20).centerInSuperview()

标签

像这样声明自定义属性标签

import UIKitPlus

extension Label {
    static var welcomeLogo: Label {
        return .init(AttributedString("My").foreground(.white).font(.sfProBold, 26),
                     AttributedString("App").font(.sfProBold, 26))
    }
}

然后像这样使用它们

let logo = Label.welcomeLogo.centerInSuperview()

图片

像这样声明资源图片

import UIKitPlus

extension Image {
    static var welcomeBackground: Image { return Image("WelcomeBackground") }
}

然后像这样使用它们

let backgroudImage = Image.welcomeBackground.edgesToSuperview()

子视图

在一行中添加子视图

view.addSubview(view1, view2, view3)