• Xcode如何利用预览(Preview)让SwiftUI视图快速适配不同尺寸的设备


    在这里插入图片描述

    概览

    有时候,我们需要根据不同尺寸的设备适配最合适的视图布局。

    这在SwiftUI项目中如何操作呢?

    在这里插入图片描述

    如上图所示,我们在SwiftUI中自定义了一个多项列表视图,不过它在不同尺寸的设备中显示的并不完美。在大屏设备尾部留出了太多空间,而在小屏设备中最后一列又显示不全,真是让人伤脑筋啊!

    不过别急!

    在本篇博文中,我们将介绍一些在不同设备中适配SwiftUI视图的小技巧,相信小伙伴们看完之后一定能妥妥的搞定适配问题了。

    那还等什么呢?

    Let’s go! 😉

    所见即所得

    要想适配不同尺寸的设备,首先我们要做的第一件是就是能够快速预览不同设备的界面。

    我们可以通过为SwiftUI视图添加特定的修改器,在Xcode预览界面中快速浏览其在对应设备中的外观:

    struct TestView_Previews: PreviewProvider {
        static var body: some View {
            NavigationView {
                // 实际测试视图的布局描述
            }
        }
        
        static var previews: some View {
            Group {
                body
                    .previewDevice(.init(rawValue: "iPhone 13 Pro Max"))
                
                body
                    .previewDevice(.init(rawValue: "iPhone 13 Pro"))
                
                body
                    .previewDevice(.init(rawValue: "iPhone 12 mini"))
                
                body
                    .previewDevice(.init(rawValue: "iPod touch (7th generation)"))
                
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    在以上预览视图代码中,我们分别将测试视图放到 iPhone 13 Pro Max、iPhone 13 Pro、iPhone 12 mini以及iPod touch等四种不同尺寸设备中去预览显示。

    获取设备尺寸

    在不同设备中预览我们的视图只是第一步,接着我们需要在运行中确定到底当前在何种设备中运行。

    或者换句话说:当前运行的设备屏幕尺寸是多少?

    为了获取当前运行设备的名称,我们只需一行代码:

    UIDevice.current.name
    
    • 1

    同样,为了获取当前运行设备屏幕的尺寸,即逻辑分辨率,同样只需一行代码:

    UIScreen.main.bounds
    
    • 1

    这里,我们只需要设备屏幕的尺寸。

    动如脱兔,静若处子

    能够在运行中确定设备的尺寸之后,我们就可以根据不同设备的尺寸大小,来动态调整我们视图的界面了。

    首先,写一个对应的布局尺寸结构:

    fileprivate enum LayoutSize {
        case tiny, small, middle, large
        
        // 根据当前设备计算实际的布局尺寸
        static func calc() -> Self {
            let width = UIScreen.main.bounds.width
            
            if width <= 320 {
                return .tiny
            }else if width <= 360 {
                return .small
            }else if width <= 390 {
                return .middle
            }else{
                return .large
            }
        }
        
        // 列表中单个列的宽度
        var unitWidth: CGFloat {
            let w: CGFloat
            switch self {
            case .tiny, .small, .middle:
                w = 30
            case .large:
                w = 38
            }
            return w
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    在以上代码中,我们根据前面4种典型iPhone设备的宽度,创建了4种对应的尺寸枚举类型。

    我们希望在 large 和 middle 尺寸类型的设备中,最大化每列的宽度;而在较小尺寸 small 和 tiny 等设备中,紧凑的显示每列,并按需合并若干列,达到最优利用空间的目的:

    if layoutSize == .large || layoutSize == .middle {
        Text("\(trace.totalGainedScore)")
            .fontWeight(.heavy)
            .font(.body)
            .foregroundColor(scoreColor)
            .opacity(1.0)
            .frame(minWidth: layoutSize.unitWidth)
        
        baseScoreView
            .underline(true, color: stateColor)
            .fontWeight(.bold)
            .font(.callout)
            .foregroundColor(stateColor)
            .frame(minWidth: layoutSize.unitWidth)
    }else{
        HStack(alignment: .top, spacing: 5) {
            Text("\(trace.totalGainedScore)")
                .fontWeight(.heavy)
                .font(.body)
                .foregroundColor(scoreColor)
                .opacity(1.0)
            
            baseScoreView
                .underline(true, color: stateColor)
                .fontWeight(.bold)
                .font(.callout)
                .foregroundColor(stateColor)
        }.frame(minWidth: 50)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    不过,即使采用上面的优化方法,在最小的 tiny 设备界面中,最后一列的显示仍然捉襟见肘。

    这时,我们需要更进一步,大幅度更改列表的布局,将第二列的内容放到第一列最底部显示,继续压榨显示空间:

    if layoutSize == .tiny {
        VStack(spacing: 2) {
            Text(Model.shortDateFt.string(from: trace.date ?? Date.distantPast))
                .fontWeight(.light)
                .font(.footnote)
                .foregroundColor(.slateGray)
            
            CommonUI.hiveScoringTracesView(trace, model: model)
            
            // tiny设备中,需要将第二列显示的状态内容放到第一列的底部
            stateView
                .foregroundColor(stateColor)
                .frame(minWidth: layoutSize.unitWidth)
        }.frame(minWidth: 65.0)
    }else{
        VStack {
            Text(Model.shortDateFt.string(from: trace.date ?? Date.distantPast))
                .fontWeight(.light)
                .font(.footnote)
                .foregroundColor(.slateGray)
            
            CommonUI.hiveScoringTracesView(trace, model: model)
        }.frame(minWidth: 65.0)
            
        stateView
            .foregroundColor(stateColor)
            .frame(minWidth: layoutSize.unitWidth)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    现在,运行代码看一下布局适配的效果:

    在这里插入图片描述

    如上图所示,现在不同设备中的显示效果是不是比一开始要好很多呢?

    我们最大化的利用了空间,做到了视图适配的收放自如,棒棒哒!🚀

    总结

    在本篇博文中,我们利用SwiftUI代码,在Xcode预览中快速查看了不同设备的界面,并据此动态调整视图的布局,达到了最优空间之利用的目的。

    最后,感谢各位观赏,我们下次再会!😎

  • 相关阅读:
    CSS常用技巧
    MongoDB基础【学习笔记】
    HttpUtils工具类
    【微信小程序】缓存数据库操作类——prototype和ES6方法
    idea意外退出mac
    HFSS笔记——求解器和求解分析
    天翎知识管理系统为研究所文档管理组织创新赋能
    免费和开源的机器翻译软件LibreTranslate
    Windows安装Visual Studio2019+OpenCV配置
    envs.yaml与requirements.txt
  • 原文地址:https://blog.csdn.net/mydo/article/details/126051304