Swift-snapKit使用

snapKit的使用注意

SnapKit 是一个优秀的 Swift Auto Layout 库,但在使用中需要注意一些重要事项。以下是详细的使用注意事项:

1. 内存管理 - 避免循环引用

❌ 错误用法:

swift

复制代码
class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let button = UIButton()
        view.addSubview(button)
        
        button.snp.makeConstraints { make in
            // 这里隐式捕获了 self
            make.center.equalTo(self.view)
            make.width.equalTo(self.view).multipliedBy(0.5)
        }
    }
}

✅ 正确用法:

swift

复制代码
class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let button = UIButton()
        view.addSubview(button)
        
        button.snp.makeConstraints { [weak self] make in
            guard let self = self else { return }
            make.center.equalTo(self.view)
            make.width.equalTo(self.view).multipliedBy(0.5)
        }
    }
}

2. 视图层级顺序

❌ 错误用法:

swift

复制代码
// 先设置约束,后添加到父视图
view.snp.makeConstraints { make in
    make.edges.equalToSuperview()
}
parentView.addSubview(view) // 太晚了!

✅ 正确用法:

swift

复制代码
// 先添加到父视图,再设置约束
parentView.addSubview(view)
view.snp.makeConstraints { make in
    make.edges.equalToSuperview()
}

3. 约束更新和重制

更新特定约束:

swift

复制代码
class CustomView: UIView {
    var topConstraint: Constraint?
    
    func setupConstraints() {
        self.snp.makeConstraints { make in
            self.topConstraint = make.top.equalToSuperview().constraint
            make.left.right.equalToSuperview()
            make.height.equalTo(100)
        }
    }
    
    func updateTopConstraint() {
        // 更新单个约束
        self.topConstraint?.update(offset: 20)
    }
}

重制所有约束:

swift

复制代码
class CustomView: UIView {
    func remakeConstraintsForNewLayout() {
        // 移除所有旧约束,创建新约束
        self.snp.remakeConstraints { make in
            make.edges.equalToSuperview()
        }
    }
    
    func updateConstraintsForAnimation() {
        // 更新现有约束
        self.snp.updateConstraints { make in
            make.top.equalToSuperview().offset(50)
        }
    }
}

4. 优先级使用

swift

复制代码
view.snp.makeConstraints { make in
    // 设置优先级
    make.width.equalTo(200).priority(.high)
    make.width.lessThanOrEqualTo(300).priority(.required)
    make.width.greaterThanOrEqualTo(100).priority(.medium)
    
    // 或者使用具体数值
    make.height.equalTo(100).priority(999)
}

5. 安全区域和边距

❌ 过时用法:

swift

复制代码
view.snp.makeConstraints { make in
    make.top.equalTo(topLayoutGuide.snp.bottom) // iOS 11 之前
}

✅ 现代用法:

swift

复制代码
view.snp.makeConstraints { make in
    // 适配安全区域
    make.top.equalTo(view.safeAreaLayoutGuide.snp.top)
    make.bottom.equalTo(view.safeAreaLayoutGuide.snp.bottom)
    
    // 或者使用 edges 快捷方式
    make.edges.equalTo(view.safeAreaLayoutGuide)
}

考虑可读边距:

swift

复制代码
view.snp.makeConstraints { make in
    make.edges.equalToSuperview().inset(UIEdgeInsets(
        top: 0, 
        left: 16, 
        bottom: 0, 
        right: 16
    ))
}

6. 性能优化

批量设置约束:

swift

复制代码
// ❌ 低效:多次布局计算
view1.snp.makeConstraints { $0.top.equalToSuperview() }
view2.snp.makeConstraints { $0.top.equalTo(view1.snp.bottom) }
view3.snp.makeConstraints { $0.top.equalTo(view2.snp.bottom) }

// ✅ 高效:一次布局计算
snp.prepareConstraints { 
    view1.snp.makeConstraints { $0.top.equalToSuperview() }
    view2.snp.makeConstraints { $0.top.equalTo(view1.snp.bottom) }
    view3.snp.makeConstraints { $0.top.equalTo(view2.snp.bottom) }
}
// 然后调用 layoutIfNeeded()

7. 动画处理

swift

复制代码
class AnimatedView: UIView {
    var topOffset: Constraint?
    
    func setup() {
        self.snp.makeConstraints { make in
            self.topOffset = make.top.equalToSuperview().offset(0).constraint
            make.centerX.equalToSuperview()
            make.width.height.equalTo(100)
        }
    }
    
    func animate() {
        // 更新约束
        self.topOffset?.update(offset: 200)
        
        // 执行动画
        UIView.animate(withDuration: 0.3) {
            self.superview?.layoutIfNeeded()
        }
    }
}

8. 常见约束模式

居中布局:

swift

复制代码
view.snp.makeConstraints { make in
    make.center.equalToSuperview()
    make.size.equalTo(CGSize(width: 100, height: 100))
}

等分布局:

swift

复制代码
let views = [view1, view2, view3]
views.forEach { parentView.addSubview($0) }

view1.snp.makeConstraints { make in
    make.left.top.bottom.equalToSuperview()
}

view2.snp.makeConstraints { make in
    make.left.equalTo(view1.snp.right)
    make.top.bottom.equalToSuperview()
    make.width.equalTo(view1)
}

view3.snp.makeConstraints { make in
    make.left.equalTo(view2.snp.right)
    make.right.top.bottom.equalToSuperview()
    make.width.equalTo(view1)
}

9. 调试技巧

添加标识符(iOS 11+):

swift

复制代码
view.snp.makeConstraints { make in
    let constraint = make.width.equalTo(100).constraint
    constraint.identifier = "CustomViewWidthConstraint"
}

检查约束冲突:

swift

复制代码
override func updateConstraints() {
    super.updateConstraints()
    
    // 检查是否有无法满足的约束
    if hasAmbiguousLayout {
        exerciseAmbiguityInLayout()
    }
}

10. 最佳实践总结

  1. 总是先 addSubview 再设置约束

  2. 使用 [weak self] 避免循环引用

  3. 合理使用 makeConstraintsupdateConstraintsremakeConstraints

  4. 考虑安全区域,特别是 iPhone X 及以上机型

  5. 为复杂动画预先保存约束引用

  6. 使用优先级解决约束冲突

  7. 批量设置约束优化性能

  8. 为重要约束添加标识符便于调试

遵循这些注意事项可以避免常见的 SnapKit 使用陷阱,写出更健壮、易维护的布局代码。

SnapKit 的布局优先级是非常重要的功能,用于处理约束冲突和动态布局。以下是详细的使用指南:

1. 优先级基础用法

内置优先级级别:

swift

复制代码
view.snp.makeConstraints { make in
    make.width.equalTo(200).priority(.high)
    make.width.greaterThanOrEqualTo(100).priority(.medium)
    make.width.lessThanOrEqualTo(300).priority(.low)
}

// 或者使用具体数值
view.snp.makeConstraints { make in
    make.width.equalTo(200).priority(999)
    make.height.equalTo(100).priority(500)
}

SnapKit 内置优先级常量:

swift

复制代码
.priority(.required)        // 1000
.priority(.high)            // 750  
.priority(.medium)          // 500
.priority(.low)             // 250
.priority(.fittingSizeLevel) // 50

2. 优先级解决约束冲突

场景:宽度自适应但有最大最小限制

swift

复制代码
label.snp.makeConstraints { make in
    // 必需约束:最大宽度不超过屏幕的80%
    make.width.lessThanOrEqualToSuperview().multipliedBy(0.8).priority(.required)
    
    // 高优先级:首选宽度
    make.width.equalTo(150).priority(.high)
    
    // 低优先级:如果内容太多可以超过150
    make.width.greaterThanOrEqualTo(150).priority(.low)
}

3. 动态调整优先级

保存约束引用并修改优先级:

swift

复制代码
class CustomView: UIView {
    var widthConstraint: Constraint?
    var heightConstraint: Constraint?
    
    func setupConstraints() {
        self.snp.makeConstraints { make in
            self.widthConstraint = make.width.equalTo(100).priority(.high).constraint
            self.heightConstraint = make.height.equalTo(100).priority(.high).constraint
            make.center.equalToSuperview()
        }
    }
    
    func makeSizeFlexible() {
        // 降低优先级,允许压缩
        self.widthConstraint?.deactivate()
        self.snp.makeConstraints { make in
            self.widthConstraint = make.width.equalTo(100).priority(.low).constraint
        }
    }
}

4. 条件优先级

根据条件设置不同优先级:

swift

复制代码
class AdaptiveView: UIView {
    var isCompact: Bool = false
    
    func updateConstraints() {
        self.snp.remakeConstraints { make in
            if isCompact {
                make.width.equalTo(80).priority(.required)
                make.height.equalTo(40).priority(.required)
            } else {
                make.width.equalTo(120).priority(.required)
                make.height.equalTo(60).priority(.required)
            }
            make.center.equalToSuperview()
        }
    }
}

5. 多约束竞争场景

按钮组布局示例:

swift

复制代码
class ButtonGroup: UIView {
    let button1 = UIButton()
    let button2 = UIButton()
    let button3 = UIButton()
    
    func setupConstraints() {
        [button1, button2, button3].forEach { addSubview($0) }
        
        button1.snp.makeConstraints { make in
            make.left.equalToSuperview()
            make.top.bottom.equalToSuperview()
            // 首选宽度,但不是强制的
            make.width.equalTo(100).priority(.high)
        }
        
        button2.snp.makeConstraints { make in
            make.left.equalTo(button1.snp.right)
            make.top.bottom.equalToSuperview()
            // 与button1等宽,高优先级
            make.width.equalTo(button1).priority(.high)
        }
        
        button3.snp.makeConstraints { make in
            make.left.equalTo(button2.snp.right)
            make.right.equalToSuperview()
            make.top.bottom.equalToSuperview()
            // 与button1等宽,但优先级较低,可以压缩
            make.width.equalTo(button1).priority(.medium)
        }
    }
}

6. UILabel 多行文本自适应

swift

复制代码
label.snp.makeConstraints { make in
    // 必需:最大宽度限制
    make.width.lessThanOrEqualTo(300).priority(.required)
    
    // 高优先级:首选宽度
    make.width.equalTo(200).priority(.high)
    
    // 低优先级:最小宽度保证可读性
    make.width.greaterThanOrEqualTo(80).priority(.low)
    
    // 高度自适应
    make.height.greaterThanOrEqualTo(40).priority(.required)
}

7. 响应式布局优先级

根据屏幕方向调整:

swift

复制代码
class ResponsiveView: UIView {
    var portraitConstraints: [Constraint] = []
    var landscapeConstraints: [Constraint] = []
    
    func setupConstraints() {
        // 竖屏约束
        self.snp.makeConstraints { make in
            portraitConstraints = [
                make.width.equalToSuperview().priority(.high).constraint,
                make.height.equalTo(200).priority(.high).constraint
            ]
        }
        
        // 横屏约束(初始不激活)
        landscapeConstraints = self.snp.prepareConstraints { make in
            make.width.equalTo(300).priority(.high).constraint
            make.height.equalToSuperview().priority(.high).constraint
        }
    }
    
    func updateForOrientation(_ isLandscape: Bool) {
        if isLandscape {
            portraitConstraints.forEach { $0.deactivate() }
            landscapeConstraints.forEach { $0.activate() }
        } else {
            landscapeConstraints.forEach { $0.deactivate() }
            portraitConstraints.forEach { $0.activate() }
        }
    }
}

8. 复杂布局优先级策略

三栏布局示例:

swift

复制代码
class ThreeColumnLayout: UIView {
    let leftView = UIView()
    let centerView = UIView()
    let rightView = UIView()
    
    func setupConstraints() {
        [leftView, centerView, rightView].forEach { addSubview($0) }
        
        // 左栏:固定宽度,高优先级
        leftView.snp.makeConstraints { make in
            make.left.top.bottom.equalToSuperview()
            make.width.equalTo(100).priority(.required)
        }
        
        // 右栏:固定宽度,高优先级
        rightView.snp.makeConstraints { make in
            make.right.top.bottom.equalToSuperview()
            make.width.equalTo(100).priority(.required)
        }
        
        // 中间栏:自适应,但有限制
        centerView.snp.makeConstraints { make in
            make.left.equalTo(leftView.snp.right)
            make.right.equalTo(rightView.snp.left)
            make.top.bottom.equalToSuperview()
            
            // 最小宽度保证,但优先级较低
            make.width.greaterThanOrEqualTo(200).priority(.medium)
            
            // 首选宽度,但不是强制的
            make.width.equalTo(300).priority(.low)
        }
    }
}

9. 调试优先级问题

添加约束标识符:

swift

复制代码
view.snp.makeConstraints { make in
    let widthConstraint = make.width.equalTo(100).priority(.high).constraint
    widthConstraint.identifier = "HighPriorityWidth"
    
    let heightConstraint = make.height.equalTo(100).priority(.medium).constraint
    heightConstraint.identifier = "MediumPriorityHeight"
}

检查激活的约束:

swift

复制代码
func debugConstraints() {
    DispatchQueue.main.async {
        let constraints = self.constraints
        for constraint in constraints {
            if let identifier = constraint.identifier {
                print("约束: \(identifier), 优先级: \(constraint.priority.rawValue), 激活: \(constraint.isActive)")
            }
        }
    }
}

10. 最佳实践总结

  1. 合理使用优先级层次.required.high.medium.low

  2. 避免过多 .required 优先级,容易导致约束冲突

  3. 为自适应布局使用 .medium.low 优先级

  4. 保存约束引用用于动态调整

  5. 使用约束标识符便于调试

  6. 考虑使用 UILayoutPriority 自定义数值.priority(UILayoutPriority(800))

  7. 测试不同屏幕尺寸下的优先级表现

  8. 优先保证内容可读性,其次考虑设计精确度

正确使用优先级可以让布局更加灵活和健壮,特别是在处理动态内容和多设备适配时。

相关推荐
Elasticsearch1 天前
如何使用 Agent Builder 排查 Kubernetes Pod 重启和 OOMKilled 事件
elasticsearch
Elasticsearch2 天前
通用表达式语言 ( CEL ): CEL 输入如何改进 Elastic Agent 集成中的数据收集
elasticsearch
郑州光合科技余经理4 天前
代码展示:PHP搭建海外版外卖系统源码解析
java·开发语言·前端·后端·系统架构·uni-app·php
feifeigo1234 天前
matlab画图工具
开发语言·matlab
dustcell.4 天前
haproxy七层代理
java·开发语言·前端
norlan_jame4 天前
C-PHY与D-PHY差异
c语言·开发语言
多恩Stone4 天前
【C++入门扫盲1】C++ 与 Python:类型、编译器/解释器与 CPU 的关系
开发语言·c++·人工智能·python·算法·3d·aigc
QQ4022054964 天前
Python+django+vue3预制菜半成品配菜平台
开发语言·python·django
遥遥江上月4 天前
Node.js + Stagehand + Python 部署
开发语言·python·node.js
m0_531237174 天前
C语言-数组练习进阶
c语言·开发语言·算法