Swift自动布局SnapKit的详细使用介绍

Snapkit的布局使用
1、 实现一个宽高为100,居于当前视图的中心的视图布局,示例代码如下

import UIKit
import SnapKit
   
   class ViewController: UIViewController {
       override func viewDidLoad() {
           super.viewDidLoad()
        
           let testView = UIView()
           testView.backgroundColor = UIColor.cyan
           view.addSubview(testView)
           testView.snp.makeConstraints { (make) in
               make.width.equalTo(100)         // 宽为100
               make.height.equalTo(100)        // 高为100
               make.center.equalTo(view)       // 位于当前视图的中心
           }
           
       } 

更简洁的写法可以

import UIKit
import SnapKit

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
     
        let testView = UIView()
        testView.backgroundColor = UIColor.cyan
        view.addSubview(testView)
        testView.snp.makeConstraints { (make) in
            make.width.height.equalTo(100)    // 链式语法直接定义宽高
            make.center.equalToSuperview()    // 直接在父视图居中
        }
        
    }
}

效果图
在这里插入图片描述
2、View2位于View1内, view2位于View1的中心, 并且距离View的边距的距离都为20

import UIKit
import SnapKit

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
     
         // 黑色视图作为父视图
         let view1 = UIView()
         view1.frame = CGRect(x: 0, y: 0, width: 300, height: 300)
         view1.center = view.center
         view1.backgroundColor = UIColor.black
         view.addSubview(view1)
         
         // 测试视图
         let view2 = UIView()
         view2.backgroundColor = UIColor.magenta
         view1.addSubview(view2)
         view2.snp.makeConstraints { (make) in
              make.top.equalToSuperview().offset(20)      // 当前视图的顶部距离父视图的顶部:20(父视图顶部+20)
              make.left.equalToSuperview().offset(20)     // 当前视图的左边距离父视图的左边:20(父视图左边+20)
              make.bottom.equalToSuperview().offset(-20)  // 当前视图的底部距离父视图的底部:-20(父视图底部-20)
              make.right.equalToSuperview().offset(-20)   // 当前视图的右边距离父视图的右边:-20(父视图右边-20)
         }
        
    }
}

更简洁的写法

import UIKit
import SnapKit

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
     
         // 黑色视图作为父视图
         let view1 = UIView()
         view1.frame = CGRect(x: 0, y: 0, width: 300, height: 300)
         view1.center = view.center
         view1.backgroundColor = UIColor.black
         view.addSubview(view1)
         
         // 测试视图
         let view2 = UIView()
         view2.backgroundColor = UIColor.magenta
         view1.addSubview(view2)
         view2.snp.makeConstraints { (make) in
            make.edges.equalToSuperview().inset(UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20))
         }
        
    }
}

效果图
在这里插入图片描述

TestPicture2
3、布局一个视图view2, 让它的水平中心线小于等于另一个视图view2的左边,可以这样布局

import UIKit
import SnapKit

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
     
         // 黑色视图作为父视图
         let view1 = UIView()
         view1.frame = CGRect(x: 0, y: 0, width: 300, height: 300)
         view1.center = view.center
         view1.backgroundColor = UIColor.black
         view.addSubview(view1)
        
         // 测试视图
         let view2 = UIView()
         view2.backgroundColor = UIColor.magenta
         view1.addSubview(view2)
         view2.snp.makeConstraints { (make) in
            // 让顶部距离view1的底部为10的距离
            make.top.equalTo(view1.snp.bottom).offset(10)
            // 设置宽、高
            make.width.height.equalTo(100)
            // 水平中心线<=view1的左边
            make.centerX.lessThanOrEqualTo(view1.snp.leading)
         }
        
    }
}

效果图
在这里插入图片描述

TestPicture
视图的属性说明
通过上面的大致简单布局我们对SnapKit有了一个基本的了解,那么, 它的布局属性是怎么来的呢?和原生的布局类有什么关联? 下面看一个SnapKit的布局属性表
在这里插入图片描述

从表中,我们知道,Snapkit的布局属性都是源自于系统的NSLayoutAttribute,那么,NSLayoutAttribute是个什么呢?其实,它在swift中是一个枚举,内部列举了很多布局属性诸如top、left、leading、centerX等,Snapkit的布局属性与它们都存在一一的对应关系。

Snapkit 的 greaterThanOrEqualTo 属性
如果想让视图View2的左边>=父视图View1的左边, 这时我们就可以用到greaterThanOrEqualTo

import UIKit
import SnapKit

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
         // 黑色视图作为父视图
         let view1 = UIView()
         view1.frame = CGRect(x: 0, y: 0, width: 300, height: 300)
         view1.center = view.center
         view1.backgroundColor = UIColor.black
         view.addSubview(view1)
        
         // 测试视图
         let view2 = UIView()
         view2.backgroundColor = UIColor.magenta
         view1.addSubview(view2)
         view2.snp.makeConstraints { (make) in
            // 让顶部距离view1的底部为10的距离
            make.top.equalTo(view1.snp.bottom).offset(10)
            // 设置宽、高
            make.width.height.equalTo(100)
            // 水平中心线<=view1的左边
            make.left.greaterThanOrEqualTo(view1)
            
            // 或者, 和上面一行代码一样的效果
//            make.left.greaterThanOrEqualTo(view1.snp.left)
         }
    }
}

效果图
在这里插入图片描述

TestPicture
其实,greaterThanOrEqualTo这个属性有点多余,比如上面这行代码 make.left.greaterThanOrEqualTo(view1) , 我们可以换成 make.left.equalToSuperview()make.left.equalTo(view1.snp.left), 效果是一样的,也就是说,一般情况下 >= 或 <= 我们都可以直接用equalTo来代替!

SnapKit的greaterThanOrEqualTo和lessThanOrEqualTo联合使用
当我们想要让某个视图的width或height大于等于某个特定的值,小于等于某个特定的值的时候,一般而言,Snapkit会以greaterThanOrEqualTo为准,这里举一个width的例子,为了方便,这里指贴出了viewDidLoad中的代码(其他没必要)

 // 黑色视图作为父视图
 let view1 = UIView()
 view1.frame = CGRect(x: 0, y: 0, width: 300, height: 300)
 view1.center = view.center
 view1.backgroundColor = UIColor.black
 view.addSubview(view1)
    
 // 测试视图
 let view2 = UIView()
 view2.backgroundColor = UIColor.magenta
 view1.addSubview(view2)
 view2.snp.makeConstraints { (make) in
    make.width.lessThanOrEqualTo(300)
    make.width.greaterThanOrEqualTo(200)
    make.height.equalTo(100)
    make.center.equalToSuperview()
 }

接着,我们来看一下效果图
在这里插入图片描述

Test6
很明显,最后的宽度是以make.width.greaterThanOrEqualTo(200)为标准的,也可以这样的,在同时使用两者的情况下,greaterThanOrEqualTo的优先级略比lessThanOrEqualTo的优先级高。值得一提的是, 在上面的例子中,如果我们只设置make.width.lessThanOrEqualTo(300),那么view2是不会显示出来的,因为view2不知道你要表达的是要显示多少,小于等于300,到底是100还是200呢?(这里指针对width和height)所以它不能确定这个约束的值,但是,如果我们单独设置make.width.greaterThanOrEqualTo(200),那么就和上面的效果一样,因为它会以200为标准布局约束!

lessThanOrEqualTo的用于上、下、左、右
如果我们想要视图view2的左边 <= view1.left + 10, 那么就可以直接用到lessThanOrEqualTo布局了,看下面这个例子

 // 黑色视图作为父视图
 let view1 = UIView()
 view1.frame = CGRect(x: 0, y: 0, width: 300, height: 300)
 view1.center = view.center
 view1.backgroundColor = UIColor.black
 view.addSubview(view1)
    
 // 测试视图
 let view2 = UIView()
 view2.backgroundColor = UIColor.magenta
 view1.addSubview(view2)
 view2.snp.makeConstraints { (make) in
    make.left.lessThanOrEqualTo(20)     // <= 父视图的左边+20
    make.right.equalTo(-40)             // = 父视图的右边-40
    make.height.equalTo(100)
    make.center.equalToSuperview()
 }

效果图
在这里插入图片描述

Test7
Snapkit布局的灵活性
Snapkit布局灵活性很强, 我们看下面的例子, 他们的效果是一样的

make.left.equalToSuperview().offset(10)
make.left.equalTo(10)
make.left.equalTo(view1.snp.left).offset(10)

设置视图的大小(width,height),他们效果是一样的

make.width.height.equalTo(100)
或
make.width.equalTo(100)
make.height.equalTo(100)
或
make.size.equalTo(CGSize(width: 100, height: 100))

优先级(priority)
我们来看一下Snapkit的优先级设置, 优先级都是附加在约束链的末尾处,看下面的使用方法

// 黑色视图作为父视图
let view1 = UIView()
view1.frame = CGRect(x: 0, y: 0, width: 300, height: 300)
view1.center = view.center
view1.backgroundColor = UIColor.black
view.addSubview(view1)
    
// 测试视图
let view2 = UIView()
view2.backgroundColor = UIColor.magenta
view1.addSubview(view2)
view2.snp.makeConstraints { (make) in
    make.width.equalTo(100).priority(666)
    make.width.equalTo(250).priority(999)
    make.height.equalTo(111)
    make.center.equalToSuperview()
}

效果图
在这里插入图片描述

从上面我们可以知道, 我们设置了两个优先级:make.width.equalTo(100).priority(666) 和 make.width.equalTo(250).priority(999), 那运行结果是一个哪个为准呢?显然是以优先级为 999的为准,因为 priority(999)>priotity(666), 所以在使用Snapkit的过程中,有时我们可以使用优先级priority来设置我们的约束, 另外,值得一提的是,SnapKit的优先级最大值只能是 1000, 如果优先级的数值超过1000,则运行时就会Crash!这里要尤其注意。

更新约束(引用约束)

我们可以通过保存某一个约束布局来更新相应的约束,或者保存一组约束布局到一个数组中更新约束, 具体看下面代码

// 保存约束(引用约束)
var updateConstraint: Constraint?
    
override func viewDidLoad() {
    super.viewDidLoad()
    
    // 黑色视图作为父视图
    let view1 = UIView()
    view1.frame = CGRect(x: 0, y: 0, width: 300, height: 300)
    view1.center = view.center
    view1.backgroundColor = UIColor.black
    view.addSubview(view1)

    // 测试视图
    let view2 = UIView()
    view2.backgroundColor = UIColor.magenta
    view1.addSubview(view2)
    view2.snp.makeConstraints { (make) in
        make.width.height.equalTo(100)  // 宽高为100
        self.updateConstraint = make.top.left.equalTo(10).constraint   // 距离父视图上、左为10
    }
    
    let updateButton = UIButton(type: .custom)
    updateButton.backgroundColor = UIColor.brown
    updateButton.frame = CGRect(x: 100, y: 80, width: 50, height: 30)
    updateButton.setTitle("更新", for: .normal)
    updateButton.addTarget(self, action: #selector(updateConstraintMethod), for: .touchUpInside)
    view.addSubview(updateButton)
}
    
// 更新约束
func updateConstraintMethod() {
    self.updateConstraint?.update(offset: 50)   // 更新距离父视图上、左为50
}

在这里插入图片描述

更新约束(snp.updateConstraints)
说起这个updateConstraints, 我也懵逼过,那么它到底有何作用呢?又怎么用呢?它和一开始就使用的makeConstraints又有什么明确的区别呢?请继续往下看

说明1:如果你这是更新某个约束或某几个约束的常量值,你就可以使用updateConstraints而不是makeConstraints。

说明2:这个也是苹果推荐用来添加或更新约束的方式

说明3:这个方法可以调用多次,会相应setNeedsUpdateConstraints, 在控制器中,可以写在override func updateViewConstraints()方法里面(当然也可以写在你想要什么时候更新的点击事件里面)

import UIKit
import SnapKit

class ViewController: UIViewController {

    lazy var blackView = UIView()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        blackView.backgroundColor = UIColor.black
        view.addSubview(blackView)
        blackView.snp.makeConstraints { (make) in
            
            // 四个约束确定位置和大小
            make.width.equalTo(100)
            make.height.equalTo(150)
            make.top.equalTo(10)
            make.centerX.equalToSuperview()
        }
 
    }
    
    override func updateViewConstraints() {
        
        blackView.snp.updateConstraints { (make) in
            // 更新距离父视图顶部的约束(从 10 ---> 300 )
            make.top.equalTo(300)
        }
        
        // 根据苹果,调用父类应该放在末尾
        super.updateViewConstraints()
    }
}

注意: 从上面的代码中我们很明确地知道, blackView通过width、height、top、centerX确定了它本身的大小和位置, 但是, 在 run 出来之后,它的top改变了距离, 从 10 变成了 300,其他三个约束保持不变, 见下图效果:
在这里插入图片描述

显而易见, 除了top约束, 其他都没有改变! 也就是说,约束被更新(相当于系统升级一样,是一个道理)

现在,我们通过UIButton的点击事件来证明一下制作约束makeConstraints和updateConstraints具体的区别在哪里?

lazy var blackView = UIView()
    
override func viewDidLoad() {
    super.viewDidLoad()
    
    blackView.backgroundColor = UIColor.black
    view.addSubview(blackView)
    blackView.snp.makeConstraints { (make) in
        
        make.width.equalTo(100)
        make.height.equalTo(150)
        make.top.equalTo(50)
        make.centerX.equalToSuperview()
    }
    
    let btn = UIButton(type: .custom)
    btn.backgroundColor = UIColor.brown
    btn.frame = CGRect(x: 100, y: 200, width: 60, height: 30)
    btn.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
    view.addSubview(btn)
 
}
    
// 点击更新/制作约束
func buttonAction() {
    
    blackView.snp.makeConstraints { (make) in
        make.width.height.equalTo(20)
        make.top.equalTo(300)
    }
    
}

先看效果图

点击事件发生前(图1):
在这里插入图片描述

点击事件发生后(图2)
在这里插入图片描述

图3
在这里插入图片描述

图4

在这里插入图片描述

上面,我们知道,视图 blackView一开始是由四个约束确定位置和大小,在点击事情响应后,我们又给 blackView 制作(记住,是制作,而不是重做,两者有明确的区别)了3个约束,分别是 width、height、top, 那么这样做问题出现在哪里呢? 第一, 点击事情发生前(图1), 在点击事件发生后(见图2), 我们发现,blackView的width、height约束改变了,但是 top却没有改变,还是原来的距离父视图顶部 50 的距离, 原因在于,我们在原来的约束基础上又添加了多余的约束, 也就是说,约束从4个变成了7个(见图3左边constraints), 这样就产生了约束不明确,进而导致snapkit的警告(见图4), 这样布局显然是不可取的,在项目中这样做极其危险,甚至可能会导致异常奔溃!!!!

现在, 我们该将点击事件中的约束布局从makeConstraints改变成updateConstraints来试试两者有什么区别(下面只添加了点击事件的代码,其他事重复的就不多此一举了)

func buttonAction() {
    // 注意这里是updateConstraints, 而不是makeConstraints
    blackView.snp.updateConstraints { (make) in
        make.width.height.equalTo(20)
        make.top.equalTo(300)
    }
    print("这里试试snapkit有没有报警告")
}

接着看点击事件后的效果图

图5

在这里插入图片描述

图6

在这里插入图片描述

图7
在这里插入图片描述

发现没有,在将makeConstraints改变成updateConstraints之后,约束还是4个,snapkit没有报警告,点击事件中的width、height、top全部起了作用,而这就是两者的本质区别:makeConstraints是制作约束,在原来的基础上再添加另外的约束,也就是画蛇添足,约束增加,视图布局就有不确定性,从而有些约束起作用,有些不起作用(如上面的top),snapkit报警告!!!而updateConstraints是更新约束,改变原有约束,约束不会增加,没经过updateConstraints处理的保持原有约束,经过处理就更新约束,约束不会减少,snapkit不会产生警告,这是正常标准的更新约束的正确方式!!!

重做约束(remakeConstraints)
重做约束的本质就是:去掉已有的所有约束, 重新做约束,记住,是做约束, 也就是说, 使用了remakeConstraints后,重做的约束必须要能确定相应视图的大小和位置, 之前makeConstraints的约束已经不会存在了,完全销毁!!!

// 点击更新/制作约束
func buttonAction() {
    
    // 注意这里是 remakeConstraints
    blackView.snp.remakeConstraints { (make) in
        make.width.height.equalTo(20)
        make.top.equalTo(300)
    }
    
    print("这里试试snapkit有没有报警告")
}

效果图

图(1)

在这里插入图片描述

图(2)

在这里插入图片描述

图(3)
在这里插入图片描述

我们看到, blackView重做了约束, 之前的约束不起任何作用,由于它在重做约束后只有 3 个约束分别是 width、height、top, 但是这里有一个问题,就是这 3 个约束只能确定大小,无法确定视图的位置, 所以在水平方向上或者左右缺少一个布局条件, 故 blackView整体视图的x紧靠左边(默认)! 另外我们发现, 在图(3)中,右上角出现了一个感叹号“!”, 那是因为告诉你缺少了一个约束条件:x-xcode-debug-views://7f81fcbc7900: runtime: Layout Issues: Horizontal position is ambiguous for UIView.

小结
通过以上学习,我们或深或浅地学习了布局三方库SnapKit的使用, 我相信,只要将上述布局会使用,并且懂得布局的原则和道理,基本上就可以“高枕无忧”了,至于涉及动态布局、动画布局等知识,后续有时间会更新文档。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
SwiftUI提供了多种方法来实现自定义布局。其中,可以使用HStack、VStack、ZStack等布局容器来组合和排列视图元素,实现各种复杂界面。 另外,SwiftUI还提供了对齐指南来帮助文本对齐,如.leading、.trailing、.top等。但是当处理在不同视图之间分割的视图时,如果需要使完全不同的两个视图部分对齐,可以使用自定义的对齐辅助线。使用这些辅助线可以在整个界面中创建和使用辅助线,在视图之前或之后发生的变化并不重要,它们仍然会按照一条线排列。 例如,可以使用offset(x: CGFloat, y: CGFloat)方法为视图指定水平和垂直的偏移距离。这个方法可以将视图在水平和垂直方向上移动指定的距离。 综上所述,使用SwiftUI可以通过布局容器和对齐指南实现自定义布局,同时也可以使用偏移方法来调整视图的位置。这些功能可以帮助您创建个性化的布局。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [SwiftUI 布局之自定义手动布局设置各种相对位置(教程含源码)](https://blog.csdn.net/iCloudEnd/article/details/107718464)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [SwiftUI之深入解析布局如何自定义AlignmentGuides](https://blog.csdn.net/Forever_wj/article/details/121958698)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值