{"id":26995905,"url":"https://github.com/nerdycat/nerdyui","last_synced_at":"2025-04-04T01:16:28.254Z","repository":{"id":55127985,"uuid":"77903435","full_name":"nerdycat/NerdyUI","owner":"nerdycat","description":"An easy way to create and layout UI components for iOS.","archived":false,"fork":false,"pushed_at":"2021-01-08T02:48:34.000Z","size":1909,"stargazers_count":403,"open_issues_count":7,"forks_count":58,"subscribers_count":16,"default_branch":"master","last_synced_at":"2025-04-02T18:51:02.663Z","etag":null,"topics":["constraints","layout","nsattributedstring","objective-c","stackview","static-tableview","ui","uilabel-link"],"latest_commit_sha":null,"homepage":null,"language":"Objective-C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nerdycat.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-01-03T09:40:25.000Z","updated_at":"2024-12-09T09:01:39.000Z","dependencies_parsed_at":"2022-08-14T12:50:37.797Z","dependency_job_id":null,"html_url":"https://github.com/nerdycat/NerdyUI","commit_stats":null,"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nerdycat%2FNerdyUI","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nerdycat%2FNerdyUI/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nerdycat%2FNerdyUI/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nerdycat%2FNerdyUI/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nerdycat","download_url":"https://codeload.github.com/nerdycat/NerdyUI/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247103308,"owners_count":20884024,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["constraints","layout","nsattributedstring","objective-c","stackview","static-tableview","ui","uilabel-link"],"created_at":"2025-04-04T01:16:27.624Z","updated_at":"2025-04-04T01:16:28.243Z","avatar_url":"https://github.com/nerdycat.png","language":"Objective-C","readme":"# NerdyUI\n[![Platform](http://cocoapod-badges.herokuapp.com/p/NerdyUI/badge.png)](https://cocoapods.org/pods/NerdyUI)\n[![Language](https://camo.githubusercontent.com/329dad681452751ddf3fed2c8a32d2c4515ae03b/687474703a2f2f696d672e736869656c64732e696f2f62616467652f6c616e67756167652d4f626a432d627269676874677265656e2e7376673f7374796c653d666c6174)](https://cocoapods.org/pods/NerdyUI)\n[![Version](http://cocoapod-badges.herokuapp.com/v/NerdyUI/badge.png)](https://cocoapods.org/pods/NerdyUI)\n[![License](http://cocoapod-badges.herokuapp.com/l/NerdyUI/badge.png)](https://cocoapods.org/pods/NerdyUI)   \nAn easy way to create and layout UI components for iOS 8 and above.   \nThere is also a [Swift version](https://github.com/nerdycat/Cupcake) available.\n\n\n---\n## Macros for create NSString, UIFont, UIColor, UIImage and Common structs\n\nYou can convert nearly everything to NSString by using `Str()` macro.  \nSimilarly, you can log variables using `Log()` macro.\n\n\tStr(100);\t\t\t//@\"100\"\n\tStr(3.14);\t\t\t//@\"3.14\"\n\tStr(@0.618);\t\t\t//@\"0.618\"\n\tStr(view.frame);\t\t//@\"{{0, 0}, {100, 100}}\"\n\tStr(view.center);\t\t//@\"{50, 50}\"\n\tStr(_cmd);\t\t\t//@\"viewDidLoad\"\n\tStr(NSString.class);\t\t//@\"NSString\"\n\tStr(\"C-String\");\t\t//@\"C-String\"\n\tStr(@\"1 + 1 = %d\", 1 + 1);\t//@\"1 + 1 = 2\"\n\n\tLog(100);\n\tLog(3.14);\n\tLog(@0.618);\n\tLog(view.frame);\n\t...\n\tLog(@\"1 + 1 = %d\", 1 + 1);\n\t\n\t//Appending String\n\t@\"1\".a(@\"2\").a(3).a(nil).a(4.0f).a(@5).a(@\"%d\", 6);    //@\"123456\"\n\t\nYou can create NSAttributedString with `AttStr()` macro.\n\n\tAttStr(@\"hello, 101\").match(@\"[0-9]+\").underline;\t//mark 101 with underline\n\tAttStr(@\"A smile \", Img(@\"smile\"), @\" !!\");\t\t//attributedString with image attachment\n\t\nYou can create UIFont with `Fnt()` macro.\n\n\tFnt(15);\t\t\t//[UIFont systemFontOfSize:15]\n\tFnt(@15);\t\t\t//[UIFont boldSystemFontOfSize:15]\n\tFnt(@\"body\");\t\t\t//UIFontTextStyleBody\n\tFnt(@\"Helvetica,15\");\t\t//helvetica font with size 15\n\t\nYou can create UIColor with `Color()` macro.\n\n\tColor(@\"red\");\t\t\t//[UIColor redColor]\n\tColor(@\"0,0,255\");\t\t//RGB color\n\tColor(@\"#0000FF\");\t\t//Hex color\n\tColor(@\"random\");\t\t//random color\n\n    //also can have an optional alpha value\n    Color(@\"red,0.5\");            \t//red color with alpha 0.5\n    Color(@\"0,0,255,0.8\");        \t//blue color with alpha 0.8\n    ...\n\n    Color(Img(@\"pattern\"));       \t//pattern image color\n\t\nYou can create UIImage with `Img()` macro.\n\n\tImg(@\"imageName\");\t\t//[UIImage imageNamed:]\n\tImg(@\"#imageName\");\t\t//prefixed with # will return an stretchable image\n\tImg(@\"red\");\t\t\t//1x1 square image with red color\n\t\nYou can also create CGPoint, CGSize, CGRect, NSRange and UIEdgeInsets with `XY()`, `WH()`, `XYWH()`, `Range()`, `Insets()` macros.\n\n\tCGPoint\t\tp = XY(20, 20);\n\tCGSize\t \ts = WH(50, 50);\n\t\n\tCGRect\t \tf1 = XYWH(20, 20, 50, 50);\n\tCGRect\t\tf2 = XYWH(f1.origin, f1.size);\n\tCGRect\t\tf3 = XYWH(f2.origin, 50, 50);\n\tCGRect\t\tf4 = XYWH(20, 20, f3.size);\n\t\n\tNSRange\t\tr = Range(10, 20);\n\t\n\tUIEdgeInsets i1 = Insets(10);\t\t\t//{10, 10, 10, 10}\n\tUIEdgeInsets i2 = Insets(10, 20);\t\t//{10, 20, 10, 20}\n\tUIEdgeInsets i3 = Insets(10, 20, 30);\t\t//{10, 20, 30, 20}\n\tUIEdgeInsets i4 = Insets(10, 20, 30, 40);\t//{10, 20, 30, 40}\n\t\nThese macros exist not only because they simplify the process of creating common types, but also indicate a new way of setting properties as you will see soon.\n\n\n## Quick access to frame property and Screen size\n\n\tsomeView.x = 10;\n\tsomeView.y = someView.x;\n\tsomeView.xy = XY(10, 10);\n\tsomeView.w = 50;\t\t\t//width\n\tsomeView.h = someView.w;\t\t//height\n\tsomeView.wh = WH(50, 50);\n\tsomeView.frame = XYWH(10, 10, 50, 50);\n\t\n\tsomeView.cx = 25;\n\tsomeView.cy = someView.cx;\n\tsomeView.center = XY(25, 25);\n\t\n\tsomeView.maxX = 60;\n\tsomeView.maxY = someView.maxX;\n\tsomeView.maxXY = XY(60, 60);\n\t\n\t//qucik access screen size\n\tsomeView.wh = WH(Screen.width, Screen.height);\n\n\n## Easy way to set up UI components\nNerdyUI make it very easy to create UI components and config properties by using a chaining syntax.\n\n\tUIView *view1 = View.xywh(20, 30, 50, 50).bgColor(@\"red\").opacity(0.7).border(3, @\"3d3d3d\");\n    UIView *view2 = View.xy(80, 30).wh(view1.wh).bgColor(@\"blue,0.7\").borderRadius(25).shadow(0.8).onClick(^{\n        Log(@\"view2\");\n    });\n\n\u003cimg src=\"./res/view.png\" alt=\"view\" width=\"30%\" /\u003e\n\n\tUIImageView *moose = ImageView.img(@\"moose\").x(20).y(100).shadow(0.6, 2, -3, -1);\n    UILabel *quiz = Label.str(@\"%d+%d=?\", 1, 1).fnt(@17).color(@\"66,66,66\").fitSize.x(moose.maxX + 10).cy(moose.cy);\n\n\u003cimg src=\"./res/moose.png\" alt=\"moose\" width=\"30%\" /\u003e\n\n\tid title = AttStr(@\"TAP ME\").fnt(15).underline.range(0, 3).fnt(@18).color(@\"random\");\n    UIButton *button1 = Button.str(title).insets(5, 10).fitSize.border(1).xy(20, 150).onClick(^(UIButton *btn) {\n        //Exp allows you to execute codes in any position.\n        quiz.text = Str(@\"%d+%d=%d\", 1, 1, Exp(btn.tag += 1)); \n        [quiz sizeToFit];\n    });\n    \n    UIButton *button2 = Button.str(@\"HAT\").highColor(@\"brown\").img(@\"hat\").gap(8);\n    button2.xywh(button1.frame).x(button1.maxX + 10).borderRadius(5).bgImg(@\"blue,0.5\").highBgImg(@\"orange\");\n    //highBgImg with color string is a very useful trick to set highlighted background color for UIButton.\n    \n\u003cimg src=\"./res/button.gif\" alt=\"button\" width=\"50%\" /\u003e\n\n    id pinField = TextField.x(button1.x).y(button1.maxY + 15).wh(170, 30).onChange(^(NSString *text) {\n        //self has been weakified, no need to warry about retain cycle.\n        [(id)[self.view viewWithTag:101] setText:text];\n    }).numberKeyboard.maxLength(4).hint(@\"pin code\").fnt(15).roundStyle;\n    \n    id textView = TextView.xywh(20, 240, 170, 100).border(1).insets(8).hint(@\"placeholder\").fnt([pinField font]).tg(101);\n\n\u003cimg src=\"./res/input.gif\" alt=\"input\" width=\"50%\" /\u003e\n\nAs you can see, most of the chainable properties are quite straight forward and self-explained. Some of them are very versatile and can take many kind of arguments. By the way, `View` is just a Macro for `[UIView new]`, same as others.\n\nYou use `.opacity()` and `.tg()` to set view's alpha and tag.\n\nYou use `.x()`, `.y()`, `.xy()`, `.w()`, `.h()`, `.wh()`, `.xywh()`, `.cx()`, `.cy()`, `.cxy()`, `.maxX()`, `.maxY()`, `.maxXY()` to set view's position and size. \n\nYou use `.touchEnabled`, `.touchDisabled` to enable or disable touch.\n\nYou use `.flexibleLeft`, `.flexibleRight`, `.flexibleTop`, `.flexibleBottom`,   `.flexibleLR`, `.flexibleTB`, `.flexibleLRTB`, `.flexibleWidth`, `.flexibleHeight`, `.flexibleWH` to set autoresizingMask.\n\nYou use `.centerAlignment`, `.rightAlignment` to set alignment.\n\nYou use `.fnt()` to set font with the same format as `Fnt()`.\n\nYou use `.str()` to set text or attribtedText with the same format as `Str()`.\n\nYou use `.img()`, `.highImg()`, `.bgImg()` and `.highBgImg()` to set image, highlightedImage, backgroundImage and highlightedBackgroundImage, with the same format as `Img()`.\n\nYou use `.tint()`, `.color()`, `.bgColor()`, `.highColor()` to set tintColor, textColor, backgroundColor and highlightedTextColor, with the same format as `Color()`.\n\nYou use `.border()`, `.borderRadius()` and `.shadow()` to config border styles and drop shadows.\n\nYou use `.fitWidth`, `.fitHeight` and `.fitSize` to change bounds to fit content.\n\nYou use `.onClick()` to add click handler to any UIView.\n\nFor UITextField and UITextView, you use `.hint()` to set placeholder, `.maxLength()` to limit the total length, `.onChange()` for adding text change handler.\n\nFor UIButton, UITextField and UITextView, you use `.insets()` to add padding to contents.\n\nAn there are many more. Check the corresponding header file for more information.\n\n\n##Enhancements to UILabel\nYou can add line spacing to UILabel by simply use `.lineGap()`.\n\nYou can add link to UILabel as well. All you have to do is create an NSAttributedString that marked as `.linkForLabel`, and then add a link click handler to UILabel with `.onLink()`.\n\n\tid 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.\";\n    id attStr = AttStr(str).range(0, 5).match(@\"lamet\").match(@\"[0-9.]+\").matchHashTag.linkForLabel;\n\t\n\tLabel.str(attStr).multiline.lineGap(10).xywh(self.view.bounds).onLink(^(NSString *text) {\n        Log(text);\n    }).addTo(self.view);\n\n\u003cimg src=\"./res/label.gif\" alt=\"label\" width=\"60%\" /\u003e\n\n\n##Easy way to Setup Constraints\nManual updating frame could be a pain sometimes. NerdyUI provide some chainable properties and a Masonry like approach to setup constraints. \n\nYou use `.fixWidth()`, `.fixHeight()`, `.fixWH()` to setup width and height constraints.\n\nYou use `.embedIn()` to add to superview with edge constraints.\n\nYou use `.horHugging()`, `.horResistance()`, `.verHugging()`, `.verResistance()`, `.lowHugging`, `.lowResistance`, `.highHugging` and `.highResistance` to adjust contentHuggingPriority and contentCompressionResistancePriority. These properties are useful when the view is embedded in a StackView (like `HorStack` or `VerStack`).\n\nFor more complicated constraints, you use `.makeCons()`, `.remakeCons()` and `.updateCons()` to start the constraints making process.\n\n\tImageView.img(@\"macbook\").embedIn(self.view).centerMode;\n\t\n    id hello = Label.str(@\"HELLO\").fnt(@20).wh(80, 80).centerAlignment;\n    id mac = Label.str(@\"MAC\").fnt(@20).wh(80, 80).centerAlignment;\n    \n    //In order to use makeCons, the view must be in the view hierarchy.\n    EffectView.darkBlur.fixWH(80, 80).addTo(self.view).makeCons(^{\n        //you can use 'make' directly without the need to declare it\n        make.right.equal.superview.centerX.constants(0);\n        make.bottom.equal.superview.centerY.constants(0);\n    }).addVibrancyChild(hello).tg(101);\n    \n    EffectView.extraLightBlur.fixWidth(80).fixHeight(80).addTo(self.view).makeCons(^{\n        make.left.bottom.equal.view(self.view).center.constants(0, 0);\n    });\n    \n    EffectView.lightBlur.addTo(self.view).makeCons(^{\n        make.size.equal.constants(80, 80).And.center.equal.constants(40, 40);\n    }).addVibrancyChild(mac);\n    \n    id subImg = Img(@\"macbook\").subImg(95, 110, 80, 80).blur(10);\n    ImageView.img(subImg).addTo(self.view).makeCons(^{\n        make.centerX.top.equal.view([self.view viewWithTag:101]).centerX.bottom.constants(0);\n    });\n\n![constraints](./res/constraints.png)\n\n\n##Easy way to Layout\n\nAdding constraints for every views by hand could be tedious. Luckily, you can build most of the layouts by simply using `HorStack` and `VerStack` (which are similar to UIStackView) and hopefully without creating any explicit constirants. \n\n\t_indexLabel = Label.fnt(17).color(@\"darkGray\").fixWidth(44).centerAlignment;\n    _iconView = ImageView.fixWH(64, 64).borderRadius(10).border(Screen.onePixel, @\"#CCCCCC\");\n    \n    //Setting preferWidth here will improve performance.\n    _titleLabel = Label.fnt(15).lines(2).preferWidth(Screen.width - 205);\n    _categoryLabel = Label.fnt(13).color(@\"darkGray\");\n    \n    _ratingLabel = Label.fnt(11).color(@\"orange\");\n    _countLabel = Label.fnt(11).color(@\"darkGray\");\n    \n    _actionButton = Button.fnt(@15).color(@\"#0065F7\").border(1, @\"#0065F7\").borderRadius(3);\n    _actionButton.highColor(@\"white\").highBgImg(@\"#0065F7\").insets(5, 10);\n    _iapLabel = Label.fnt(9).color(@\"darkGray\").lines(2).str(@\"In-App\\nPurchases\").centerAlignment;\n    \n    //.gap() will add spacing between all items.\n    id ratingStack = HorStack(_ratingLabel, _countLabel).gap(5);\n    id midStack = VerStack(_titleLabel, _categoryLabel, ratingStack).gap(4);\n    id actionStack = VerStack(_actionButton, _iapLabel).gap(4).centerAlignment;\n    \n    HorStack(\n             _indexLabel,\n             _iconView,\n             @10,           //Add spacing betweens two items.\n             midStack,\n             NERSpring,     //Using spring to ensure actionStack always stay in the right most position.\n             actionStack\n    ).embedIn(self.contentView, 10, 0, 10, 15);\n\n\u003cimg src=\"./res/appcell.png\" alt=\"appcell\" width=\"60%\" /\u003e\n\nHere we create a cell mimic the AppStore Top Charts list cell. As you can see, the usages of HorStack and VerStack are quite simple. You divide your UI into small part of stacks, and embed them together with optional spacing. You can see how they are stack visually by click \"Debug View Hierarchy\". \n\n\u003cimg src=\"./res/appcell2.png\" alt=\"appcell2\" width=\"60%\" /\u003e\n\nAfter creation, all you have to do is setting item's values. Their appearance will update automatically according to you configurations. \n\n##Lightweight Styling\nNearly all the chainable properties can be set as style.\n\n\t//global style\n\tStyle(@\"h1\").color(@\"#333333\").fnt(17);\n   \tStyle(@\"button\").fixHeight(30).insets(0, 10).borderRadius(5);\n   \t//local style\n   \tid actionButtonStyle = Style().styles(@\"button h1\").bgImg(@\"red\").highBgImg(@\"blue\").highColor(@\"white\");\n\nHere you create two global styles (which can be referred globally by name later) and a local style. The local style is inherit from both two global styles with `.styles()` properties. After creation, you can apply styles to any UIView or NSAttributedString using the same syntax. \n\n\tid foo = Label.styles(@\"h1\").str(@\"hello world\");\n   \tid bar = Button.styles(actionButtonStyle).str(@\"Send Email\");\n\n##Others\n\nYou can create Static TableView with `PlainTV` and `GroupTV`, which may be useful for Settings page. \n\t\n\tPlainTV(Row.str(@\"Row1\"), Row.str(@\"Row2\"), Row.str(@\"Row3\")).embedIn(self.view);\n\nYou can present `Alert` And `ActionSheet` using the chaining syntax as well.\n\n\tAlert.title(@\"Title\").message(@\"Message\").action(@\"OK\",^{}), cancel(@\"Cancel\").show();\n\tActionSheet.title(@\"Title\").message(@\"Message\").action(@\"OK\",^{}), cancel(@\"Cancel\").show();\n\t\nFor NSArray, we also provide you `.forEach()`, `.map()`, `.filter()` and `.reduce()`. \n\n\tid result = @[@1, @2, @3, @4].map(^(NSInteger n) {\n        return n * 2;\n    }).filter(^(NSInteger n) {\n        return n \u003c 5;\n    }).reduce(^(NSInteger ac, NSInteger n) {\n        return ac + n;\n    });\n\t\n\n##Cautions\nInside `.onClick()`, `.onLink()`, `.onChange()` and `.onFinish()`, `self` has been weakified so you can use `self` directly without worrying retain cycle. Sometimes you may want to make a strong reference of `self` inside handler in order to prolong its life time.\n\nNerdyUI use a lot of macros and category methods without prefixing. It's likely will clash with your own codes or third party frameworks, so use carefully.\n\n## Installation\n\tpod \"NerdyUI\"\n\n\t \n\n\n\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnerdycat%2Fnerdyui","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnerdycat%2Fnerdyui","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnerdycat%2Fnerdyui/lists"}