{"id":15288692,"url":"https://github.com/bestyun/flexlayoutkit","last_synced_at":"2025-10-05T20:26:22.238Z","repository":{"id":198644140,"uuid":"696580976","full_name":"BestYun/FlexLayoutKit","owner":"BestYun","description":"Flexbox in Swift,like SwiftUI and Flutter","archived":false,"fork":false,"pushed_at":"2024-04-17T09:58:33.000Z","size":328,"stargazers_count":13,"open_issues_count":1,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-10T00:45:47.782Z","etag":null,"topics":["declarative-ui","flex","flexbox","flexbox-css","flutter","swift","swiftui","uikit","yoga","yogakit"],"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/BestYun.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}},"created_at":"2023-09-26T03:16:06.000Z","updated_at":"2024-12-24T00:56:20.000Z","dependencies_parsed_at":null,"dependency_job_id":"e703ae1a-f6a0-4039-9793-7b817a233236","html_url":"https://github.com/BestYun/FlexLayoutKit","commit_stats":null,"previous_names":["bestyun/flexboxuikit","bestyun/flexlayoutkit"],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BestYun%2FFlexLayoutKit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BestYun%2FFlexLayoutKit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BestYun%2FFlexLayoutKit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BestYun%2FFlexLayoutKit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/BestYun","download_url":"https://codeload.github.com/BestYun/FlexLayoutKit/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248681491,"owners_count":21144700,"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":["declarative-ui","flex","flexbox","flexbox-css","flutter","swift","swiftui","uikit","yoga","yogakit"],"created_at":"2024-09-30T15:52:06.377Z","updated_at":"2025-10-05T20:26:22.153Z","avatar_url":"https://github.com/BestYun.png","language":"Swift","readme":"# FlexLayoutKit\n\n![Swift](https://img.shields.io/badge/Swift-5.4-orange.svg)\n[![CocoaPods](http://img.shields.io/cocoapods/v/FlexLayoutKit.svg)](https://cocoapods.org/pods/FlexLayoutKit)\n\n\n基于[facebook/yoga](https://github.com/facebook/yoga)实现一个类似swiftui和Flutter的声明式UI框架\n\n### Requirements\n* iOS 10.0+\n* Xcode 12.5\n* Swift 5.4\n\n### Installation\n---\n#### Cocoapods\n```\npod 'FlexLayoutKit', '~\u003e 0.5'\n以下可选\npod 'FlexLayoutKit/SDWebImage'\npod 'FlexLayoutKit/Kingfisher' #需要ios 12以上\n\n```\n\n### 特性\n- [x] FlexBox布局\n- [x] 声明式语法,类似**SwiftUI**,如**HStackView**、**VStackView**、**ZStackView**，类似Flutter中的**Row**、**Column**、**Stack**、**Wrap**\n- [x] 自动计算**UITableViewCell** 高度\n- [x] 支持**VScrollView**、**HScrollView**,自动计算**contentSize**\n- [x] 使用**Wrap**轻松实现流式布局,超过屏幕时会自动换行\n- [x] **Forin**和**if else** DSL支持\n- [x] 数据驱动UI,更新数据后自动会更新UI\n- [x] 支持百分比\n- [x] 链式语法\n\n### Usage 用法\n#### Quick Start 快速开始\n```swift\nimport FlexLayoutKit  //1.导入FlexLayoutKit\nimport UIKit\n\n//2.继承FlexboxBaseViewController\nclass ViewController: FlexboxBaseViewController \n{\n    override func viewDidLoad() {\n        super.viewDidLoad()\n\n        view.flex\n            .mainAxis(.center)\n            .crossAxis(.center)\n            .addItems(subviews: bodyView())\n    }\n\n    @FlexboxViewBuilder func bodyView() -\u003e [FlexboxView] {\n        Text(\"Hello FlexLayoutKit\")\n    }\n}\n\n```\n\nor\n```swift\nimport FlexLayoutKit\n\nclass ViewController: UIViewController{\n    override func viewDidLoad() {\n        super.viewDidLoad()\n        view.flex.mainAxis(.center).crossAxis(.center).addItems {\n            HStackView(mainAxis: .center, crossAxis: .center) {\n                Text(\"Hello FlexLayoutKit\")\n            }\n        }\n    }\n    \n    override func viewDidLayoutSubviews() {\n        super.viewDidLayoutSubviews()\n        view.flex.applyLayout()\n    }\n    \n}\n```\n\n#### example1\n\u003cimg src=\"https://github.com/BestYun/FlexLayoutKit/blob/main/doc_imgs/example1.png\" /\u003e\n\n```swift\nHStackView {\n    ZStackView {\n        ImageView()\n            .backgroundColor(UIColor.gray.withAlphaComponent(0.5))\n            .cornerRadius(8)\n            .left(0)\n            .bottom(0)\n            .size(width: 50, height: 50)\n       \n        Text(\"1\")\n            .fontSize(12)\n            .textColor(.white)\n            .right(0)\n            .top(0)\n            .size(16)\n            .cornerRadius(8)\n            .backgroundColor(.red)\n            .textAlignment(.center)\n    }\n    .size(58)\n    .margin(.right, 8)\n   \n    VStackView(mainAxis: .spaceAround) {\n        HStackView(crossAxis: .center) {\n            Text(\"Leo\")\n                .fontSize(16, weight: .bold)\n                .expanded()\n            Text(\"13:30\")\n                .fontSize(12, weight: .medium)\n                .textColor(.gray)\n        }\n\n        Text(\"hello,nice to meet you\")\n    }\n    .height(50)\n    .expanded()\n    .margin(.top, 8)\n}\n.padding(.horizontal, 15)\n.margin(.top, 100)\n\n```\n\n#### HStackView使用\n```swift\nHStackView {\n    ImageView().size(40).cornerRadius(10).backgroundColor(.gray.withAlphaComponent(0.2))\n    Spacer(10)\n    Text(\"Leo\").textColor(.orange).fontSize(16,weight: .medium)\n}\n```\n#### VStackView使用\n```swift\nVStackView(crossAxis: .center) {\n    ImageView().size(40).cornerRadius(10).backgroundColor(.gray.withAlphaComponent(0.2))\n    Spacer(10)\n    Text(\"Leo\").textColor(.orange).fontSize(16,weight: .medium)\n}\n```\n#### ZStackView使用\n```swift\nZStackView {\n    FlexContainer(mainAxis: .center, crossAxis: .center){\n        Text(\"99\")\n    }\n    .cornerRadius(15)\n    .backgroundColor(.red)\n    .top(0)\n    .right(0)\n    .size(30)\n}\n.size(100)\n.backgroundColor(.orange)\n```\n\n#### Wrap用法\n```swift\nlet tags = [\"tag1\",\"tag2\",\"tag3\",\"tag4\",\"tag5\",\"tag6\",\"tag7\",\"tag8\",\"tag9\"]\n//gap 是行间距和列间距简写\nWrap(gap: 10){\n    for tag in tags {\n        Text(tag)\n            .backgroundColor(.gray.withAlphaComponent(0.5))\n            .textAlignment(.center)\n            .cornerRadius(15)\n            .padding(.horizontal,10)\n            .height(30)\n            .onTap {\n                print(tag)\n            }\n    }\n}\n\n```\n\n#### ForIn用法\n```swift\nVScrollView {\n    for i in 0...100 {\n        FlexContainer(mainAxis: .center, crossAxis: .center) {\n            Text(\"\\(i)\")\n        }\n        .height(60)\n        .backgroundColor(.orange.withAlphaComponent(0.1))\n        .margin(.vertical,5)\n    }\n}\n```\n\n#### if else用法\n```swift\nlet state = true\nHStackView {\n    if state {\n        Text(\"true\")\n    }else{\n        Text(\"false\")\n    }\n}\n```\n\n#### @UState使用\n```swift\n@UState var count: String = \"count\"\nvar step: Int = 0 {\n    didSet{\n        count = \"count = \\(step)\"\n    }\n}\n\nVStackView(mainAxis: .center, crossAxis: .center) {\n    Text($count).textColor(.black)\n    Button(\"add\").margin(.top,10).backgroundColor(.blue).onTap { [unowned self] in\n        self.step = self.step + 1\n        //修改内容后,要重新布局\n        self.updateFlexLayout()\n    }\n}\n\n```\n#### 百分比\n```swift\nText(\"FlexPercent\").backgroundColor(.orange).width(20%).height(20%)\n\n```\n\n#### 自动计算UITableViewCell 动态高度\n```swift\n1)cell继承ListCell，并设置isDynamicHeight值为true\n\nclass CellItem: ListCell {\n    \n    override var isDynamicHeight: Bool { true }\n    @FlexboxViewBuilder  func bodyView() -\u003e [FlexboxView] {\n        return VStackView {\n            ...\n        }\n    }\n}\n\n2)UITableView的rowHeight设置为UITableView.automaticDimension\nUITableView().flex.expanded().apply {\n    $0.delegate = self\n    $0.dataSource = self\n    $0.register(CellItem.self, forCellReuseIdentifier: \"cellID\")\n    $0.rowHeight = UITableView.automaticDimension\n}\n\n```\n\n\n#### 自动计算UICollectionViewCell 动态高度\n```swift\n1)cell继承GridCell，并设置isDynamicHeight值为true\n\nprivate class FCollectionCell: GridCell {\n    override var isDynamicHeight: Bool { true }\n   @UState  var text: String?\n    \n    override init(frame: CGRect) {\n        super.init(frame: frame)\n        contentView.backgroundColor = .darkGray\n        \n    }\n\n    override func bodyView() -\u003e FlexboxView {\n        Text($text)\n            .fontSize(18)\n            .textColor(.orange)\n            .backgroundColor(.gray)\n            .numberOfLines(0)\n    }\n    \n    @available(*, unavailable)\n    required init?(coder: NSCoder) {\n        fatalError(\"init(coder:) has not been implemented\")\n    }\n}\n\n\n2)UICollectionViewFlowLayout设置estimatdItemSize设置一个非0值开启自动计算高度\n    lazy var layout = UICollectionViewFlowLayout().then { layout in\n        layout.minimumLineSpacing = 10\n        layout.minimumInteritemSpacing = 10\n        //estimatdItemSize设置一个非0值开启自动计算高度，宽度要固定一个值，高度设置预估值\n        layout.estimatedItemSize = CGSize(width: UIScreen.main.bounds.width - 10*2, height: 100)\n        layout.itemSize = UICollectionViewFlowLayout.automaticSize \n    }\n\n```\n\n\n\n#### 自动计算UICollectionViewCell 动态宽度\n```swift\n1)cell继承GridCell，并设置isDynamicHeight值为true，同时将scrollDirection设置为.horizontal\n\nprivate class FCollectionCell: GridCell {\n    override var isDynamicHeight: Bool { true }\n    override var scrollDirection: UICollectionView.ScrollDirection { .horizontal }\n    @UState var text: String?\n                                   \n    override init(frame: CGRect) {\n        super.init(frame: frame)\n    }\n\n    override func bodyView() -\u003e FlexboxView {\n        Text($text).expanded().backgroundColor(.orange).cornerRadius(10).padding(.horizontal,20)\n    }\n                                      \n    @available(*, unavailable)\n    required init?(coder: NSCoder) {\n        fatalError(\"init(coder:) has not been implemented\")\n    }\n}\n\n\n\n2)UICollectionViewFlowLayout设置estimatdItemSize设置一个非0值开启自动计算宽度\n    lazy var layout = UICollectionViewFlowLayout().then { layout in\n        layout.minimumLineSpacing = 10\n        layout.minimumInteritemSpacing = 10\n        //estimatdItemSize设置一个非0值开启自动计算宽度，高度要固定一个值，宽度设置预估值\n        layout.estimatedItemSize = CGSize(width: 10, height: 80)\n        layout.itemSize = UICollectionViewFlowLayout.automaticSize \n    }\n\n```\n\n\n#### Modifier chain 链式语法\n```swift\nUILabel()\n    .modifier\n    .text(\"链式语法\")\n    .textColor(.orange)\n    .font(.systemFont(ofSize: 16))\n    \n```\n等同于\n```swift\nlet label = UILabel()\nlabel.text = \"test apply\"\nlabel.font = .systemFont(ofSize: 16)\nlabel.textColor = .orange\n```\n\n#### apply sugar \n\u003e 只在UIView有效\n```swift\nUIView(frame: CGRect(x: 10, y: 100, width: 60, height: 60)).apply {\n    $0.backgroundColor = .blue\n    $0.layer.cornerRadius = 30\n    $0.clipsToBounds = true\n}\n\nUILabel().apply { label in\n    label.text = \"test apply\"\n    label.font = .systemFont(ofSize: 16)\n    label.textColor = .orange\n}\n\n```\n等同于\n```swift\nlet blueView = UIView(frame: CGRect(x: 10, y: 100, width: 60, height: 60))\nblueView.backgroundColor = .blue\nblueView.layer.cornerRadius = 30\nblueView.clipsToBounds = true\n\n\nlet label = UILabel()\nlabel.text = \"test apply\"\nlabel.font = .systemFont(ofSize: 16)\nlabel.textColor = .orange\n```\n---\n### flexbox布局参考资料\n* [reactnative文档中的flexbox](https://reactnative.cn/docs/flexbox)\n* [yoga-布局引擎]( https://tbfungeek.github.io/2019/11/05/%E5%9C%A8%E9%A1%B9%E7%9B%AE%E4%B8%AD%E4%BD%BF%E7%94%A8Yoga-%E5%B8%83%E5%B1%80%E5%BC%95%E6%93%8E/)\n* [mozilla CSS_flexible_box_layout](https://developer.mozilla.org/zh-CN/docs/Web/CSS/CSS_flexible_box_layout/Basic_concepts_of_flexbox)\n* [阮一峰 Flex 布局教程：语法篇\n](https://www.ruanyifeng.com/blog/2015/07/flex-grammar.html)\n\n### FlexBox布局\n\u003cimg src=\"https://github.com/BestYun/FlexLayoutKit/blob/main/doc_imgs/flex_terms.png\" /\u003e\n\n* 主轴方向\n* 布局方向 ltr,rtl\n* 主轴方向子项分布 mainAxis\n* 次轴方向子项分布 crossAxis\n* 次轴方向多行子项分布 \n* 子项自身分布\n\n* flexbox文档\n    * justifyContent\n    * alignContent\n    * alignItems\n    * alignSelf\n    * flexDirection\n    * direction\n    * flexWrap\n    * position\n    \n### API\n* margin padding left right top bottom \n* size width height minWidth\n* flex 属性\n* applyLayout\n* markDirty\n* sizeThatFits\n* numberOfChildren\n* isIncludedInLayout\n* enabled\n* display\n\n### UI\n* HStackView = Row\n* VStackView = Column\n* ZStackView = Stack 与Flutter和SwiftUI有差异,需要自己定义好size才有效果\n* Wrap\n* Text\n* ImageView\n* Space\n* TextField\n* TextView\n* ScrollView\n    * VScrollView\n    * HScrollView\n* ListCell = UITableViewCell\n* GridCell = UICollectionViewCell\n\n\n\n## Flex makeLayout\n\u003e 对于没有第二次封装的UIVIew,可以使用以下方法进行布局\n\n```swift\nUILabel().flex.makeLayout {\n    $0.margin(.left, 10).margin(.top, 100)\n}.apply {\n    _ = $0.modifier\n        .text(\"flex.makeLayout写法\")\n        .font(.systemFont(ofSize: 18))\n        .textColor(.orange)\n}\n```\n\n#### 动画\n```\nvar blowUp = false\nlet boxView = FlexContainer()\n\nVStackView(mainAxis: .center, crossAxis: .center) {\n    boxView.flex.size(100).modifier.backgroundColor(.blue)\n    \n    Button(\"动画\").size(width: 100, height: 30)\n        .backgroundColor(.orange).margin(.top,10)\n        .onTap { [unowned self] in\n            UIView.animate(withDuration: 0.25, delay: 0) {\n                self.boxView.flex.size(self.blowUp ? 200 : 100)\n                self.updateFlexLayout()\n                self.blowUp = !self.blowUp\n            }\n        }\n    \n}\n```\n\n## 更新内容\n\n\n\n\n### Todo\n* 双向绑定\n* UITableView封装 ListView ListItem\n    * 参考 https://github.com/josercc/SwiftTableViewGroup\n* UICollection封装 GridView H V\n* 瀑布流\n* 测试\n* 支持SPM\n\n## 参考\n* https://github.com/MihaelIsaev/UIKitPlus\n* https://blog.eppz.eu/declarative-uikit-with-10-lines-of-code/\n* https://github.com/hmlongco/RxSwiftWidgets\n* https://tech.youzan.com/-sheng-ming-shi-uikitzai-you-zan-mei-ye-de-shi-jian/\n* https://github.com/sakiyamaK/DeclarativeUIKit\n* https://github.com/hmlongco/Builder/tree/main\n* https://github.com/hainayanda/Draftsman\n* https://github.com/nicklockwood/layout  xml实现布局\n* https://kazaimazai.com/swifty-uikit/\n* https://github.com/KazaiMazai/SwiftyUIKit\n* https://github.com/zhenglibao/FlexLib\n* https://github.com/layoutBox/FlexLayout\n* https://github.com/pujiaxin33/StackUI\n\n## License\n**FlexLayoutKit** is under MIT license. See the [LICENSE](LICENSE) file for more info.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbestyun%2Fflexlayoutkit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbestyun%2Fflexlayoutkit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbestyun%2Fflexlayoutkit/lists"}