Git Product home page Git Product logo

flexlayoutkit's Introduction

FlexLayoutKit

Swift CocoaPods

基于facebook/yoga实现一个类似swiftui和Flutter的声明式UI框架

Requirements

  • iOS 10.0+
  • Xcode 12.5
  • Swift 5.4

Installation


Cocoapods

pod 'FlexLayoutKit', '~> 0.5'
以下可选
pod 'FlexLayoutKit/SDWebImage'
pod 'FlexLayoutKit/Kingfisher' #需要ios 12以上

特性

  • FlexBox布局
  • 声明式语法,类似SwiftUI,如HStackViewVStackViewZStackView,类似Flutter中的RowColumnStackWrap
  • 自动计算UITableViewCell 高度
  • 支持VScrollViewHScrollView,自动计算contentSize
  • 使用Wrap轻松实现流式布局,超过屏幕时会自动换行
  • Forinif else DSL支持
  • 数据驱动UI,更新数据后自动会更新UI
  • 支持百分比
  • 链式语法

Usage 用法

Quick Start 快速开始

import FlexLayoutKit  //1.导入FlexLayoutKit
import UIKit

//2.继承FlexboxBaseViewController
class ViewController: FlexboxBaseViewController 
{
    override func viewDidLoad() {
        super.viewDidLoad()

        view.flex
            .mainAxis(.center)
            .crossAxis(.center)
            .addItems(subviews: bodyView())
    }

    @FlexboxViewBuilder func bodyView() -> [FlexboxView] {
        Text("Hello FlexLayoutKit")
    }
}

or

import FlexLayoutKit

class ViewController: UIViewController{
    override func viewDidLoad() {
        super.viewDidLoad()
        view.flex.mainAxis(.center).crossAxis(.center).addItems {
            HStackView(mainAxis: .center, crossAxis: .center) {
                Text("Hello FlexLayoutKit")
            }
        }
    }
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        view.flex.applyLayout()
    }
    
}

example1

HStackView {
    ZStackView {
        ImageView()
            .backgroundColor(UIColor.gray.withAlphaComponent(0.5))
            .cornerRadius(8)
            .left(0)
            .bottom(0)
            .size(width: 50, height: 50)
       
        Text("1")
            .fontSize(12)
            .textColor(.white)
            .right(0)
            .top(0)
            .size(16)
            .cornerRadius(8)
            .backgroundColor(.red)
            .textAlignment(.center)
    }
    .size(58)
    .margin(.right, 8)
   
    VStackView(mainAxis: .spaceAround) {
        HStackView(crossAxis: .center) {
            Text("Leo")
                .fontSize(16, weight: .bold)
                .expanded()
            Text("13:30")
                .fontSize(12, weight: .medium)
                .textColor(.gray)
        }

        Text("hello,nice to meet you")
    }
    .height(50)
    .expanded()
    .margin(.top, 8)
}
.padding(.horizontal, 15)
.margin(.top, 100)

HStackView使用

HStackView {
    ImageView().size(40).cornerRadius(10).backgroundColor(.gray.withAlphaComponent(0.2))
    Spacer(10)
    Text("Leo").textColor(.orange).fontSize(16,weight: .medium)
}

VStackView使用

VStackView(crossAxis: .center) {
    ImageView().size(40).cornerRadius(10).backgroundColor(.gray.withAlphaComponent(0.2))
    Spacer(10)
    Text("Leo").textColor(.orange).fontSize(16,weight: .medium)
}

ZStackView使用

ZStackView {
    FlexContainer(mainAxis: .center, crossAxis: .center){
        Text("99")
    }
    .cornerRadius(15)
    .backgroundColor(.red)
    .top(0)
    .right(0)
    .size(30)
}
.size(100)
.backgroundColor(.orange)

Wrap用法

let tags = ["tag1","tag2","tag3","tag4","tag5","tag6","tag7","tag8","tag9"]
//gap 是行间距和列间距简写
Wrap(gap: 10){
    for tag in tags {
        Text(tag)
            .backgroundColor(.gray.withAlphaComponent(0.5))
            .textAlignment(.center)
            .cornerRadius(15)
            .padding(.horizontal,10)
            .height(30)
            .onTap {
                print(tag)
            }
    }
}

ForIn用法

VScrollView {
    for i in 0...100 {
        FlexContainer(mainAxis: .center, crossAxis: .center) {
            Text("\(i)")
        }
        .height(60)
        .backgroundColor(.orange.withAlphaComponent(0.1))
        .margin(.vertical,5)
    }
}

if else用法

let state = true
HStackView {
    if state {
        Text("true")
    }else{
        Text("false")
    }
}

@UState使用

@UState var count: String = "count"
var step: Int = 0 {
    didSet{
        count = "count = \(step)"
    }
}

VStackView(mainAxis: .center, crossAxis: .center) {
    Text($count).textColor(.black)
    Button("add").margin(.top,10).backgroundColor(.blue).onTap { [unowned self] in
        self.step = self.step + 1
        //修改内容后,要重新布局
        self.updateFlexLayout()
    }
}

百分比

Text("FlexPercent").backgroundColor(.orange).width(20%).height(20%)

自动计算UITableViewCell 动态高度

1)cell继承ListCell,并设置isDynamicHeight值为true

class CellItem: ListCell {
    
    override var isDynamicHeight: Bool { true }
    @FlexboxViewBuilder  func bodyView() -> [FlexboxView] {
        return VStackView {
            ...
        }
    }
}

2)UITableView的rowHeight设置为UITableView.automaticDimension
UITableView().flex.expanded().apply {
    $0.delegate = self
    $0.dataSource = self
    $0.register(CellItem.self, forCellReuseIdentifier: "cellID")
    $0.rowHeight = UITableView.automaticDimension
}

自动计算UICollectionViewCell 动态高度

1)cell继承GridCell,并设置isDynamicHeight值为true

private class FCollectionCell: GridCell {
    override var isDynamicHeight: Bool { true }
   @UState  var text: String?
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        contentView.backgroundColor = .darkGray
        
    }

    override func bodyView() -> FlexboxView {
        Text($text)
            .fontSize(18)
            .textColor(.orange)
            .backgroundColor(.gray)
            .numberOfLines(0)
    }
    
    @available(*, unavailable)
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}


2)UICollectionViewFlowLayout设置estimatdItemSize设置一个非0值开启自动计算高度
    lazy var layout = UICollectionViewFlowLayout().then { layout in
        layout.minimumLineSpacing = 10
        layout.minimumInteritemSpacing = 10
        //estimatdItemSize设置一个非0值开启自动计算高度,宽度要固定一个值,高度设置预估值
        layout.estimatedItemSize = CGSize(width: UIScreen.main.bounds.width - 10*2, height: 100)
        layout.itemSize = UICollectionViewFlowLayout.automaticSize 
    }

自动计算UICollectionViewCell 动态宽度

1)cell继承GridCell,并设置isDynamicHeight值为true,同时将scrollDirection设置为.horizontal

private class FCollectionCell: GridCell {
    override var isDynamicHeight: Bool { true }
    override var scrollDirection: UICollectionView.ScrollDirection { .horizontal }
    @UState var text: String?
                                   
    override init(frame: CGRect) {
        super.init(frame: frame)
    }

    override func bodyView() -> FlexboxView {
        Text($text).expanded().backgroundColor(.orange).cornerRadius(10).padding(.horizontal,20)
    }
                                      
    @available(*, unavailable)
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}



2)UICollectionViewFlowLayout设置estimatdItemSize设置一个非0值开启自动计算宽度
    lazy var layout = UICollectionViewFlowLayout().then { layout in
        layout.minimumLineSpacing = 10
        layout.minimumInteritemSpacing = 10
        //estimatdItemSize设置一个非0值开启自动计算宽度,高度要固定一个值,宽度设置预估值
        layout.estimatedItemSize = CGSize(width: 10, height: 80)
        layout.itemSize = UICollectionViewFlowLayout.automaticSize 
    }

Modifier chain 链式语法

UILabel()
    .modifier
    .text("链式语法")
    .textColor(.orange)
    .font(.systemFont(ofSize: 16))
    

等同于

let label = UILabel()
label.text = "test apply"
label.font = .systemFont(ofSize: 16)
label.textColor = .orange

apply sugar

只在UIView有效

UIView(frame: CGRect(x: 10, y: 100, width: 60, height: 60)).apply {
    $0.backgroundColor = .blue
    $0.layer.cornerRadius = 30
    $0.clipsToBounds = true
}

UILabel().apply { label in
    label.text = "test apply"
    label.font = .systemFont(ofSize: 16)
    label.textColor = .orange
}

等同于

let blueView = UIView(frame: CGRect(x: 10, y: 100, width: 60, height: 60))
blueView.backgroundColor = .blue
blueView.layer.cornerRadius = 30
blueView.clipsToBounds = true


let label = UILabel()
label.text = "test apply"
label.font = .systemFont(ofSize: 16)
label.textColor = .orange

flexbox布局参考资料

FlexBox布局

  • 主轴方向

  • 布局方向 ltr,rtl

  • 主轴方向子项分布 mainAxis

  • 次轴方向子项分布 crossAxis

  • 次轴方向多行子项分布

  • 子项自身分布

  • flexbox文档

    • justifyContent
    • alignContent
    • alignItems
    • alignSelf
    • flexDirection
    • direction
    • flexWrap
    • position

API

  • margin padding left right top bottom
  • size width height minWidth
  • flex 属性
  • applyLayout
  • markDirty
  • sizeThatFits
  • numberOfChildren
  • isIncludedInLayout
  • enabled
  • display

UI

  • HStackView = Row
  • VStackView = Column
  • ZStackView = Stack 与Flutter和SwiftUI有差异,需要自己定义好size才有效果
  • Wrap
  • Text
  • ImageView
  • Space
  • TextField
  • TextView
  • ScrollView
    • VScrollView
    • HScrollView
  • ListCell = UITableViewCell
  • GridCell = UICollectionViewCell

Flex makeLayout

对于没有第二次封装的UIVIew,可以使用以下方法进行布局

UILabel().flex.makeLayout {
    $0.margin(.left, 10).margin(.top, 100)
}.apply {
    _ = $0.modifier
        .text("flex.makeLayout写法")
        .font(.systemFont(ofSize: 18))
        .textColor(.orange)
}

动画

var blowUp = false
let boxView = FlexContainer()

VStackView(mainAxis: .center, crossAxis: .center) {
    boxView.flex.size(100).modifier.backgroundColor(.blue)
    
    Button("动画").size(width: 100, height: 30)
        .backgroundColor(.orange).margin(.top,10)
        .onTap { [unowned self] in
            UIView.animate(withDuration: 0.25, delay: 0) {
                self.boxView.flex.size(self.blowUp ? 200 : 100)
                self.updateFlexLayout()
                self.blowUp = !self.blowUp
            }
        }
    
}

更新内容

Todo

参考

License

FlexLayoutKit is under MIT license. See the LICENSE file for more info.

flexlayoutkit's People

Contributors

bestyun avatar

Stargazers

Mezhevikin Alexey avatar  avatar  avatar ZhouJiatao avatar Siren avatar  avatar 唐朝健 avatar 闲人 avatar

Watchers

 avatar

Forkers

tangchaojian

flexlayoutkit's Issues

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.