一个 UI 小尝试~

一个链式的 UI 小尝试。。

Swift

结合 Snapkit 进行的 Swift 链式UI。

命名空间

Swift 有自己的命名空间。不像 Objective-C 一样,需要一个很繁琐的前缀。但是如果是系统的 API 呢,万一重名了呢?那么我们可以进行一个独特的命名空间。

看了KingfisherRxSwift 的源码,自己的命名空间并不难。无非就是定义一个结构体或类,并且定义一个命名的协议,之后进行扩展。还是上代码吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct Reader<T> {
let base : T
init(_ base: T) {
self.base = base
}
}
protocol ReaderNameCompatible {
associatedtype ReaderCompatibleType
var rd : Reader<ReaderCompatibleType> { get }
static var RD : Reader<ReaderCompatibleType>.Type { get }
}
extension ReaderNameCompatible {
var rd : Reader<Self> {
return Reader(self)
}
public static var RD : Reader<Self>.Type {
return Reader<Self>.self
}
}

为了区分 static 调用,所以关于 static 的实例,采用了大写的 RD。
这里还出了一个小乌龙。原先,自己定义的 Struct 是这样的。

1
2
3
4
5
6
struct Reader<Base> {
let base : Base
init(_ base: Base) {
self.base = base
}
}

泛型用了 Base 表示。但是当用到 RxSwift 的时候,那些我自己实现协议的类,比如 UIColor 等。经常就会莫名其妙报错。而且很玄幻。

UI 小尝试

项目中使用了 Snapkit。但是每次都得很繁琐的写上几行代码。

1
2
3
4
5
6
let btn = UIButton()
btn.backgroundColor = .yellow
view.addSubview(btn)
btn.snp.makeConstraints {
$0.edges.equalToSuperview()
}

于是就在想,能不能一行搞定呢。于是就有了一个小扩展。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import UIKit
import SnapKit
extension UIView : ReaderNameCompatible { }
extension Reader where T: UIView {
typealias View = T
@discardableResult
func add(in superView: UIView) -> Reader<View> {
superView.addSubview(base)
return self
}
@discardableResult
func with(_ handler: ((View) -> Void)) -> Reader<View> {
handler(base)
return self
}
@discardableResult
func constaint(_ handler: ((ConstraintMaker) -> Void)) -> View {
base.snp.makeConstraints { (make) in
handler(make)
}
return base
}
}

使用这个扩展后,当在写 UI 布局的时候,就可以是下面这样了。

1
2
3
4
5
let btn = UIButton().rd.add(in: view).with {
$0.backgroundColor = .yellow
}.constaint {
$0.edges.equalToSuperview()
}

形式上,看上去也挺 OK 的。

Objective-C

上述用 Swift 以及 Snapkit 进行一个链式的 UI。那么用 Objective-C 呢。上述是使用了 Snapkit,那么 Objective-C 就选择用 Masonry 吧。

先上源码吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// UIView+Extension.h
@interface UIView (Extension)
- (UIView * (^)(UIView *))addToParentView;
- (UIView *(^)(void (^)(MASConstraintMaker *)))makeConstraint;
- (UIView *(^)(void (^)(UIView *)))config;
@end
// UIView+Extension.m
@implementation UIView (Extension)
- (UIView * (^)(UIView *))addToParentView {
return ^id(UIView *parentView) {
[parentView addSubview:self];
return self;
};
}
- (UIView *(^)(void (^)(MASConstraintMaker *)))makeConstraint {
return ^id(void (^block)(MASConstraintMaker *) ) {
[self mas_makeConstraints:block];
return self;
};
}
- (UIView *(^)(void (^)(UIView *)))config {
return ^id(void (^block)(UIView *)) {
block(self);
return self;
};
}
@end

在使用上,是这样的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
UIView.new.addToParentView(self.view)
.config(^(UIView *make){
make.backgroundColor = [UIColor yellowColor];
})
.makeConstraint(^(MASConstraintMaker *make){
if (@available(iOS 11.0, *)) {
make.top.equalTo(self.view.mas_safeAreaLayoutGuideTop);
} else {
// Fallback on earlier versions
}
make.height.offset(30);
make.left.right.equalTo(self.view);
});

比较遗憾的是,在使用 config 或者 makeConstraint 时,块的用法并不是很好的进行提示。

就这样吧。一个小尝试。一篇没有水分的 Blog。(逃