ios UI布局篇

说实话,经历过Duilibvertical,horizonal布局和AndroidLinerLayoutRelativeLayout的布局之后,初入IOS 布局篇,只能说“蛋疼”,UI布局太累了。但是不管怎么样,坑已入,那么就必须鼓足勇气去填坑了。

下面是自己封装的swift版本的vertical,horizonal布局,原理同Duilib布局

使用须知:同duilib的布局原理
绝对布局:如果不指定高度和宽度的话子控件默认是占用该布局高度和宽度,如果有多个控件则采取覆盖式操作。
水平布局UIHorizontalView,子控件需设置宽度或者weight,如果不设置则默认weight=1,系统将根据设置的值和比重进行调整控件的宽度。高度可选,如果子控件没有设置高度,则默认占据整个父空间的高度
垂直布局UIVerticalView,子控件需设置高度或者weight,如果不设置则默认weight=1,系统将根据设置的值和比重进行调整控件的高度。宽度可选,如果子控件没有设置宽度,则默认占据整个父空间的宽度
水平滚动布局UIHScrollView,该控件支持显示大于屏幕宽度的内容。需要精准的设置每一个子控件的宽度,从而精准定位内容展示所需的宽度
垂直滚动布局UIVScrollView,该控件支持显示大于屏幕高度的内容。需要精准的设置每一个子控件的高度,从而精准定位内容展示所需的高度
以上所有扩展属性均以lv_开头

View扩展属性设置方法,以下属性只在以上布局中生效

lv_set_width:设置view的宽度
lv_get_width:获取view的宽度
lv_set_height:设置view的高度
lv_get_height:获取view的高度
lv_set_weight:设置view的比重
lv_get_weight:获取view的比重
lv_set_marign:设置view的边距
lv_get_marign:获取view的边距

下面以在UIHorizontalView居中显示一张图片为例:

let uiHorizonView = UIHorizontalView()

let uiImageView = UIImageView()
//uiImageView.lv_set_width(value:200)
uiImageView.lv_set_weight(value:2)

uiHorizonView.addSubview(UIView())
uiHorizonView.addSubview(uiImageView)
uiHorizonView.addSubview(UIView())

代码分析:此时UIHorizontalView添加了三个对象,两个UIView一个UIImageView。由于两个UIView既没有设置宽度也没有设置weight,所以默认以weight=1处理,UIImageView设置了weight=2,所以左右两个UIView各占1/(1+1+2)的宽度,UIImageView占2/(1+1+2)的了宽度。如果假设UIHorizontalView的宽度为1000,高度为200,那么UIImageView的位置则为(left:250,top:0,right:750,bottom:200)

以下为源代码:

//
//  UIViewLayout.swift
//  fmspeed
//
//  Created by jefcat on 2021/9/8.
//

import UIKit

enum LVMarignType{
    case Left
    case Top
    case Right
    case Bottom
}

struct AssocKey {
    static var own_data = "own_data"
    static var lv_width = "lv_width"
    static var lv_height = "lv_height"
    static var lv_weight = "lv_weight"
    static var lv_marign_left = "lv_marign_left"
    static var lv_marign_top = "lv_marign_top"
    static var lv_marign_right = "lv_marign_right"
    static var lv_marign_bottom = "lv_marign_bottom"
}

extension UIView{
    func lv_set_width(value:CGFloat?){
        objc_setAssociatedObject(self, &(AssocKey.lv_width), value, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
    }
    
    func lv_get_width() -> CGFloat? {
        let val = objc_getAssociatedObject(self, &(AssocKey.lv_width))
        if val != nil{
            return val as? CGFloat
        }
        return nil
    }
    
    func set_owndata(value:Any?){
        objc_setAssociatedObject(self, &(AssocKey.own_data), value, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
    }
    
    func get_owndata() -> Any? {
        let val = objc_getAssociatedObject(self, &(AssocKey.own_data))
        return val
    }
    
    func lv_set_height(value:CGFloat?){
        objc_setAssociatedObject(self, &(AssocKey.lv_height), value, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
    }
    
    func lv_get_height() -> CGFloat? {
        let val = objc_getAssociatedObject(self, &(AssocKey.lv_height))
        if val != nil{
            return val as? CGFloat
        }
        return nil
    }
    
    func lv_set_weight(value:CGFloat?){
        objc_setAssociatedObject(self, &(AssocKey.lv_weight), value, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
    }
    
    func lv_get_weight() -> CGFloat? {
        let val = objc_getAssociatedObject(self, &(AssocKey.lv_weight))
        if val != nil{
            return val as? CGFloat
        }
        return nil
    }
    
    func lv_set_marign(type:LVMarignType,value:CGFloat?){
        switch type {
        case .Left:
            objc_setAssociatedObject(self, &(AssocKey.lv_marign_left), value, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        case .Top:
            objc_setAssociatedObject(self, &(AssocKey.lv_marign_top), value, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        case .Right:
            objc_setAssociatedObject(self, &(AssocKey.lv_marign_right), value, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        case .Bottom:
            objc_setAssociatedObject(self, &(AssocKey.lv_marign_bottom), value, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
        
    }
    
    func lv_get_marign(type:LVMarignType) -> CGFloat? {
        var value:Any? = nil
        switch type {
        case .Left:
            value = objc_getAssociatedObject(self, &(AssocKey.lv_marign_left))
        case .Top:
            value = objc_getAssociatedObject(self, &(AssocKey.lv_marign_top))
        case .Right:
            value = objc_getAssociatedObject(self, &(AssocKey.lv_marign_right))
        case .Bottom:
            value = objc_getAssociatedObject(self, &(AssocKey.lv_marign_bottom))
        }
    
        if value != nil{
            return value as? CGFloat
        }
        return nil
    }
    
    fileprivate func lv_get_marign_x(type:LVMarignType)->CGFloat{
        var marign = lv_get_marign(type: type)
       
        if  marign == nil{
            marign = 0.0
        }
        else{
            marign = marign!
        }
        return marign!
    }
}

protocol UIViewLayoutDelegate:AnyObject {
    func onContentSizeNeed(sender:Any,size:CGSize)
}
//绝对定位布局
class UIObsolateView: UIView {

    /*
    // Only override draw() if you perform custom drawing.
    // An empty implementation adversely affects performance during animation.
    override func draw(_ rect: CGRect) {
        // Drawing code
    }
    */
    var _delegate:UIViewLayoutDelegate? = nil
    
    override func layoutSubviews() {
        super.layoutSubviews()
        
        for item in subviews{
            //计算宽度
            var item_width = item.lv_get_width()
            if item_width == nil{
                let item_weight = item.lv_get_weight()
                if item_weight != nil {
                    item_width = item_weight!/1.0*self.bounds.width
                }
                else{
                    item_width = self.bounds.width
                }
            }
            
            // 计算高度
            var item_height = item.lv_get_height()
            
            if item_height == nil{
                let item_weight = item.lv_get_weight()
                if item_weight != nil {
                    item_height = item_weight!/1.0*self.bounds.height
                }
                else{
                    item_height = self.bounds.height
                }
            }
            //计算fram位置
            item.frame = CGRect(x:item.lv_get_marign_x(type: .Left), y: item.lv_get_marign_x(type: .Top), width: item_width!, height: item_height!)
        }
    }
    
    //当存在子控件动态 隐藏,展示,删除,添加的时候调用
    func refreshLayout() {
        layoutSubviews()
    }
    
    override var frame: CGRect{
        didSet{
            refreshLayout()
        }
    }
}

//水平布局
class UIHorizontalView: UIView {

    /*
    // Only override draw() if you perform custom drawing.
    // An empty implementation adversely affects performance during animation.
    override func draw(_ rect: CGRect) {
        // Drawing code
    }
    */
    var _delegate:UIViewLayoutDelegate? = nil
    
    override func layoutSubviews() {
        super.layoutSubviews()
        //计算view的宽度和权重
        var total_width:CGFloat = 0.0
        var total_weight:CGFloat = 0.0
        for item in subviews{
            //不可见控件不参与计算
            if item.isHidden{
                continue
            }
            let item_width = item.lv_get_width()
            if item_width != nil{
                total_width += (item_width! + item.lv_get_marign_x(type: .Left) + item.lv_get_marign_x(type: .Right))
            }
            else{
                total_width += item.lv_get_marign_x(type: .Left) + item.lv_get_marign_x(type: .Right)
                let item_weight = item.lv_get_weight()
                if item_weight != nil{
                    total_weight += item_weight!
                }
                else{
                    total_weight += 1.0
                }
            }
        }
        
        //计算权重的宽度
        var reset_width = self.bounds.width - total_width
        if reset_width <= CGFloat(0){
            reset_width = 0.0
        }
        
        var xoffset:CGFloat = 0.0
        for item in subviews{
            //不可见控件不参与计算
            if item.isHidden{
                continue
            }
            var item_width = item.lv_get_width()
            if item_width == nil{
                let item_weight = item.lv_get_weight()
                if item_weight != nil {
                    item_width = reset_width * item_weight!/total_weight
                }
                else{
                    item_width = reset_width/total_weight
                }
            }
            
            // 计算边距
            var item_height = item.lv_get_height()
            var yoffset:CGFloat = 0
            if item_height == nil{
                item_height = self.bounds.height - item.lv_get_marign_x(type: .Top) - item.lv_get_marign_x(type: .Bottom)
                yoffset = item.lv_get_marign_x(type: .Top)
            }
            else{
                yoffset = item.lv_get_marign_x(type: .Top)
            }
            
            if item_height! <= CGFloat(0){
                item_height = CGFloat(0)
            }
            //计算fram位置
            item.frame = CGRect(x: xoffset + item.lv_get_marign_x(type: .Left), y: yoffset, width: item_width!, height: item_height!)
            xoffset += (item_width! + item.lv_get_marign_x(type: .Left) + item.lv_get_marign_x(type: .Right))
        }
        
        if xoffset > self.bounds.width{
            _delegate?.onContentSizeNeed(sender:self,size:CGSize(width: xoffset, height: self.bounds.height))
        }
    }
    
    //当存在子控件动态 隐藏,展示,删除,添加的时候调用
    func refreshLayout() {
        layoutSubviews()
    }
    
    override var frame: CGRect{
        didSet{
            refreshLayout()
        }
    }
}


class UIVerticalView: UIView {

    /*
    // Only override draw() if you perform custom drawing.
    // An empty implementation adversely affects performance during animation.
    override func draw(_ rect: CGRect) {
        // Drawing code
    }
    */
    
    var _delegate:UIViewLayoutDelegate? = nil
    
    override func layoutSubviews() {
        super.layoutSubviews()
        //计算view的高度和权重
        var total_height:CGFloat = 0.0
        var total_weight:CGFloat = 0.0
        for item in subviews{
            //不可见控件不参与计算
            if item.isHidden{
                continue
            }
            let item_height = item.lv_get_height()
            if item_height != nil{
                total_height += (item_height! + item.lv_get_marign_x(type: .Top) + item.lv_get_marign_x(type: .Bottom))
            }
            else{
                total_height += item.lv_get_marign_x(type: .Top) + item.lv_get_marign_x(type: .Bottom)
                let item_weight = item.lv_get_weight()
                if item_weight != nil{
                    total_height += item_weight!
                }
                else{
                    total_weight += 1.0
                }
            }
        }
        
        //计算权重的高度
        var reset_height = self.bounds.height - total_height
        if reset_height <= CGFloat(0){
            reset_height = 0.0
        }
        
        var yoffset:CGFloat = 0.0
        for item in subviews{
            //不可见控件不参与计算
            if item.isHidden{
                continue
            }
            var item_height = item.lv_get_height()
            if item_height == nil{
                let item_weight = item.lv_get_weight()
                if item_weight != nil {
                    item_height = reset_height * item_weight!/total_weight
                }
                else{
                    item_height = reset_height/total_weight
                }
            }
            
            // 计算边距
            var item_width = item.lv_get_width()
            var xoffset:CGFloat = 0
            if item_width == nil{
                item_width = self.bounds.width - item.lv_get_marign_x(type: .Left) - item.lv_get_marign_x(type: .Right)
                xoffset = item.lv_get_marign_x(type: .Left)
            }
            else{
                xoffset = item.lv_get_marign_x(type: .Left)
            }
            
            if item_width! <= CGFloat(0){
                item_width = CGFloat(0)
            }
            //计算fram位置
            item.frame = CGRect(x: xoffset, y:yoffset + item.lv_get_marign_x(type: .Top), width: item_width!, height: item_height!)
            yoffset += (item_height! + item.lv_get_marign_x(type: .Top) + item.lv_get_marign_x(type: .Bottom))
        }
        
        if yoffset > self.bounds.width{
            _delegate?.onContentSizeNeed(sender:self,size:CGSize(width: self.bounds.width, height:yoffset))
        }
    }
    
    //当存在子控件动态 隐藏,展示,删除,添加的时候调用
    func refreshLayout() {
        layoutSubviews()
    }
    
    override var frame: CGRect{
        didSet{
            refreshLayout()
        }
    }
}

class UIVScrollView:UIScrollView,UIViewLayoutDelegate{
    func onContentSizeNeed(sender: Any, size: CGSize) {
        contentSize = size
    }
    
    private var _contentView = UIVerticalView()
    
    override func addSubview(_ view: UIView) {
        _contentView.addSubview(view)
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        initView()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        initView()
    }
    
    func initView() {
        _contentView._delegate = self
        super.addSubview(_contentView)
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        _contentView.frame = CGRect(x: 0, y: 0, width: self.bounds.width, height: self.bounds.height)
    }
    
    func refreshLayout() {
        _contentView.layoutSubviews()
    }
    
    override var frame: CGRect{
        didSet{
            refreshLayout()
        }
    }
}

class UIHScrollView:UIScrollView,UIViewLayoutDelegate{
    func onContentSizeNeed(sender: Any, size: CGSize) {
        contentSize = size
    }
    
    private var _contentView = UIHorizontalView()
    
    override func addSubview(_ view: UIView) {
        _contentView.addSubview(view)
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        initView()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        initView()
    }
    
    func initView() {
        _contentView._delegate = self
        super.addSubview(_contentView)
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        _contentView.frame = CGRect(x: 0, y: 0, width: self.bounds.width, height: self.bounds.height)
    }
    
    func refreshLayout() {
        _contentView.layoutSubviews()
    }
    
    override var frame: CGRect{
        didSet{
            refreshLayout()
        }
    }
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值