1. 程式人生 > >最好用的 iOS 快速佈局UI庫

最好用的 iOS 快速佈局UI庫

NerdyUI

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

Cupcake (Swift 版本)

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

相關文章:
NerdyUI 使用小技巧


序言

眾所周知,UI在一個App中所佔的比重是很大的,如果能快速的佈局UI,就能大大的提高App的整體開發效率,NerdyUI正是基於這個理由建立的。

NerdyUI使用非常緊湊的鏈式語法,提供一些常用但系統控制元件又缺失的功能,更為簡便的約束建立方式和更好理解的佈局系統,勢必能大大減少您的程式碼量和開發時間。


快速建立 NSString, UIFont, UIColor, UIImage 和常用的 structs

你可以用 Str() 來轉換大部分型別到NSString。同理,你可以用 Log() 來列印大部分的變數。

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() 來建立NSAttributedString。

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

你可以用 Fnt() 來建立UIFont。

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

你可以用 Color() 來建立UIColor。

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

//以上這些還可以有一個可選的alpha引數
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() 來建立UIImage。

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

你可以用 XY(), WH(), XYWH(), Range(), Insets() 來建立CGPoint, CGSize, CGRect, NSRange, UIEdgeInsets。XYWH()Insets() 支援多種建立方式。

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}

使用這些巨集可以簡化一些常見型別的建立過程,更重要的是你可以用同樣的方式來設定檢視的屬性值,稍後你就會明白這是什麼意思。

快速訪問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);

我猜大部分人都有類似的擴充套件吧

快速的建立UI控制元件

NerdyUI 使用鏈式語法來快速的建立和設定 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

//如果後續不需要再訪問 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() 可在任何位置執行任意程式碼
    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() 可以用來設定 UIButton 的 highlightedBackgroundColor,這是一個非常有用的功能

button

button

id pinField = TextField.x(button1.x).y(button1.maxY + 15).wh(170, 30).onChange(^(NSString *text) {
    //這裡的 self 已經自動做了 weakify 處理, 不用擔心會有引用迴圈
    [(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

正如你所看到的,大部分鏈式屬性還是比較簡單明瞭的。有一些屬性非常的靈活,可以接受不同型別的引數。順便說一下,View 只是 [UIView new] 的巨集定義,Label 只是 [UILabel new] 的巨集定義,其他幾個UI類也一樣(就是類名去掉 UI )。

你可以用 .opacity().tg() 來設定檢視的 alpha 和 tag 值.

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

你可以用 .touchEnabled, .touchDisabled, .invisible 來設定檢視是否可點和是否可見。

你可以用 .flexibleLeft, .flexibleRight, .flexibleTop, .flexibleBottom, .flexibleLR, .flexibleTB, .flexibleLRTB, .flexibleWidth, .flexibleHeight, .flexibleWH 等來設定autoresizingMask。

你可以用 .centerAlignment, .rightAlignment 等來設定對齊屬性。

你可以用 .fnt() 來設定字型,它能接受的引數跟 Fnt() 一樣。

你可以用 .str() 來設定 text 或者 attributedText, 它能接受的引數跟 Str() 一樣。

你可以用 .img(), .highImg()u, .bgImg().highBgImg() 來設定 image, highlightedImage, backgroundImage 和 highlightedBackgroundImage。 他們能接受的引數跟 Img() 一樣。

你可以用 .tint(), .color(), .bgColor(), .highColor() 來設定 tintColor, textColor, backgroundColor 和 highlightedTextColor, 它們能接受的引數跟 Color() 一樣。

你可以用 .border(), .borderRadius().shadow() 來設定邊框和陰影。

你可以用 .fitWidth, .fitHeight.fitSize 來改變檢視的大小,使它的大小剛好能包含檢視的內容。

你可以用 .onClick() 來給任何檢視新增一個單擊事件。

至於 UITextField 和 UITextView, 你可以用 .hint() 來設定 placeholder, .maxLength() 來限制輸入文字的長度, .onChange() 來新增一個文字改變事件。

如果是 UIButton, UITextField 和 UITextView, 你還可以使用 .insets() 來新增一些padding。

這裡列出的只是一部分屬性,你可以到對應的擴充套件標頭檔案裡看完整的屬性列表。

UILabel擴充套件

以前如果想給UILabel新增行間距,必須使用NSAttributedString。現在你只需要使用 .lineGap() 設定一下就行了。

另一個很有的擴充套件功能是連結,你只需要使用 AttStr() 來建立一個NSAttributedString, 並標記其中一部分為 .linkForLabel,那麼標記的那部分自動就會變成連結。然後你只需要用 .onLink() 來給UILabel 新增一個連結點選事件就行了。

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

快速的建立約束

有的時候手動修改 frame 會顯得很麻煩。NerdyUI 提供一些鏈式屬性和一個跟 Masonry 類似的方式來建立約束。

你可以用 .fixWidth(), .fixHeight(), .fixWH() 來新增寬高約束。

你可以用 .embedIn() 來把一個檢視嵌入到它的父視圖裡, 這會新增上下左右的約束。

你可以用 .horHugging(), .horResistance(), .verHugging(), .verResistance(), .lowHugging, .lowResistance, .highHugging.highResistance 來設定 contentHuggingPriority 和 contentCompressionResistancePriority。當有多個檢視在 StackView 裡時,可以用這些屬性來設定允許哪些檢視可以拉伸,哪些檢視不可以拉伸。

對於更復雜的約束, 你可以用 .makeCons(), .remakeCons().updateCons() 來設定約束, 就像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() 之前必須把當前檢視加到父視圖裡,這裡使用 .addTo() 來執行此操作
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

快速佈局

手動給每個檢視新增約束稍微想一下就知道會很麻煩。幸好大部分的 UI 可以用 HorStack()VerStack() 來實現。使用這兩個簡易版 StackView,加上上面介紹的那幾個屬性,很多時候你根本不需要手動顯示的建立任何約束。

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

//用 .preferWidth() 來設定 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() 會在每一個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可在兩個 Item 之間新增間隙
         midStack,
         NERSpring,     //NERSpring是一個特殊的變數,它相當於一個彈簧,保證actionStack始終停留在最右邊
         actionStack
).embedIn(self.contentView, 10, 0, 10, 15);

appcell

appcell

這裡我們模仿 AppStore 排行榜來建立一個類似的 Cell 。可以看出 HorStack (橫向) 和 VerStack (豎向) 的用法非常的簡單。你只需要找出最小的 Stack ,然後把它嵌到上一層的 Stack 裡,重複這個過程直到最外層的 Stack 用 embedIn 來新增到它的父視圖裡。最後你還可以給這些檢視加上一些間隙(gap)。

使用 "Debug View Hierarchy" 可以看到這些檢視是怎麼巢狀再一起的。

appcell2

appcell2

一旦佈局完,剩下的就是設定要顯示的內容,其他的都不需要再動了。

輕量級 Style

大部分鏈式屬性都可以設定為 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");

這裡我們建立了兩個全域性 Style 和一個區域性 Style。區域性 Style 使用 .styles() 來繼承那兩個全域性 Style。建立完之後,全域性 Style 可以使用 Style 名來全域性引用,區域性 Style 只能使用變數名來引用。所有的 UIView(及其子類) 和 NSAttributedString 都可以引用這些 Style。

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

其他

你可以用 PlainTVGroupTV 來建立靜態的 UITableView,比如說設定頁面。

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

你可以用 AlertActionSheet 來建立並顯示 UIAlert 和 UIActionSheet。

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

對於NSArray, 我們提供了 .forEach(), .map(), .filter().reduce() 等這幾個鏈式屬性。

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;
});

注意

在鏈式屬性裡直接使用中文字串常量會導致後續的自動補全提示失效,一個解決方案是把中文字串單獨拿出來定義為一個變數,或者把 .str(), .hint() 等 放在最後面。

當你使用 .onClick(), .onLink(), .onChange().onFinish() 時, 裡面的 self 已經做了 weakify 處理了,所以你不需要擔心會有引用迴圈問題。有時候你可能需要對它做個強引用來保證它不會被提前釋放。這幾個屬性除了可以傳一個 block 之外,還可以傳一個方法名來作為回撥方法。

NerdyUI 使用了非常多的巨集定義和類別方法,而且為了方便使用沒新增任何字首。雖然所有的名字都是經過精心挑選的,不排除有跟您自己程式碼或其他第三方庫衝突的可能,請注意。

用CocoaPods安裝

pod "NerdyUI"

 



作者:貓執旅人
連結:https://www.jianshu.com/p/ac47c4826a19
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯絡作者獲得授權並註明出處。