最好用的 iOS 快速布局UI庫(kù)

NerdyUI

github: https://github.com/nerdycat/NerdyUI

Cupcake (Swift 版本)

github: https://github.com/nerdycat/Cupcake

相關(guān)文章:
NerdyUI 使用小技巧


序言

眾所周知,UI在一個(gè)App中所占的比重是很大的,如果能快速的布局UI,就能大大的提高App的整體開(kāi)發(fā)效率,NerdyUI正是基于這個(gè)理由創(chuàng)建的。

NerdyUI使用非常緊湊的鏈?zhǔn)秸Z(yǔ)法,提供一些常用但系統(tǒng)控件又缺失的功能,更為簡(jiǎn)便的約束創(chuàng)建方式和更好理解的布局系統(tǒng),勢(shì)必能大大減少您的代碼量和開(kāi)發(fā)時(shí)間。


快速創(chuàng)建 NSString, UIFont, UIColor, UIImage 和常用的 structs

你可以用 Str() 來(lái)轉(zhuǎn)換大部分類型到NSString。同理,你可以用 Log() 來(lái)打印大部分的變量。

Str(100);                    //@"100"
Str(3.14);                  //@"3.14"
Str(@0.618);                  //@"0.618"
Str(view.frame);              //@"{{0, 0}, {100, 100}}"
Str(view.center);            //@"{50, 50}"
Str(_cmd);                  //@"viewDidLoad"
Str(NSString.class);          //@"NSString"
Str("C-String");              //@"C-String"
Str(@"1 + 1 = %d", 1 + 1);  //@"1 + 1 = 2

Log(100);
Log(3.14);
Log(@0.618);
Log(view.frame);
...
Log(@"1 + 1 = %d", 1 + 1);

//拼接字符串
@"1".a(@"2").a(3).a(nil).a(4.0f).a(@5).a(@"%d", 6);    //@"123456"

你可以用 AttStr() 來(lái)創(chuàng)建NSAttributedString。

AttStr(@"hello, 101").match(@"[0-9]+").underline;  //給101加下劃線
AttStr(@"A smile ", Img(@"smile"), @" !!");     //帶圖片的NSAttributedString

你可以用 Fnt() 來(lái)創(chuàng)建UIFont。

Fnt(15);                     //[UIFont systemFontOfSize:15]
Fnt(@15);                   //[UIFont boldSystemFontOfSize:15]
Fnt(@"body");               //UIFontTextStyleBody
Fnt(@"Helvetica,15");       //helvetica font with size 15

你可以用 Color() 來(lái)創(chuàng)建UIColor。

Color(@"red");              //[UIColor redColor]
Color(@"0,0,255");          //RGB color
Color(@"#0000FF");          //Hex color
Color(@"random");            //random color

//以上這些還可以有一個(gè)可選的alpha參數(shù)
Color(@"red,0.5");            //red color with alpha 0.5
Color(@"0,0,255,0.8");        //blue color with alpha 0.8
...

Color(Img(@"pattern"));       //pattern image color

你可以用 Img() 來(lái)創(chuàng)建UIImage。

Img(@"imageName");          //[UIImage imageNamed:@"imageName"]
Img(@"#imageName");        //加上#號(hào)會(huì)返回一個(gè)可拉伸的圖片,拉伸位置在圖片中心
Img(@"red");                  //返回一個(gè) 1x1 大小的紅色圖片,可用在某些需要純色圖片的地方

你可以用 XY(), WH(), XYWH(), Range(), Insets() 來(lái)創(chuàng)建CGPoint, CGSize, CGRect, NSRange, UIEdgeInsets。XYWH()Insets() 支持多種創(chuàng)建方式。

CGPoint     p = XY(20, 20);
CGSize      s = WH(50, 50);

CGRect      f1 = XYWH(20, 20, 50, 50);
CGRect      f2 = XYWH(f1.origin, f1.size);
CGRect      f3 = XYWH(f2.origin, 50, 50);
CGRect      f4 = XYWH(20, 20, f3.size);

NSRange     r = Range(10, 20);

UIEdgeInsets i1 = Insets(10);               //{10, 10, 10, 10}
UIEdgeInsets i2 = Insets(10, 20);           //{10, 20, 10, 20}
UIEdgeInsets i3 = Insets(10, 20, 30);       //{10, 20, 30, 20}
UIEdgeInsets i4 = Insets(10, 20, 30, 40);   //{10, 20, 30, 40}

使用這些宏可以簡(jiǎn)化一些常見(jiàn)類型的創(chuàng)建過(guò)程,更重要的是你可以用同樣的方式來(lái)設(shè)置視圖的屬性值,稍后你就會(huì)明白這是什么意思。

快速訪問(wèn)frame屬性和屏幕大小

someView.x = 10;
someView.y = someView.x;
someView.xy = XY(10, 10);
someView.w = 50;                //width
someView.h = someView.w;        //height
someView.wh = WH(50, 50);
someView.frame = XYWH(10, 10, 50, 50);

someView.cx = 25;
someView.cy = someView.cx;
someView.center = XY(25, 25);

someView.maxX = 60;
someView.maxY = someView.maxX;
someView.maxXY = XY(60, 60);

//Screen只是 [UIScreen mainScreen] 的宏定義
someView.wh = WH(Screen.width, Screen.height);

我猜大部分人都有類似的擴(kuò)展吧

快速的創(chuàng)建UI控件

NerdyUI 使用鏈?zhǔn)秸Z(yǔ)法來(lái)快速的創(chuàng)建和設(shè)置 UI 控件。

UIView *view1 = View.xywh(20, 30, 50, 50).bgColor(@"red").opacity(0.7).border(3, @"3d3d3d");
UIView *view2 = View.xy(80, 30).wh(view1.wh).bgColor(@"blue,0.7").borderRadius(25).shadow(0.8).onClick(^{
    Log(@"view2");
});
view
view
UIImageView *moose = ImageView.img(@"moose").x(20).y(100).shadow(0.6, 2, -3, -1);
UILabel *quiz = Label.str(@"%d+%d=?", 1, 1).fnt(@17).color(@"66,66,66").fitSize.x(moose.maxX + 10).cy(moose.cy);
moose
moose
//如果后續(xù)不需要再訪問(wèn) title 的屬性,定義為 id 可以減少一些代碼量
id title = AttStr(@"TAP ME").fnt(15).underline.range(0, 3).fnt(@18).color(@"random");
UIButton *button1 = Button.str(title).insets(5, 10).fitSize.border(1).xy(20, 150).onClick(^(UIButton *btn) {
    //Exp() 可在任何位置執(zhí)行任意代碼
    quiz.text = Str(@"%d+%d=%d", 1, 1, Exp(btn.tag += 1)); 
    [quiz sizeToFit];
});

UIButton *button2 = Button.str(@"HAT").highColor(@"brown").img(@"hat").gap(8);
button2.xywh(button1.frame).x(button1.maxX + 10).borderRadius(5).bgImg(@"blue,0.5").highBgImg(@"orange");
//.highBgImg() 可以用來(lái)設(shè)置 UIButton 的 highlightedBackgroundColor,這是一個(gè)非常有用的功能
button
button
id pinField = TextField.x(button1.x).y(button1.maxY + 15).wh(170, 30).onChange(^(NSString *text) {
    //這里的 self 已經(jīng)自動(dòng)做了 weakify 處理, 不用擔(dān)心會(huì)有引用循環(huán)
    [(id)[self.view viewWithTag:101] setText:text];
}).numberKeyboard.maxLength(4).hint(@"pin code").fnt(15).roundStyle;

id textView = TextView.xywh(20, 240, 170, 100).border(1).insets(8).hint(@"placeholder").fnt([pinField font]).tg(101);
input
input

正如你所看到的,大部分鏈?zhǔn)綄傩赃€是比較簡(jiǎn)單明了的。有一些屬性非常的靈活,可以接受不同類型的參數(shù)。順便說(shuō)一下,View 只是 [UIView new] 的宏定義,Label 只是 [UILabel new] 的宏定義,其他幾個(gè)UI類也一樣(就是類名去掉 UI )。

你可以用 .opacity().tg() 來(lái)設(shè)置視圖的 alpha 和 tag 值.

你可以用 .x(), .y(), .xy(), .w(), .h(), .wh(), .xywh(), .cx(), .cy(), .cxy(), .maxX(), .maxY(), .maxXY() 等來(lái)設(shè)置視圖的大小和位置。

你可以用 .touchEnabled, .touchDisabled, .invisible 來(lái)設(shè)置視圖是否可點(diǎn)和是否可見(jiàn)。

你可以用 .flexibleLeft, .flexibleRight, .flexibleTop, .flexibleBottom, .flexibleLR, .flexibleTB, .flexibleLRTB, .flexibleWidth, .flexibleHeight, .flexibleWH 等來(lái)設(shè)置autoresizingMask。

你可以用 .centerAlignment, .rightAlignment 等來(lái)設(shè)置對(duì)齊屬性。

你可以用 .fnt() 來(lái)設(shè)置字體,它能接受的參數(shù)跟 Fnt() 一樣。

你可以用 .str() 來(lái)設(shè)置 text 或者 attributedText, 它能接受的參數(shù)跟 Str() 一樣。

你可以用 .img(), .highImg()u, .bgImg().highBgImg() 來(lái)設(shè)置 image, highlightedImage, backgroundImage 和 highlightedBackgroundImage。 他們能接受的參數(shù)跟 Img() 一樣。

你可以用 .tint(), .color(), .bgColor(), .highColor() 來(lái)設(shè)置 tintColor, textColor, backgroundColor 和 highlightedTextColor, 它們能接受的參數(shù)跟 Color() 一樣。

你可以用 .border(), .borderRadius().shadow() 來(lái)設(shè)置邊框和陰影。

你可以用 .fitWidth, .fitHeight.fitSize 來(lái)改變視圖的大小,使它的大小剛好能包含視圖的內(nèi)容。

你可以用 .onClick() 來(lái)給任何視圖添加一個(gè)單擊事件。

至于 UITextField 和 UITextView, 你可以用 .hint() 來(lái)設(shè)置 placeholder, .maxLength() 來(lái)限制輸入文本的長(zhǎng)度, .onChange() 來(lái)添加一個(gè)文本改變事件。

如果是 UIButton, UITextField 和 UITextView, 你還可以使用 .insets() 來(lái)添加一些padding。

這里列出的只是一部分屬性,你可以到對(duì)應(yīng)的擴(kuò)展頭文件里看完整的屬性列表。

UILabel擴(kuò)展

以前如果想給UILabel添加行間距,必須使用NSAttributedString。現(xiàn)在你只需要使用 .lineGap() 設(shè)置一下就行了。

另一個(gè)很有的擴(kuò)展功能是鏈接,你只需要使用 AttStr() 來(lái)創(chuàng)建一個(gè)NSAttributedString, 并標(biāo)記其中一部分為 .linkForLabel,那么標(biāo)記的那部分自動(dòng)就會(huì)變成鏈接。然后你只需要用 .onLink() 來(lái)給UILabel 添加一個(gè)鏈接點(diǎn)擊事件就行了。

id str = @"Lorem ipsum 20 dolor sit er elit lamet, consectetaur cillium #adipisicing pecu, sed do #eiusmod tempor incididunt ut labore et 3.14 dolore magna aliqua.";
id attStr = AttStr(str).range(0, 5).match(@"lamet").match(@"[0-9.]+").matchHashTag.linkForLabel;

Label.str(attStr).multiline.lineGap(10).xywh(self.view.bounds).onLink(^(NSString *text) {
    Log(text);
}).addTo(self.view);
label
label

快速的創(chuàng)建約束

有的時(shí)候手動(dòng)修改 frame 會(huì)顯得很麻煩。NerdyUI 提供一些鏈?zhǔn)綄傩院鸵粋€(gè)跟 Masonry 類似的方式來(lái)創(chuàng)建約束。

你可以用 .fixWidth(), .fixHeight(), .fixWH() 來(lái)添加寬高約束。

你可以用 .embedIn() 來(lái)把一個(gè)視圖嵌入到它的父視圖里, 這會(huì)添加上下左右的約束。

你可以用 .horHugging(), .horResistance(), .verHugging(), .verResistance(), .lowHugging, .lowResistance, .highHugging.highResistance 來(lái)設(shè)置 contentHuggingPriority 和 contentCompressionResistancePriority。當(dāng)有多個(gè)視圖在 StackView 里時(shí),可以用這些屬性來(lái)設(shè)置允許哪些視圖可以拉伸,哪些視圖不可以拉伸。

對(duì)于更復(fù)雜的約束, 你可以用 .makeCons(), .remakeCons().updateCons() 來(lái)設(shè)置約束, 就像Masonry一樣。

ImageView.img(@"macbook").embedIn(self.view).centerMode;

id hello = Label.str(@"HELLO").fnt(@20).wh(80, 80).centerAlignment;
id mac = Label.str(@"MAC").fnt(@20).wh(80, 80).centerAlignment;

//使用 .makeCons() 之前必須把當(dāng)前視圖加到父視圖里,這里使用 .addTo() 來(lái)執(zhí)行此操作
EffectView.darkBlur.fixWH(80, 80).addTo(self.view).makeCons(^{
    //在 .makeCons() 里你可以直接使用 make 變量,不需要顯示的定義它
    make.right.equal.superview.centerX.constants(0);
    make.bottom.equal.superview.centerY.constants(0);
}).addVibrancyChild(hello).tg(101);

EffectView.extraLightBlur.fixWidth(80).fixHeight(80).addTo(self.view).makeCons(^{
    make.left.bottom.equal.view(self.view).center.constants(0, 0);
});

EffectView.lightBlur.addTo(self.view).makeCons(^{
    make.size.equal.constants(80, 80).And.center.equal.constants(40, 40);
}).addVibrancyChild(mac);

id subImg = Img(@"macbook").subImg(95, 110, 80, 80).blur(10);
ImageView.img(subImg).addTo(self.view).makeCons(^{
    make.centerX.top.equal.view([self.view viewWithTag:101]).centerX.bottom.constants(0);
});
constraints
constraints

快速布局

手動(dòng)給每個(gè)視圖添加約束稍微想一下就知道會(huì)很麻煩。幸好大部分的 UI 可以用 HorStack()VerStack() 來(lái)實(shí)現(xiàn)。使用這兩個(gè)簡(jiǎn)易版 StackView,加上上面介紹的那幾個(gè)屬性,很多時(shí)候你根本不需要手動(dòng)顯示的創(chuàng)建任何約束。

_indexLabel = Label.fnt(17).color(@"darkGray").fixWidth(44).centerAlignment;
_iconView = ImageView.fixWH(64, 64).borderRadius(10).border(Screen.onePixel, @"#CCCCCC");

//用 .preferWidth() 來(lái)設(shè)置 preferredMaxLayoutWidth,有助于提高性能
_titleLabel = Label.fnt(15).lines(2).preferWidth(Screen.width - 205);
_categoryLabel = Label.fnt(13).color(@"darkGray");

_ratingLabel = Label.fnt(11).color(@"orange");
_countLabel = Label.fnt(11).color(@"darkGray");

_actionButton = Button.fnt(@15).color(@"#0065F7").border(1, @"#0065F7").borderRadius(3);
_actionButton.highColor(@"white").highBgImg(@"#0065F7").insets(5, 10);
_iapLabel = Label.fnt(9).color(@"darkGray").lines(2).str(@"In-App\nPurchases").centerAlignment;

//.gap() 會(huì)在每一個(gè)StackView Item 之間添加間隙
id ratingStack = HorStack(_ratingLabel, _countLabel).gap(5);
id midStack = VerStack(_titleLabel, _categoryLabel, ratingStack).gap(4);
id actionStack = VerStack(_actionButton, _iapLabel).gap(4).centerAlignment;

HorStack(
         _indexLabel,
         _iconView,
         @10,           //使用NSNumber可在兩個(gè) Item 之間添加間隙
         midStack,
         NERSpring,     //NERSpring是一個(gè)特殊的變量,它相當(dāng)于一個(gè)彈簧,保證actionStack始終停留在最右邊
         actionStack
).embedIn(self.contentView, 10, 0, 10, 15);
appcell
appcell

這里我們模仿 AppStore 排行榜來(lái)創(chuàng)建一個(gè)類似的 Cell 。可以看出 HorStack (橫向) 和 VerStack (豎向) 的用法非常的簡(jiǎn)單。你只需要找出最小的 Stack ,然后把它嵌到上一層的 Stack 里,重復(fù)這個(gè)過(guò)程直到最外層的 Stack 用 embedIn 來(lái)添加到它的父視圖里。最后你還可以給這些視圖加上一些間隙(gap)。

使用 "Debug View Hierarchy" 可以看到這些視圖是怎么嵌套再一起的。

appcell2
appcell2

一旦布局完,剩下的就是設(shè)置要顯示的內(nèi)容,其他的都不需要再動(dòng)了。

輕量級(jí) Style

大部分鏈?zhǔn)綄傩远伎梢栽O(shè)置為 style。

//全局Style
Style(@"h1").color(@"#333333").fnt(17);
Style(@"button").fixHeight(30).insets(0, 10).borderRadius(5);
//局部Style
id actionButtonStyle = Style().styles(@"button h1").bgImg(@"red").highBgImg(@"blue").highColor(@"white");

這里我們創(chuàng)建了兩個(gè)全局 Style 和一個(gè)局部 Style。局部 Style 使用 .styles() 來(lái)繼承那兩個(gè)全局 Style。創(chuàng)建完之后,全局 Style 可以使用 Style 名來(lái)全局引用,局部 Style 只能使用變量名來(lái)引用。所有的 UIView(及其子類) 和 NSAttributedString 都可以引用這些 Style。

id foo = Label.styles(@"h1").str(@"hello world");
id bar = Button.styles(actionButtonStyle).str(@"Send Email");

其他

你可以用 PlainTVGroupTV 來(lái)創(chuàng)建靜態(tài)的 UITableView,比如說(shuō)設(shè)置頁(yè)面。

PlainTV(Row.str(@"Row1"), Row.str(@"Row2"), Row.str(@"Row3")).embedIn(self.view);

你可以用 AlertActionSheet 來(lái)創(chuàng)建并顯示 UIAlert 和 UIActionSheet。

Alert.title(@"Title").message(@"Message").action(@"OK",^{}), cancel(@"Cancel").show();
ActionSheet.title(@"Title").message(@"Message").action(@"OK",^{}), cancel(@"Cancel").show();

對(duì)于NSArray, 我們提供了 .forEach(), .map(), .filter().reduce() 等這幾個(gè)鏈?zhǔn)綄傩浴?/p>

id result = @[@1, @2, @3, @4].map(^(NSInteger n) {
    return n * 2;
}).filter(^(NSInteger n) {
    return n < 5;
}).reduce(^(NSInteger ac, NSInteger n) {
    return ac + n;
});

注意

在鏈?zhǔn)綄傩岳镏苯邮褂弥形淖址A繒?huì)導(dǎo)致后續(xù)的自動(dòng)補(bǔ)全提示失效,一個(gè)解決方案是把中文字符串單獨(dú)拿出來(lái)定義為一個(gè)變量,或者把 .str(), .hint() 等 放在最后面。

當(dāng)你使用 .onClick(), .onLink(), .onChange().onFinish() 時(shí), 里面的 self 已經(jīng)做了 weakify 處理了,所以你不需要擔(dān)心會(huì)有引用循環(huán)問(wèn)題。有時(shí)候你可能需要對(duì)它做個(gè)強(qiáng)引用來(lái)保證它不會(huì)被提前釋放。這幾個(gè)屬性除了可以傳一個(gè) block 之外,還可以傳一個(gè)方法名來(lái)作為回調(diào)方法。

NerdyUI 使用了非常多的宏定義和類別方法,而且為了方便使用沒(méi)添加任何前綴。雖然所有的名字都是經(jīng)過(guò)精心挑選的,不排除有跟您自己代碼或其他第三方庫(kù)沖突的可能,請(qǐng)注意。

用CocoaPods安裝

pod "NerdyUI"
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 原文地址:http://www.lxweimin.com/p/ac47c4826a19 NerdyUI 最好用的快速...
    默默_David閱讀 1,355評(píng)論 0 1
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,948評(píng)論 18 139
  • 翻譯自“Collection View Programming Guide for iOS” 0 關(guān)于iOS集合視...
    lakerszhy閱讀 3,924評(píng)論 1 22
  • 2016-12-14 Yesterday's mistake 昨天是看著英文的原文改寫(xiě)的, 所有點(diǎn)沒(méi)動(dòng)腦. 今天讀...
    愿景力閱讀 472評(píng)論 0 1
  • 記得暑假在泰國(guó)游玩的后幾天,我們住在離芭東海灘很近的一個(gè)別墅區(qū)里,這里的每棟別墅都很大,依山而建,造型各異,...
    雙魚(yú)的Antony閱讀 1,122評(píng)論 0 50