{"id":16755974,"url":"https://github.com/mihaelisaev/uikitplus","last_synced_at":"2025-04-07T13:03:55.639Z","repository":{"id":34997655,"uuid":"194542895","full_name":"MihaelIsaev/UIKitPlus","owner":"MihaelIsaev","description":"🏰 Declarative UIKit with LivePreview for iOS9+ (best alternative to SwiftUI)","archived":false,"fork":false,"pushed_at":"2024-09-30T07:55:25.000Z","size":1030,"stargazers_count":601,"open_issues_count":19,"forks_count":35,"subscribers_count":17,"default_branch":"master","last_synced_at":"2025-03-31T12:01:38.051Z","etag":null,"topics":["betterthanswiftui","swift","uikit","wrapper"],"latest_commit_sha":null,"homepage":"","language":"Swift","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/MihaelIsaev.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-06-30T17:27:25.000Z","updated_at":"2025-03-30T02:41:39.000Z","dependencies_parsed_at":"2023-02-18T16:45:22.705Z","dependency_job_id":"eb94510e-8002-4299-8e18-622ff9f97ac1","html_url":"https://github.com/MihaelIsaev/UIKitPlus","commit_stats":{"total_commits":644,"total_committers":6,"mean_commits":"107.33333333333333","dds":"0.012422360248447228","last_synced_commit":"daa5ffa9219ce8cbc7e8ed01abd6c1aa440a24e7"},"previous_names":[],"tags_count":122,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MihaelIsaev%2FUIKitPlus","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MihaelIsaev%2FUIKitPlus/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MihaelIsaev%2FUIKitPlus/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MihaelIsaev%2FUIKitPlus/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/MihaelIsaev","download_url":"https://codeload.github.com/MihaelIsaev/UIKitPlus/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247657275,"owners_count":20974344,"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":["betterthanswiftui","swift","uikit","wrapper"],"created_at":"2024-10-13T03:24:27.525Z","updated_at":"2025-04-07T13:03:55.620Z","avatar_url":"https://github.com/MihaelIsaev.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n    \u003ca href=\"LICENSE\"\u003e\n        \u003cimg src=\"https://img.shields.io/badge/license-MIT-brightgreen.svg\" alt=\"MIT License\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://swift.org\"\u003e\n        \u003cimg src=\"https://img.shields.io/badge/swift-5.2-brightgreen.svg\" alt=\"Swift 5.5\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://swift.org\"\u003e\n        \u003cimg src=\"https://img.shields.io/badge/iOS-9+-brightgreen.svg\" alt=\"Swift 5.5\"\u003e\n    \u003c/a\u003e\n    \u003cimg src=\"https://img.shields.io/badge/iPadOS+Catalyst-✓-brightgreen.svg\" alt=\"iPadOS and Catalyst support\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/macOS-✓-brightgreen.svg\" alt=\"macOS support\"\u003e\n    \u003ca href=\"https://cocoapods.org/pods/UIKit-Plus\"\u003e\n        \u003cimg src=\"https://img.shields.io/cocoapods/v/UIKit-Plus.svg\" alt=\"Cocoapod\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://discord.gg/q5wCPYv\"\u003e\n        \u003cimg src=\"https://img.shields.io/discord/612561840765141005\" alt=\"Swift.Stream\"\u003e\n    \u003c/a\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e🚀❤️ YOU WILL LOVE \u003cb\u003eUIKIT\u003c/b\u003e MORE THAN EVER ❤️🚀\u003c/p\u003e\n\u003cbr/\u003e\n\u003cp align=\"center\"\u003e\u003cb\u003eNothing is impossible!\u003c/b\u003e\u003c/p\u003e\n\u003cp align=\"center\"\u003eBuild awesome responsive UIs even simpler than with SwiftUI \u003cb\u003ecause you already know everything\u003c/b\u003e.\u003c/p\u003e\n\u003cbr/\u003e\n\u003cp align=\"center\"\u003eWith. Live. Preview. iOS9+.\u003c/p\u003e\n\u003cbr/\u003e\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"https://user-images.githubusercontent.com/1272610/216769696-65cc09d1-2796-4b7e-b746-01e25fc70486.jpeg\"\u003e\n\u003c/p\u003e\n\u003cbr/\u003e\n\u003cp align=\"center\"\u003e\u003ca href=\"https://github.com/MihaelIsaev/UIKitPlusExample\" style=\"color:green;\"\u003eEXAMPLES\u003c/a\u003e\u003c/p\u003e\n\u003cp align=\"center\"\u003e\u003ca href=\"https://discord.gg/q5wCPYv\"\u003eOUR COMMUNITY IN DISCORD\u003c/a\u003e\u003c/p\u003e\n\n## Requirements\n\nXcode 13.0+\n\nSwift 5.5+\n\nGood mood\n\n## Installation\n\n#### With [CocoaPods](https://cocoapods.org)\n\nAdd the following line to your Podfile:\n```ruby\npod 'UIKit-Plus', '~\u003e 2.2.0'\n```\n\n#### With [Swift Package Manager](https://swift.org/package-manager/)\n\nIn Xcode 13.0+ go to `File -\u003e Swift Packages -\u003e Add Package Dependency` and enter there URL of this repo\n```\nhttps://github.com/MihaelIsaev/UIKitPlus\n```\n\n#### IMPORTANT!\n\nSince version 2 there are a lot of advantages and fixes, and your project could look cleaner since there are no AppDelegate and SceneDelegate anymore, everything is under the hood like with SwiftUI, but it is very obvious and convenient to use any AppDelegate/SceneDelegate methods.\n\nCheck it out by creating a project with the new project template!\n\n#### IMPORTANT!\n\nTo support iOS lower than 13 you have to set `-weak_framework SwiftUI` in `Other Linker Flags` in `Build Settings`.\n\nWithout that your app gonna crash on iOS lower than 13 because it will try to load SwiftUI without luck.\n\n\u003cimg width=\"816\" alt=\"Screenshot 2020-03-29 at 03 35 10\" src=\"https://user-images.githubusercontent.com/1272610/77836323-bbd71e00-716e-11ea-88f8-3a6b135b99ec.png\"\u003e\n\n## Project Template! 🍾\n\nTo simplify life with UIKitPlus you can download our template!\n\nFor that run the following commands in console\n\n```bash\ngit clone https://github.com/MihaelIsaev/UIKitPlus.git\ncp -R UIKitPlus/Templates ~/Library/Developer/Xcode/\nrm -rf UIKitPlus\n```\n\nAfter that you will be able to go to `File -\u003e New -\u003e Project` and choose `UIKitPlus` app! 🚀\n\n![UIKitPlus App Template Screenshot](https://user-images.githubusercontent.com/1272610/78511655-87d3ac80-77af-11ea-96f7-dc0b75287207.jpg)\n\n\u003e 💡After project creation you have to install UIKitPlus manually either with Swift Package Manager or with CocoaPods\n\n### File Template\n\nTogether with project template you will get the file template 👍\n\n## Features\n\n### 1. Delayed constraints\n\nDeclare all the constraints in advance before adding view to superview. Even by tags.\n```swift\nButton(\"Click me\").width(300).centerInSuperview()\n```\n\n### 2. Declarativity\n\nBuild everything declarative way. Any view. Any control. Even layers, gestures, colors, fonts, etc.\n```swift\nUText(\"Hello world\").color(.red).alignment(.center).font(.sfProMedium, 15)\n// or\nUText(\"Hello \".color(.red).font(.sfProMedium, 15), \"world\".color(.green).font(.sfProBold, 15)).alignment(.center)\n// or even\n\"Hello world\".color(.red).alignment(.center).font(.sfProMedium, 15)\n```\n\n### 3. Reactivity\n\nUse `@UState` for any property, react on any thing, map states to different types, etc.\n```swift\n@UState var text = \"Hello world\"\nUText($text)\n\n@UState var number = 5\nUText($number.map { \"\\($0)\" })\n\n@UState var bool = false\nUText($bool.map { $0 ? \"enabled\" : \"disabled\" })\n```\n\n### 4. Purity\n\nEverything is pretty clear. Clean short code without magic.\n\n### 5. SwiftUI-like but still beloved UIKit\n\nDeclare subviews like in SwiftUI (but don't forget that we're still in UIKit and use autolayout)\n\n```swift\nbody {\n    View1()\n    View2()\n    View3()\n    // btw it is NOT limited to 10\n}\n```\n\n### 6. Reusable and extendable\n\nDeclare views or its styles in extensions. Subclass views. Use all the power of OOP.\n\n### 7. All modern features\n\nDiffable data-source (yes yes for iOS9+). Dynamic colors for light/dark mode. Stateable animations. Reactivity.\n\n### 8. Everything and even more\n\nBuilt-in `ImageLoader`, no need in huge 3rd party libs. Just set URL to `Image`. Fully customizable and overridable.\n```swift\nUImage(url: \"\")\nUImage(url: \"\", defaultImage: UIImage(named: \"emptyImage\")) // set default image to show it while loading\nUImage(url: \"\", loader: .defaultRelease) // release image before start loading\nUImage(url: \"\", loader: .defaultImmediate) // immediate replace image after loading\nUImage(url: \"\", loader: .defaultFade) // replace image with fade effect after loading\nUImage(url: \"\", loader: ImageLoader()) // subclass from `ImageLoader` and set you custom loader here\n```\n\nEasy device model and type detection and ability to set values based on that.\n```swift\nUButton(\"Click me\").width(400 !! iPhone6(300) !! .iPhone5(200))\n```\n\nLocalizable strings\n```swift\nLocalization.default = .en // set any localization as default to use it with not covered languages\nLocalization.current = .en // override current locale\nString(.en(\"Hello\"), .fr(\"Bonjour\"), .ru(\"Привет\"))\n```\n\nCustom trait collections.\n\n### 9. Live Preview\n\nLive preview provided by SwiftUI (available only since macOS Catalina).\n\n\u003e The only problem we have is that since names of views are the same in `UIKitPlus` and `SwiftUI` we should use aliases like `UButton` for `Button` or `UView` for `View`, so everything with `U` prefix. It is only necessary if you want to use live previews, otherwise there is no need to import `SwiftUI`, so no name conflicts.\n\n#### Preview single item\n\n\u003e 💡 You can create as many preview structs as you need\n\n`ViewController` example\n\n```swift\n#if canImport(SwiftUI) \u0026\u0026 DEBUG\nimport SwiftUI\n@available(iOS 13.0, *)\nstruct MyViewController_Preview: PreviewProvider, DeclarativePreview {\n    static var preview: Preview {\n        Preview {\n            MainViewController()\n        }\n        .colorScheme(.dark)\n        .device(.iPhoneX)\n        .language(.fr)\n        .rtl(true)\n    }\n}\n#endif\n```\n\n`View` example\n\n```swift\n#if canImport(SwiftUI) \u0026\u0026 DEBUG\nimport SwiftUI\n@available(iOS 13.0, *)\nstruct MyButton_Preview: PreviewProvider, DeclarativePreview {\n    static var preview: Preview {\n        Preview {\n            UButton(String(.en(\"Hello\"), .fr(\"Bonjour\"), .ru(\"Привет\")))\n                .circle()\n                .background(.blackHole / .white)\n                .color(.white / .black)\n                .height(54)\n                .edgesToSuperview(h: 8)\n                .centerYInSuperview()\n        }\n        .colorScheme(.dark)\n        .layout(.fixed(width: 300, height: 64))\n        .language(.fr)\n        .rtl(true)\n    }\n}\n#endif\n```\n\n#### Preview group 🔥\n\nIt is just convenient way to create multiple previews inside one struct\n\nLimitations:\n- only 10 previews inside group\n- `rtl` and `language` properties can be set only to group, not to previews directly\n\n```swift\n#if canImport(SwiftUI) \u0026\u0026 DEBUG\nimport SwiftUI\n@available(iOS 13.0, *)\nstruct MyPreviewGroup_Preview: PreviewProvider, DeclarativePreviewGroup {\n    static var previewGroup: PreviewGroup {\n        PreviewGroup { // 1 to 10 previews inside\n            Preview {\n                MainViewController()\n            }\n            .colorScheme(.dark)\n            .device(.iPhoneX)\n            Preview {\n                MainViewController()\n            }\n            .colorScheme(.light)\n            .device(.iPhoneX)\n            Preview {\n                // in this group title will be shown in `fr` language\n                UButton(String(.en(\"Hello\"), .fr(\"Bonjour\"), .ru(\"Привет\")))\n                    .circle()\n                    .background(.blackHole / .white)\n                    .color(.white / .black)\n                    .height(54)\n                    .edgesToSuperview(h: 8)\n                    .centerYInSuperview()\n            }\n            .colorScheme(.dark)\n            .layout(.fixed(width: 300, height: 64))\n        }\n        .language(.fr) // limited to group\n        .rtl(true) // limited to group\n    }\n}\n#endif\n```\n\n## Usage\n\n```swift\nimport UIKitPlus\n```\n\nEven no need to import `UIKit` at all!\n\n\u003cdetails\u003e\n\u003csummary\u003eConstraints\u003c/summary\u003e\n\n#### Solo\n\n##### aspectRatio\n\n```swift\n/// 1:1\nUView().aspectRatio()\n\n/// 1:1 low priority\nUView().aspectRatio(priority: .defaultLow)\n\n/// 4:3\nUView().aspectRatio(4 / 3)\n\n/// 4:3 low priority\nUView().aspectRatio(priority: .defaultLow)\n```\n##### width\n\n```swift\n/// 100pt\nUView().width(100)\n\n/// Stateable width\n@UState var width: CGFloat = 100\n\nUView().width($width)\n\n/// Stateable but based on different type\n@UState var expanded = false\n\nUView().width($expanded.map { $0 ? 200 : 100 })\n\n/// Different value for different devices\n/// 80pt for iPhone5, 120pt for any iPad, 100pt for any other devices\nUView().width(100 !! .iPhone5(80) !! .iPad(150))\n```\n\n##### height\n\n```swift\n/// 100pt\nUView().height(100)\n\n/// Stateable width\n@UState var height: CGFloat = 100\n\nUView().height($width)\n\n/// Stateable but based on different type\n@UState var expanded = false\n\nUView().height($expanded.map { $0 ? 200 : 100 })\n\n/// Different value for different devices\n/// 80pt for iPhone5, 120pt for any iPad, 100pt for any other devices\nUView().height(100 !! .iPhone5(80) !! .iPad(150))\n```\n\n##### size\n\n```swift\n/// width 100pt, height 100pt\nUView().size(100)\n\n/// width 100pt, height 200pt\nUView().size(100, 200)\n\n/// Stateable\n@UState var width: CGFloat = 100\n@UState var height: CGFloat = 100\n\nUView().size($width, 200)\nUView().size(100, $height)\nUView().size($width, $height)\n\n/// for both\n@UState var size: CGFloat = 100\nUView().size($size)\n\n/// Stateable but based on different type\n@UState var expanded = false\n\nUView().size($expanded.map { $0 ? 200 : 100 })\nUView().size(100, $expanded.map { $0 ? 200 : 100 })\nUView().size(100 !! .iPad(200), $expanded.map { $0 ? 200 !! .iPad(300) : 100 !! .iPad(200) })\nUView().size($width, $expanded.map { $0 ? 200 : 100 })\nUView().size($expanded.map { $0 ? 200 : 100 }, 100)\nUView().size($expanded.map { $0 ? 200 : 100 }, $height)\n```\n\nRead and write view's solo constraints directly. And even animate them.\n\n```swift\nlet v = UView()\nv.width = 100\nv.height = 100\nUIViewPropertyAnimator(duration: 0.5, curve: .easeInOut) {\n    v.width = 200\n    v.height = 300\n}.startAnimation()\n```\n\n#### Super\n\n##### edges\n\n```swift\n/// all edges to superview 0pt\nUView().edgesToSuperview()\n\n/// all edges to superview 16pt\nUView().edgesToSuperview(16)\n\n/// horizontal edges: 16pt, vertical edges: 24pt\nUView().edgesToSuperview(16, 24)\n\n/// horizontal edges: 16pt\nUView().edgesToSuperview(h: 16)\n\n/// vertical edges: 24pt\nUView().edgesToSuperview(v: 24)\n\n/// each edge to different value to superview\nUView().edgesToSuperview(top: 24, leading: 16, trailing: -16, bottom: -8)\n```\n\n##### top\n\n```swift\n/// 16pt to top of superview\nUView().topToSuperview(16)\n\n/// 16pt to safeArea top of superview\nUView().topToSuperview(16, safeArea: true)\n\n/// Stateable\n@UState var top: CGFloat = 16\n\nUView().topToSuperview($top)\n\n/// Stateable but based on different type\n@UState var expanded = false\n\nUView().topToSuperview($expanded.map { $0 ? 0 : 16 })\n```\n\n##### leading\n\n```swift\n/// 16pt to leading of superview\nUView().leadingToSuperview(16)\n\n/// all the same as with topToSuperview\n```\n\n##### trailing\n\n```swift\n/// -16pt to trailing of superview\nUView().trailingToSuperview(-16)\n\n/// all the same as with topToSuperview\n```\n\n##### bottom\n\n```swift\n/// -16pt to bottom of superview\nUView().leadingToSuperview(-16)\n\n/// all the same as with topToSuperview\n```\n\n##### centerX\n\n```swift\n/// right in center of superview horizontally\nUView().centerXInSuperview()\n\n/// 16pt from horizontal center of superview\nUView().centerXToSuperview(16)\n\n/// all the same as with topToSuperview\n```\n\n##### centerY\n\n```swift\n/// right in center of superview vertically\nUView().centerYInSuperview()\n\n/// 16pt from vertical center of superview\nUView().centerYToSuperview(16)\n\n/// all the same as with topToSuperview\n```\n\n##### center\n\n```swift\n/// right in center of superview both horizontally and vertically\nUView().centerInSuperview()\n\n/// 16pt from horizontal center of superview, 8pt from vertical center of superview\nUView().centerInSuperview(x: 16, y: 8)\n\n/// all the same as with topToSuperview\n```\n\n##### width\n\n```swift\n/// equal width with superview\nUView().widthToSuperview()\n\n/// equal width with superview with low priority\nUView().widthToSuperview(priority: .defaultLow)\n\n/// half width of superview\nUView().widthToSuperview(multipliedBy: 0.5)\n\n/// half width of superview with low priority\nUView().widthToSuperview(multipliedBy: 0.5, priority: .defaultLow)\n\n/// all the same as with topToSuperview\n```\n\n##### height\n\n```swift\n/// equal height with superview\nUView().heightToSuperview()\n\n/// all the same as with widthToSuperview\n```\n\nRead and write view's super constraints directly. And even animate them.\n\n```swift\nlet v = UView()\nv.top = 24\nv.leading = 16\nv.trailing = 16\nv.bottom = -24\nUIViewPropertyAnimator(duration: 0.5, curve: .easeInOut) {\n    v.top = 0\n    v.leading = 8\n    v.trailing = 8\n    v.bottom = 0\n}.startAnimation()\n```\n\n#### Relative\n\n##### top\n\n```swift\nUView().top(to: otherView)\nUView().top(to: otherView, 16)\nUView().top(to: otherView, $topStateValue)\nUView().top(to: .top, of: otherView)\nUView().top(to: .top, of: otherView, $topStateValue)\n```\n\n##### leading\n```swift\nUView().leading(to: otherView)\n\n/// all the same as for top(to:)\n```\n\n##### trailing\n```swift\nUView().trailing(to: otherView)\n\n/// all the same as for top(to:)\n```\n\n##### bottom\n```swift\nUView().bottom(to: otherView)\n\n/// all the same as for top(to:)\n```\n\n##### left\n```swift\nUView().left(to: otherView)\n\n/// all the same as for top(to:)\n```\n\n##### right\n```swift\nUView().right(to: otherView)\n\n/// all the same as for top(to:)\n```\n\n##### centerX\n```swift\nUView().centerX(to: otherView)\n\n/// all the same as for top(to:)\n```\n\n##### centerY\n```swift\nUView().centerY(to: otherView)\n\n/// all the same as for top(to:)\n```\n\n##### center\n```swift\nUView().center(to: otherView)\n\n/// all the same as for top(to:)\n```\n\n##### width\n```swift\nUView().width(to: otherView)\n\n/// all the same as for top(to:)\n```\n\n##### height\n```swift\nUView().height(to: otherView)\n\n/// all the same as for top(to:)\n```\n\n##### equal\n```swift\n/// just a convenient method to width\u0026height\nUView().equalSize(to: otherView)\n\n/// all the same as for top(to:)\n```\n\n\u003e 💡 TIP: Feel free to use `UState`, and values based on device type everywhere\n\n#### Relative constraints by tags 🔥\n\n\u003cp align=\"center\"\u003e\n\u003cimg width=\"375\" alt=\"Screenshot 2020-04-18 at 05 47 57\" src=\"https://user-images.githubusercontent.com/1272610/79625178-2bba4200-8138-11ea-959b-f7487a45cee5.png\"\u003e\n\u003c/p\u003e\n\nReally often we have to create some views with constraints related to each other 😃\n\nThe classic way is to create a variable with view somewhere outside, like this\n\n```swift\nlet someView = UView()\n```\n\nthen we used it with other views to make relative constraints\n\n```swift\nUView {\n    someView.size(200).background(.red).centerInSuperview()\n    UView().size(100).background(.cyan).centerXInSuperview().top(to: someView)\n    UView().size(100).background(.purple).centerXInSuperview().bottom(to: someView)\n    UView().size(100).background(.yellow).centerYInSuperview().right(to: someView)\n    UView().size(100).background(.green).centerYInSuperview().left(to: someView)\n}\n```\n\nBut if it's not necessary to declare view outside the you can use tag! And easily rely to it from other views!\n\n```swift\nUView {\n    UView().size(200).background(.red).centerInSuperview().tag(7)\n    UView().size(100).background(.cyan).centerXInSuperview().top(to: 7)\n    UView().size(100).background(.purple).centerXInSuperview().bottom(to: 7)\n    UView().size(100).background(.yellow).centerYInSuperview().right(to: 7)\n    UView().size(100).background(.green).centerYInSuperview().left(to: 7)\n}\n```\n\nEven order doesn't matter 🤗\n\n```swift\nUView {\n    UView().size(100).background(.cyan).centerXInSuperview().top(to: 7)\n    UView().size(100).background(.purple).centerXInSuperview().bottom(to: 7)\n    UView().size(100).background(.yellow).centerYInSuperview().right(to: 7)\n    UView().size(100).background(.green).centerYInSuperview().left(to: 7)\n    UView().size(200).background(.red).centerInSuperview().tag(7)\n}\n```\n\nYou even can add view later and all related views will immediately stick to it once it's added 🚀\n\n```swift\nlet v = UView {\n    UView().size(100).background(.cyan).centerXInSuperview().top(to: 7)\n    UView().size(100).background(.purple).centerXInSuperview().bottom(to: 7)\n    UView().size(100).background(.yellow).centerYInSuperview().right(to: 7)\n    UView().size(100).background(.green).centerYInSuperview().left(to: 7)\n}\nDispatchQueue.main.asyncAfter(deadline: .now() + 5) {\n    UIView.animate(withDuration: 1) {\n        v.body {\n            UView().size(200).background(.red).centerInSuperview().tag(7)\n        }\n    }\n}\n```\n\n#### Extra\n\nAny constraint value may be set as `CGFloat` or with `Relation` and even `Multiplier`\n```swift\n// just equal to 10\nUView().leading(to: .trailing, of: anotherView, 10)\n\n// greaterThanOrEqual to 10\nUView().leading(to: .trailing, of: anotherView, \u003e=10)\n\n// lessThanOrEqual to 10\nUView().leading(to: .trailing, of: anotherView, \u003c=10)\n\n// equal to 10 with 1.5 multiplier\nUView().leading(to: .trailing, of: anotherView, 10 ~ 1.5)\n\n// equal to 10 with 1.5 multiplier and 999 priority\nUView().leading(to: .trailing, of: anotherView, 10 ~ 1.5 ! 999)\n\n// equal to 10 with 1.5 multiplier and `.defaultLow` priority\nUView().leading(to: .trailing, of: anotherView, 10 ~ 1.5 ! .defaultLow)\n\n// equal to 10 with 999 priority\nUView().leading(to: .trailing, of: anotherView, 10 ! 999)\n```\n\n#### More about constraints direct access\nOk, let's imagine that you have a view which is sticked to its superview\n```swift\nlet view = UView().edgesToSuperview()\n```\nnow your view have top, leading, trailing and bottom constraints to its superview and e.g. you want to change `top` constraint so you could do it like this\n```swift\nview.top = 16\n```\nor\n```swift\nview.declarativeConstraints.top?.constant = 16\n```\nthe same way works with all view's constraints, so you can change them or even delete them just by setting them `nil`.\n\nAnother situation if you have a view which have a constrain to another relative view\n```swift\nlet centerView = UView().background(.black).size(100).centerInSuperview()\nlet secondView = UView().background(.green).size(100).centerXInSuperview().top(to: .bottom, of: centerView, 16)\n```\nand for example you want to reach bottom constraint of `centerView` related to `secondView`, do it like this\n```swift\n// short way\ncenterView.outer[.bottom, secondView] = 32 // changes their vertical spacing from 16 to 32\n// long way\ncenterView.declarativeConstraints.outer[.bottom, secondView]?.constant = 32 // changes their vertical spacing from 16 to 32\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eRoot View Controller 🍀\u003c/summary\u003e\n\n[Detailed instruction](Docs/RootViewController.md)\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eView\u003c/summary\u003e\n\n\u003e alias is `UView`\n\nView may be created with empty initializer\n```swift\nUView()\n```\nor you can put subviews into it right while initialization\n```swift\nUView {\n    UView()\n    UView()\n}\n```\nor you can wrap some view using `inline` keyword so that inner view will stick all edges to superview\n```swift\nUView(inline: MKMapView())\n```\nalso you can add subviews to that superview by calling `.body { ... }` method. even multiple times.\n```swift\nUView().body {\n    UView()\n    UVSpace(8)\n    UView()\n}.body {\n    UView()\n}.body {\n    UView()\n    UView()\n    UView()\n}\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eVerificationCodeView\u003c/summary\u003e\n\n`// implemented. to be described more`\n\nThis is really bonus view! :D Almost every app now uses verification codes for login and now you can easily implement that code view with UIKitPlus! :)\n```swift\nVerificationCodeField().digitWidth(64)\n                       .digitsMargin(25)\n                       .digitBorder(.bottom, 1, 0xC6CBD3)\n                       .digitColor(0x171A1D)\n                       .font(.sfProRegular, 32)\n                       .entered(verify)\n\nfunc verify(_ code: String) {\n  print(\"entered code: \" + code)\n}\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eVisualEffectView\u003c/summary\u003e\n\n```swift\n// implemented. to be described more\n\nUVisualEffectView(.darkBlur)\nUVisualEffectView(.lightBlur)\nUVisualEffectView(.extraLightBlur)\n// iOS10+\nUVisualEffectView(.prominent)\nUVisualEffectView(.regular)\n\n// iOS13+ (but can be used since iOS9+)\n// automatic dynamic effect for light and dark modes\nUVisualEffectView(.darkBlur, .lightBlur) // effect will be switched automatically. darkBlur is for light mode.\n```\nCreate your own extension for your custom effects to use them easily like in example above\n```swift\nextension UIVisualEffect {\n    public static var darkBlur: UIVisualEffect { return UIBlurEffect(style: .dark) }\n}\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eUWrapperView\u003c/summary\u003e\n\nIt is simple `View` but with ability to initialize with inner view\n```swift\nUWrapperView {\n  UView().background(.red).shadow()\n}.background(.green).shadow()\n```\nand you could specify innerView`s padding right here\n```swift\n// to the same padding for all sides\nUWrapperView {\n  UView()\n}.padding(10)\n// or to specific padding for each side\nUWrapperView {\n  UView()\n}.padding(top: 10, left: 5, right: 10, bottom: 5)\n// or even like this\nUWrapperView {\n  UView()\n}.padding(top: 10, right: 10)\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eLayerView\u003c/summary\u003e\n\n`// implemented. to be described`\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eImpact Feedback\u003c/summary\u003e\n\nMy favourite feature.\n\n```swift\nImpactFeedback.error()\nImpactFeedback.success()\nImpactFeedback.selected()\nImpactFeedback.bzz()\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eLocalization 🇮🇸🇩🇪🇯🇵🇲🇽\u003c/summary\u003e\n\n```swift\n// set any localization as default\nLocalization.default = .en\n\n// override current locale\nLocalization.current = .en\n\n// create string relative to current language\nlet myString = String(\n    .en(\"Hello\"),\n    .fr(\"Bonjour\"),\n    .ru(\"Привет\"),\n    .es(\"Hola\"),\n    .zh_Hans(\"你好\"),\n    .ja(\"こんにちは\"))\nprint(myString)\n```\n\nBy default current language is equal to `Locale.current` but you can change it by setting `Localizer.current = .en`.\nAlso localizer have `default` language in case if user's language doesn't match any in your string, and you could set it just by calling `Localizer.default = .en`.\n\nAlso you can use localizable strings directly in Button, Text, TextView, TextField and AttributedString\n```swift\nUText(.en(\"Hello\"), .ru(\"Привет\"), .fr(\"Bonjour\"), .es(\"Hola\"))\n\nUTextView(.en(\"Hello\"), .ru(\"Привет\"), .fr(\"Bonjour\"), .es(\"Hola\"))\n    .placeholder(.en(\"Hello\"), .ru(\"Привет\"), .fr(\"Bonjour\"), .es(\"Hola\"))\n\nUTextField(.en(\"Hello\"), .ru(\"Привет\"), .fr(\"Bonjour\"), .es(\"Hola\"))\n    .placeholder(.en(\"Hello\"), .ru(\"Привет\"), .fr(\"Bonjour\"), .es(\"Hola\"))\n\nUButton(.en(\"Hello\"), .ru(\"Привет\"), .fr(\"Bonjour\"), .es(\"Hola\"))\nUButton().title(.en(\"Hello\"), .ru(\"Привет\"), .fr(\"Bonjour\"), .es(\"Hola\"), state: .highlighted)\n\nString(.en(\"Hello\"), .ru(\"Привет\"), .fr(\"Bonjour\"), .es(\"Hola\"))\n```\n\n### But how to use this awesome localization with 10+ languages in the app?\n\nJust create a dedicated localization file (e.g. `Localization.swift`) like this\n\n```swift\nextension String {\n    static func transferTo(_ wallet: String) -\u003e String {\n        String(.en(\"Transfer to #\\(wallet)\"),\n                  .ru(\"Перевод на #\\(wallet)\"),\n                  .zh(\"转移到 #\\(wallet)\"),\n                  .ja(\"＃\\(wallet)に転送\"),\n                  .es(\"Transferir a #\\(wallet)\"),\n                  .fr(\"Transférer au #\\(wallet)\"),\n                  .sv(\"Överför till #\\(wallet)\"),\n                  .de(\"Übertragen Sie auf #\\(wallet)\"),\n                  .tr(\"\\(wallet) numarasına aktar\"),\n                  .it(\"Trasferimento al n. \\(wallet)\"),\n                  .cs(\"Převod na #\\(wallet)\"),\n                  .he(\"\\(wallet) העבר למספר\"),\n                  .ar(\"\\(wallet)#نقل إلى\"))\n    }\n    static var copyLink: String {\n        String(.en(\"Copy link to clipboard\"),\n                  .ru(\"Скопировать ссылку\"),\n                  .zh(\"复制链接到剪贴板\"),\n                  .ja(\"リンクをクリップボードにコピー\"),\n                  .es(\"Copiar enlace al portapapeles\"),\n                  .fr(\"Copier le lien dans le presse-papiers\"),\n                  .sv(\"Kopiera länk till urklipp\"),\n                  .de(\"Link in Zwischenablage kopieren\"),\n                  .tr(\"Bağlantıyı panoya kopyala\"),\n                  .it(\"Copia il link negli appunti\"),\n                  .cs(\"Zkopírujte odkaz do schránky\"),\n                  .he(\"העתק קישור ללוח\"),\n                  .ar(\"نسخ الرابط إلى الحافظة\"))\n    }\n    static var copyLinkSucceeded: String {\n        String(.en(\"Link has been copied to clipboard\"),\n                  .ru(\"Ссылка успешно скопирована в буфер обмена\"),\n                  .zh(\"链接已复制到剪贴板\"),\n                  .ja(\"リンクがクリップボードにコピーされました\"),\n                  .es(\"El enlace ha sido copiado al portapapeles\"),\n                  .fr(\"Le lien a été copié dans le presse-papiers\"),\n                  .sv(\"Länken har kopierats till Urklipp\"),\n                  .de(\"Der Link wurde in die Zwischenablage kopiert\"),\n                  .tr(\"Bağlantı panoya kopyalandı\"),\n                  .it(\"Il link è stato copiato negli appunti\"),\n                  .cs(\"Odkaz byl zkopírován do schránky\"),\n                  .he(\"הקישור הועתק ללוח\"),\n                  .ar(\"تم نسخ الرابط إلى الحافظة\"))\n    }\n    static var shareNumber: String {\n        String(.en(\"Share number\"),\n                  .ru(\"Поделиться номером\"),\n                  .zh(\"分享号码\"),\n                  .ja(\"共有番号\"),\n                  .es(\"Compartir número\"),\n                  .fr(\"Numéro de partage\"),\n                  .sv(\"Aktienummer\"),\n                  .de(\"Teilenummer\"),\n                  .tr(\"Numarayı paylaş\"),\n                  .it(\"Condividi il numero\"),\n                  .cs(\"Sdílejte číslo\"),\n                  .he(\"מספר שתף\"),\n                  .ar(\"رقم السهم\"))\n    }\n    static var shareLink: String {\n        String(.en(\"Share link\"),\n                  .ru(\"Поделиться ссылкой\"),\n                  .zh(\"分享链接\"),\n                  .ja(\"共有リンク\"),\n                  .es(\"Compartir enlace\"),\n                  .fr(\"Lien de partage\"),\n                  .sv(\"Dela länk\"),\n                  .de(\"Einen Link teilen\"),\n                  .tr(\"Linki paylaş\"),\n                  .it(\"Condividi il link\"),\n                  .cs(\"Sdílet odkaz\"),\n                  .he(\"שתף קישור\"),\n                  .ar(\"مشاركة الرابط\"))\n    }\n}\n```\n\nAnd then use localized string all over the app this easy way\n\n```swift\nUText(.transferTo(\"123\")) // Transfer to #123\nUText(.copyLinkSucceeded) // Copy link to clipboard\nUButton(.shareNumber) // Share number\nUButton(.shareLink) // Share link\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eView Controller\u003c/summary\u003e\n\n`// implemented. to be described`\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eStatus bar style\u003c/summary\u003e\n\nIn any `UViewController` you can set `statusBarStyle` and all its values are iOS9+.\n```swift\noverride var statusBarStyle: StatusBarStyle { .default }\noverride var statusBarStyle: StatusBarStyle { .dark }\noverride var statusBarStyle: StatusBarStyle { .light }\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eColors\u003c/summary\u003e\n\n```swift\n/// Simple color\nUIColor.red\n\n/// Automatic dynamic color: black for light mode, white for dark mode\nUIColor.black / UIColor.white\n\n/// color in hex, represented as int and supported by all color properties\n0xFF0000\n\n/// hex color converted to UIColor\n0xFF0000.color\n\n/// hex colors as dynamic UIColor\n0x000.color / 0xfff.color\n\n/// color with alpha\nUIColor.white.alpha(0.5)\n\n/// hex color with alpha\n0xFFFFFF.color.alpha(0.5)\n```\n\nDeclare custom colors like this\n```swift\nimport UIKitPlus\n\nextension UIColor {\n    static var mainBlack: UIColor { return .black  }\n    static var otherGreen: UIColor { return 0x3D7227.color  } // 61 114 39\n}\n```\nand then use them just like\n```swift\nUText(\"Hello world\").color(.otherGreen).background(.mainBlack)\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eFonts\u003c/summary\u003e\n\n```swift\n// implemented. to be described\n\n/// helper to print all the fonts in console (debug only)\nUIFont.printAll()\n```\n\nAdd your custom fonts to the project and then declare them like this\n```swift\nimport UIKitPlus\n\nextension FontIdentifier {\n    public static var sfProBold = FontIdentifier(\"SFProDisplay-Bold\")\n    public static var sfProRegular = FontIdentifier(\"SFProDisplay-Regular\")\n    public static var sfProMedium = FontIdentifier(\"SFProDisplay-Medium\")\n}\n```\nand then use them just like\n```swift\nUButton().font(.sfProMedium, 15)\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eGestures\u003c/summary\u003e\n\n[Detailed instruction](Docs/Gestures.md)\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eStates\u003c/summary\u003e\n\n\u003e alias is `UState`\n\n```swift\n/// usual\n@UState var myState = UIColor.red\n@UState var myState = \"\"\n@UState var myState = 0\n// etc.\n\n/// expressable\n$boolStateToColor.map { $0 == true ? .red : .green }\n$boolStateToString.map { !$0 ? \"night\" : \"day\" }\n\n/// mix to Int states into one String expressable\n$state1.and($state2).map { $0 \u003e $1 ? \"higher\" : \"lower\" }\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eAttributed Strings\u003c/summary\u003e\n\n```swift\n\"hello\".background(.gray)\n       .foreground(.red)\n       .font(.sfProBold, 15)\n       .paragraphStyle(.default)\n       .ligature(1)\n       .kern(1)\n       .strikethroughStyle(1)\n       .underlineStyle(.patternDash)\n       .strokeColor(.purple)\n       .strokeWidth(1)\n       .shadow()\n       // or .shadow(offset: .zero, blur: 1, color: .lightGray)\n       .textEffect(\"someEffect\")\n       .attachment(someAttachment)\n       .link(\"http://github.com\")\n       .baselineOffset(1)\n       .underlineColor(.cyan)\n       .strikethroughColor(.magenta)\n       .obliqueness(1)\n       .expansion(1)\n       .glyphForm(.horizontal)\n       .writingDirection(.rightToLeft)\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eAnimations\u003c/summary\u003e\n\n`// implemented. to be described`\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eActivity Indicator\u003c/summary\u003e\n\n`// implemented. to be described`\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eBar Button Item\u003c/summary\u003e\n\n`// implemented. to be described`\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eButton\u003c/summary\u003e\n\n\u003e alias is `UButton`\n\n`// to be described more`\n\n```swift\nUButton()\nUButton(\"Tap me\")\nUButton().title(\"Tap me\") // useful if you declared Button from extension like below\nUButton.mySuperButton.title(\"Tap me\")\n```\nbackground and background for highlighted state\n```swift\nUButton(\"Tap me\").background(.white).backgroundHighlighted(.darkGray)\n```\ntitle color for different states\n```swift\nUButton(\"Tap me\").color(.black).color(.lightGray, .disabled)\n```\nset some font from declared identifiers or with system fonts\n```swift\nUButton(\"Tap me\").font(v: .systemFont(ofSize: 15))\nUButton(\"Tap me\").font(.sfProBold, 15)\n```\nadd image\n```swift\nUButton(\"Tap me\").image(UIImage(named: \"cat\"))\nUButton(\"Tap me\").image(\"cat\")\n```\nYou can handle tap action easily\n```swift\nUButton(\"Tap me\").onTapGesture { print(\"button tapped\") }\nUButton(\"Tap me\").onTapGesture { button in\n    print(\"button tapped\")\n}\n```\nor like this\n```swift\nfunc tapped() { print(\"button tapped\") }\nUButton(\"Tap me\").onTapGesture(tapped)\n\nfunc tapped(_ button: Button) { print(\"button tapped\") }\nUButton(\"Tap me\").onTapGesture(tapped)\n```\n\nDeclare custom buttons like this\n```swift\nimport UIKitPlus\n\nextension UButton {\n    static var bigBottomWhite: Button {\n        return UButton()\n            .color(.darkGray)\n            .color(.black, .highlighted)\n            .font(.sfProMedium, 15)\n            .background(.white)\n            .backgroundHighlighted(.lightGray)\n            .circle()\n    }\n    static var bigBottomGreen: Button {\n        return UButton().color(.white).font(.sfProMedium, 15).background(.mainGreen).circle()\n    }\n}\n```\nand then use them like this\n```swift\nUButton.bigBottomWhite.size(300, 50).bottomToSuperview(20).centerInSuperview()\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eCollection\u003c/summary\u003e\n\n```swift\n// implemented. to be described\n\n// difference between Collection and CollectionView\n// flow layouts\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eControlView\u003c/summary\u003e\n\n`// implemented. to be described`\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eDatePicker\u003c/summary\u003e\n\n`// implemented. to be described`\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eDynamicPickerView\u003c/summary\u003e\n\n`// implemented. to be described`\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eStackView\u003c/summary\u003e\n\n\u003e alias is `UStackView`\n\n`// implemented. to be described`\n\n```swift\nUStackView().axis(.vertical)\n            .alignment(.fill)\n            .distribution(.fillEqually)\n            .spacing(16)\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eVStack\u003c/summary\u003e\n\n\u003e alias is `UVStack`\n\n`// implemented. to be described more`\nThe same as `StackView` but with predefined axis and ability to easily add arranged subviews\n```swift\nUVStack (\n  UText(\"hello world\").background(.green),\n  UVSpace(16) // 16pt delimiter\n  UText(\"hello world\").background(.red)\n)\n.spacing(10)\n.alignment(.left)\n.distribution(...)\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eVScrollStack\u003c/summary\u003e\n\n```swift\n// implemented. to be described\n\n/// it is the same as VStack but it is combined with ScrollView\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eHStack\u003c/summary\u003e\n\n\u003e alias is `UHStack`\n\n`// implemented. to be described more`\nThe same as `StackView` but with predefined axis and ability to easily add arranged subviews\n```swift\nUHStack (\n  UText(\"hello world\").background(.green),\n  UHSpace(16) // 16pt delimiter\n  UText(\"hello world\").background(.red)\n)\n.spacing(10)\n.alignment(.left)\n.distribution(...)\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eHScrollStack\u003c/summary\u003e\n\n```swift\n// implemented. to be described\n\n/// it is the same as HStack but it is combined with ScrollView\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eHSpace\u003c/summary\u003e\n\n```swift\n/// just a horizontal delimiter\nUHSpace(16)\n/// alternatively\nUView().width(16)\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eVSpace\u003c/summary\u003e\n\n```swift\n/// just a vertical delimiter\nUVSpace(16)\n/// alternatively\nUView().height(16)\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eSpace\u003c/summary\u003e\n\n```swift\n/// just a flexible space for stack views\nUSpace()\n/// alternatively\nUView()\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eHUD\u003c/summary\u003e\n\n`// implemented. to be described`\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eImage\u003c/summary\u003e\n\n\u003e alias is `UImage`\n\n`// to be described more`\n\nDeclare asset images like this\n```swift\nimport UIKitPlus\n\nextension Image {\n    static var welcomeBackground: UImage { return UImage(\"WelcomeBackground\") }\n}\n```\nand then use them like this\n```swift\nlet backgroudImage = UImage.welcomeBackground.edgesToSuperview()\n```\n\n#### With built-in `ImageLoader`\n\n```swift\nUImage(url: \"\")\nUImage(url: \"\", defaultImage: UIImage(named: \"emptyImage\")) // set default image to show it while loading\nUImage(url: \"\", loader: .defaultRelease) // release image before start loading\nUImage(url: \"\", loader: .defaultImmediate) // immediate replace image after loading\nUImage(url: \"\", loader: .defaultFade) // replace image with fade effect after loading\nUImage(url: \"\", loader: ImageLoader()) // subclass from `ImageLoader` and set you custom loader here\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eInputView\u003c/summary\u003e\n\n`// implemented. to be described`\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eList\u003c/summary\u003e\n\n\u003e alias is `UList`\n\n```swift\n// implemented. to be described\n\nalso describe auto-DIFF with Identable models\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eTableView\u003c/summary\u003e\n\n`// implemented. to be described`\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003ePickerView\u003c/summary\u003e\n\n`// implemented. to be described`\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eRefreshControl\u003c/summary\u003e\n\n`// implemented. to be described`\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eScrollView\u003c/summary\u003e\n\n`// implemented. to be described more`\n```swift\nUScrollView().paging(true).scrolling(false).hideIndicator(.horizontal)\nUScrollView().paging(true).scrolling(false).hideAllIndicators()\nUScrollView().contentInset(.zero)\nUScrollView().contentInset(top: 10, left: 5, right: 5, bottom: 10)\nUScrollView().contentInset(top: 10, bottom: 10)\nUScrollView().scrollIndicatorInsets(.zero)\nUScrollView().scrollIndicatorInsets(top: 10, left: 5, right: 5, bottom: 10)\nUScrollView().scrollIndicatorInsets(top: 10, bottom: 10)\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eSegmentedControl\u003c/summary\u003e\n\n\u003e alias is `USegmentedControl`\n\n`// implemented. to be described more`\n```swift\n@UState var selectedItem = 0\nUSegmentedControl(\"One\", \"Two\").select($selectedItem)\n// or simply\nUSegmentedControl(\"One\", \"Two\").select(0).changed { print(\"segment changed to \\($0)\") }\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eSliderView\u003c/summary\u003e\n\n`// implemented. to be described`\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eStepper\u003c/summary\u003e\n\n\u003e alias is `UStepper`\n\n`// implemented. to be described`\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eTextField\u003c/summary\u003e\n\n\u003e alias is `UTextField`\n\n```swift\n// implemented. to be described\n\n// format with AnyFormat\n```\n\n```swift\nUTextField()\nUTextField(\"some text\")\nUTextField().text(\"some text\")\nUTextField.mySuperDuperTextField.text(\"some text\")\n```\nset some font from declared identifiers or with system fonts\n```swift\nUTextField().font(v: .systemFont(ofSize: 15))\nUTextField().font(.sfProBold, 15)\n```\nset text color\n```swift\nUTextField().color(.red)\n```\nset text alignment\n```swift\nUTextField().alignment(.center)\n```\nplaceholder\n```swift\nUTextField().placeholder(\"email\")\n// or use AttributedString to make it colored\nUTextField().placeholder(AttributedString(\"email\").foreground(.green))\n```\nsecure\n```swift\nUTextField().secure()\n```\nremove any text from field easily\n```swift\nUTextField().cleanup()\n```\nset keyboard and content type\n```swift\nUTextField().keyboard(.emailAddress).content(.emailAddress)\n```\nlisten if user typing or not\n```swift\nUTextField().typing($isTyping, interval: 2) // very useful for chats\n```\nset delegate\n```swift\nUTextField().delegate(self)\n```\nor get needed events declarative way\n```swift\nUTextField().shouldBeginEditing { tf in return true }\n            .didBeginEditing { tf in }\n            .shouldEndEditing { tf in return true }\n            .didEndEditing { tf in }\n            .shouldChangeCharacters { tf, range, replacement in return true }\n            .shouldClear { tf in return true }\n            .shouldReturn { tf in return true }\n            .editingDidBegin { tf in }\n            .editingChanged { tf in }\n            .editingDidEnd { tf in }\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eText (aka UILabel)\u003c/summary\u003e\n\n\u003e alias is `UText` or just `Label`\n\n`// to be described more`\nIt either may be initialized with `String` or unlimited amount of `AttributedString`s\n```swift\nUText(\"hello 👋 \")\nUText().text(\"hello\") // useful if declare label in extension like below\nUText.mySuperLabel.text(\"hello\")\nUText(\"hello\".foreground(.red), \"world\".foreground(.green))\n```\nset some font from declared identifiers or with system fonts\n```swift\nUText(\"hello\").font(v: .systemFont(ofSize: 15))\nUText(\"hello\").font(.sfProBold, 15)\n```\nset text color\n```swift\nUText(\"hello\").color(.red)\n```\nset text alignment\n```swift\nUText(\"hello\").alignment(.center)\n```\nset amount of lines\n```swift\nUText(\"hello\").lines(1)\nUText(\"hello\\nworld\").lines(0)\nUText(\"hello\\nworld\").lines(2)\nUText(\"hello\\nworld\").multiline()\n```\n\nDeclare custom attributed labels like this\n```swift\nimport UIKitPlus\n\nextension UText {\n    static var welcomeLogo: UText {\n        UText(\"My\".foreground(.white).font(.sfProBold, 26), \"App\".font(.sfProBold, 26))\n    }\n}\n```\nand then use them like this\n```swift\nlet logo = UText.welcomeLogo.centerInSuperview()\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eTextView\u003c/summary\u003e\n\n\u003e alias is `UTextView`\n\n`// implemented. to be described`\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eToggle\u003c/summary\u003e\n\n\u003e alias is `UToggle`\n\n`// implemented. to be described`\n\u003c/details\u003e\n\n#### Properties\n\nAll the properties are available to be set declaratively and can be binded to `UState`.\n\nA lot of layer properties are available directly and have convenient initializers.\n\n\u003cdetails\u003e\n\u003csummary\u003eAlpha\u003c/summary\u003e\n\n```swift\nUView().alpha(0)\nUView().alpha($alphaState)\nUView().alpha($boolState.map { $0 ? 1 : 0 })\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eBackground\u003c/summary\u003e\n\n```swift\nUView().background(.red)\nUView().background(0xff0000)\nUView().background($colorState)\nUView().background($boolState.map { $0 ? .red : .green })\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eBorders\u003c/summary\u003e\n\nTo set border on all sides\n```swift\nUView().border(1, .black)\nUView().border(1, 0x000)\n```\nTo set border on specific side\n```swift\nUView().border(.top, 1, .black)\nUView().border(.left, 1, .black)\nUView().border(.right, 1, .black)\nUView().border(.bottom, 1, .black)\n```\nTo remove border from specific side\n```swift\n.removeBorder(.top)\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eBounds\u003c/summary\u003e\n\n```swift\n// implemented. to be described\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eCompression Resistance\u003c/summary\u003e\n\n```swift\n// implemented. to be described\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eCorners\u003c/summary\u003e\n\nTo set radius to all corners\n```swift\nUView().corners(10)\nUView().corners($cornerRadiusState)\n```\nTo set custom radius for specific corner\n```swift\nUView().corners(10, .topLeft, .topRight)\nUView().corners(10, .topLeft, .bottomRight)\nUView().corners(10, .topLeft, .topRight, .bottomLeft, .bottomRight)\n```\nTo make your view's corners round automatically by smaller side\n```swift\nUView().circle()\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eHidden\u003c/summary\u003e\n\n```swift\nUView().hidden() // will set `true` by default\nUView().hidden(true)\nUView().hidden(false)\nUView().hidden($hiddenState)\nUView().hidden($stringState.map { $0.count \u003e 0 })\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eHugging Priority\u003c/summary\u003e\n\n```swift\n// implemented. to be described\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eItself\u003c/summary\u003e\n\n```swift\n// implemented. to be described\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eLayout Margin\u003c/summary\u003e\n\n```swift\n// to all sides\nUView().layoutMargin(10)\n// optional sides\nUView().layoutMargin(top: 10)\nUView().layoutMargin(left: 10, bottom: 5)\nUView().layoutMargin(top: 10, right: 5)\n// vertical and horizontal\nUView().layoutMargin(x: 10, y: 5) // top: 5, left: 10, right: 10, bottom: 5\nUView().layoutMargin(x: 10) // left: 10, right: 10\nUView().layoutMargin(y: 5) // top: 5, bottom: 5\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eFocus to next responder or resign\u003c/summary\u003e\n\n```swift\n// implemented. to be described\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eOpacity\u003c/summary\u003e\n\n```swift\nUView().opacity(0)\nUView().opacity($alphaState)\nUView().opacity($boolState.map { $0 ? 1 : 0 })\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eRasterize\u003c/summary\u003e\n\nTo rasterize layer, e.g. for better shadow performance\n```swift\nUView().rasterize() // true by default\nUView().rasterize(true)\nUView().rasterize(false)\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eShadow\u003c/summary\u003e\n\n```swift\n// to be described more\n\n// and with mroe than one shadow\n// and with state, expressableState\n```\n```swift\nUView().shadow() // by default it's black, opacity 1, zero offset, radius 10\nUView().shadow(.gray, opacity: 0.8, offset: .zero, radius: 5)\nUView().shadow(0x000000, opacity: 0.8, offset: .zero, radius: 5)\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eShake\u003c/summary\u003e\n\nYou can shake any view just by calling\n```swift\nUView().shake()\n```\nAnd you could customize shake effect\n```swift\nUView().shake(values: [-20, 20, -20, 20, -10, 10, -5, 5, 0],\n              duration: 0.6,\n              axis: .horizontal,\n              timing: .easeInEaseOut)\nUView().shake(-20, 20, -20, 20, -10, 10, -5, 5, 0,\n              duration: 0.6,\n              axis: .horizontal,\n              timing: .easeInEaseOut)\n```\nor even create an extension\n```swift\nimport UIKitPlus\n\nextension DeclarativeProtocol {\n  func myShake() {\n      UView().shake(-20, 20, -20, 20, -10, 10, -5, 5, 0,\n                    duration: 0.6,\n                    axis: .horizontal,\n                    timing: .easeInEaseOut)\n  }\n}\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eTag\u003c/summary\u003e\n\n```swift\nUView().tag(0)\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eTint\u003c/summary\u003e\n\n```swift\nUView().tint(.red)\nUView().tint(0xff0000)\nUView().tint($colorState)\nUView().tint($boolState.map { $0 ? .red : .green })\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eUser Interaction\u003c/summary\u003e\n\n```swift\n// implemented. to be described\n```\n\u003c/details\u003e\n\n# Examples\n\n[Example app is here](https://github.com/MihaelIsaev/UIKitPlusExample)\n\n## Example 1\n```swift\nimport UIKitPlus\n\nclass MyViewController: ViewController {\n    lazy var view1 = UView()\n\n    override func buildUI() {\n        super.buildUI()\n        body {\n            view1.background(.black).size(100).centerInSuperview()\n            UView().background(.red).size(30, 20).centerXInSuperview().top(to: .bottom, of: view1, 16)\n        }\n    }\n}\n```\n## Example 2\n```swift\nimport UIKitPlus\n\n// Just feel how easy you could build \u0026 declare your views\n// with all needed constraints, properties and actions\n// even before adding them to superview!\nclass LoginViewController: ViewController {\n    @UState var email = \"\"\n    @UState var password = \"\"\n\n    override func buildUI() {\n        super.buildUI()\n        view.backgroundColor = .black\n        body {\n            UButton.back.onTapGesture { print(\"back tapped\") }\n            UText.welcome.text(\"Welcome\").centerXInSuperview().topToSuperview(62, safeArea: true)\n            UVStack {\n                UTextField.welcome.text($email).placeholder(\"Email\").keyboard(.emailAddress).content(.emailAddress)\n                UTextField.welcome.text($password).placeholder(\"Password\").content(.password).secure()\n                UView().height(10) // just to add extra space\n                UButton.bigBottomGreen.title(\"Sign In\").onTapGesture(signIn)\n            }.edgesToSuperview(top: 120, leading: 16, trailing: -16)\n        }\n    }\n\n    func signIn() {\n        // do an API call to your server with awesome CodyFire lib 😉\n    }\n}\n```\nAnd you just need a few extensions to make it work\n```swift\n// PRO-TIP:\n// To avoid mess declare reusable views in extensions like this\nextension FontIdentifier {\n    static var sfProRegular = FontIdentifier(\"SFProDisplay-Regular\")\n    static var sfProMedium = FontIdentifier(\"SFProDisplay-Medium\")\n}\nextension UText {\n    static var title: UText { UText().color(.white).font(.sfProMedium, 18) }\n}\nextension UTextField {\n    static var welcome: UTextField {\n        UTextField()\n            .height(40)\n            .background(.clear)\n            .color(.black)\n            .tint(.mainGreen)\n            .border(.bottom, 1, .gray)\n            .font(.sfProRegular, 16)\n    }\n}\nextension UButton {\n    static var back: UButton { UButton(\"backIcon\").topToSuperview(64).leadingToSuperview(24) }\n    static var bigBottomGreen: UButton {\n        UButton()\n            .color(.white)\n            .font(.sfProMedium, 15)\n            .background(.green)\n            .height(50)\n            .circle()\n            .shadow(.gray, opacity: 1, offset: .init(width: 0, height: -1), radius: 10)\n    }\n}\n\n// PRO-TIP2:\n// I'd suggest you to use extensions for everything: fonts, images, labels, buttons, colors, etc.\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmihaelisaev%2Fuikitplus","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmihaelisaev%2Fuikitplus","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmihaelisaev%2Fuikitplus/lists"}