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. 优先保证内容可读性,其次考虑设计精确度

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

相关推荐
魔力女仆4 小时前
分享一个 JS 鼠标跟随贪吃蛇背景库
开发语言·javascript·计算机外设
2401_894915535 小时前
GEO 搜索优化完整源码从零部署:环境配置、集群搭建全流程
开发语言·python·tcp/ip·算法·unity
麻瓜老宋6 小时前
AI开发C语言应用按步走,表达式计算器calc的第二十二步,分号赋值链式修复、TOKEN_ASSIGN
c语言·开发语言·atomcode
Ai拆代码的曹操7 小时前
Git 集成:AI 代理中的版本控制工作流设计
大数据·git·elasticsearch
颜x小8 小时前
[C#] C++与c#语法对比
开发语言·c++·c#
weixin_423652138 小时前
C#使用JObject
开发语言·c#
国科安芯8 小时前
FreeRTOS RISC-V 浮点上下文切换移植:在 IAR 工程中完整保存 FPU 寄存器
java·开发语言·单片机·嵌入式硬件·算法·系统架构·risc-v
霸道流氓气质9 小时前
Java中集成Smile 技术教程:从入门到工程实践
java·开发语言
Elasticsearch9 小时前
Elastic Observability 更新后的指标定价:业界领先的指标能力 —— 现在成本也更低!
elasticsearch
Elasticsearch9 小时前
Elastic 新的指标能力将显著提升公共部门 IT 的正常运行时间
elasticsearch