用SwiftUI写一个简单页面

级别:★☆☆☆☆
标签:「iOS 13」「SwiftUI」「Xcode 11.1」
作者: 647
审校: QiShare团队


前言:
之前同事WYongW写了一篇《用Flutter写一个简单页面》,本篇将和大家一起调研一下苹果今年推出的SwiftUI框架。
接下来,让我们一起入门一下SwiftUI(尝尝鲜)。


一、SwiftUI是什么?

1. 定义:

简单来说,SwiftUI是苹果在“WWDC-2019”推出的一款全新的“声明式UI”框架。
拆开看,Swift + UI。(由此可以看出Swift越来越重要)

2. 特点:
  • “简洁迅速”的Swift:越来越简洁的Swift语法,配上Swift迅速的优势。

  • “即视”的UI:降低调试成本,一边写code、一边就可查看UI

  • 跨平台:一套代码,即可完成iOSiPadOSmacOSwatchOS的开发与适配。

  • “声明式”编程

简单来说,对比之前的“指令式”编程,我们通常需要告诉计算机“怎么做”
“声明式”编程是让我们告诉计算机“做什么”?(至于最底层怎么做,我们无需关心。)

举个例子,对于写UI而言,

  • 指令式编程:就是,怎么画?把每个framelayout等等统统需要计算到位。
  • 声明式编程:就是,画什么?把想要的效果描述出来,其他都交给框架去做。
3. 开发环境:

这么新的技术肯定需要环境的支持。SwiftUI所需要的开发环境,如下:

  • Xcode:Xcode 11.1+
  • MacOS:MacOS 10.15+
  • iOS:iOS 13+

PS:由于SwiftUI只能应用与iOS 13系统以上的设备。
因此,这项技术不建议用在需要适配低版本(iOS 13 以下)的App上。
不过如果是无需适配低版本的新项目,或者学习者全可以上手“玩一玩”。
毕竟苹果的新技术还是很有意思的嘛~

二、SwiftUI的基本组件(语法)

这块知识比较“基础”且“重要”。只有记住了这些基本组件,我们才能用较少的代码开发出精美的App。

下面,我将给大家介绍一些重要的SwiftUI组件:

组件介绍:

名称 含义
Text 用来显示文本的组件,类似UIKit中的UILabel
Image 用来展示图片的组件,类似UIKit中的UIImageView
Button 用于可点击的按钮组件,类似UIKit中的UIButton
List 用来展示列表的组件,类似UIKit中的UITableView
ScrollView 用来支持滑动的组件,类似UIKit中的UIScrollView
Spacer 一个灵活的空间,用来填充空白的组件。
Divider 一条分割线,用来划分区域的组件。
VStack 将子视图按“竖直方向”排列布局。(Vertical stack
HStack 将子视图按“水平方向”排列布局。(Horizontal stack
ZStack 将子视图按“两轴方向均对齐”布局(居中,有重叠效果)

基本组件:

  • Text:用来显示文本的组件,类似UIKit中的UILabel
Text("Hello, we are QiShare!").foregroundColor(.blue).font(.system(size: 32.0))
  • Image:用来展示图片的组件,类似UIKit中的UIImageView
Image.init(systemName: "star.fill").foregroundColor(.yellow)
  • Button:用于可点击的按钮组件,类似UIKit中的UIButton
Button(action: { self.showingProfile.toggle() }) {
    Image(systemName: "paperplane.fill")
        .imageScale(.large)
        .accessibility(label: Text("Right"))
        .padding()
}
  • List:用来展示列表的组件,类似UIKit中的UITableView
List(0..<5){_ in
        NavigationLink.init(destination: VStack(alignment:.center){
            Image.init(systemName: "\(item+1).square.fill").foregroundColor(.green)
            Text("详情界面\(item + 1)").font(.system(size: 16))
    }) {
          //ListRow
       }
  • ScrollView:用来支持滑动的组件,类似UIKit中的UIScrollView

  • Spacer:一个灵活的空间,用来填充空白的组件。

  • Divider:一条分割线,用来划分区域的组件。

布局组件:

  • VStack:将子视图按“竖直方向”布局。(Vertical stack)

  • HStack:将子视图按“水平方向”布局。(Horizontal stack)

  • ZStack:将子视图按“两轴方向均对齐”布局。

功能组件:

  • NavigationView:负责App中导航功能的组件,类似UIKit中的UINavigationView

  • NavigationLink:负责App页面跳转的组件,类似于UINavigationView中的pushpop功能。

NavigationView {
    List(0..<5){_ in
        NavigationLink.init(destination: VStack(alignment:.center){
            Image.init(systemName: "\(item+1).square.fill").foregroundColor(.green)
            Text("详情界面\(item + 1)").font(.system(size: 16))
    }) {
          //ListRow
       }
}
.navigationBarTitle("导航\(item)",displayMode: .inline)
  • TabView:负责App中的标签页功能的组件,类似UIKit中的UITabBarController
TabView {
    Text("The First Tab")
        .tabItem {
            Image(systemName: "1.square.fill")
            Text("First")
        }
    Text("Another Tab")
        .tabItem {
            Image(systemName: "2.square.fill")
            Text("Second")
        }
    Text("The Last Tab")
        .tabItem {
            Image(systemName: "3.square.fill")
            Text("Third")
        }
}
.font(.headline)

三、SwiftUI快速上手实践

下面让我们快速实现一个有TabView、NavigationView、List的Demo。

SF Symbols 是从 iOS 13macOS 10.15 开始内置于系统中的字符图标库,它提供了上千种常见的线条图标,而且我们可以任意地为它们设置尺寸,颜色等属性。Apple 甚至准备了专门的app:SF Symbols 来帮助你查看可用的符号:

接下来就让我们用这些Symbols制作个小Demo。

  • ContentView:
import SwiftUI

struct ContentView: View {
    
    @State var isLeftNav = false
    @State var isRightNav = false
    
    init() {
        //修改导航栏文字颜色
        UINavigationBar.appearance().largeTitleTextAttributes = [.foregroundColor: UIColor.systemBlue]
        UINavigationBar.appearance().titleTextAttributes = [.foregroundColor: UIColor.systemBlue]
        UINavigationBar.appearance().tintColor = .systemBlue
    }
    
    var body: some View {
        TabView {
            
            // Tab1:
            NavigationView {
                List(Symbols, id:\.self) {
                    ListRow(symbol: $0)
                }
                .navigationBarTitle(Text("SF Symbols"))
                .navigationBarItems(leading: leftNavButton, trailing: rightNavButton)
            }.tabItem {
                Image.init(systemName: "star.fill")
                Text("Tab1").font(.subheadline)
            }
            
            // Tab2:
            NavigationView {
                Text("This is the second tab.")
            }.tabItem {
                Image.init(systemName: "star.fill")
                Text("Tab2").font(.subheadline)
            }
        }
    }
    
    var leftNavButton: some View {
        Button(action: { self.isLeftNav.toggle() }) {
            Image(systemName: "person.crop.circle")
                .imageScale(.large)
                .accessibility(label: Text("Left"))
                .padding()
        }
        .sheet(isPresented: $isLeftNav) {
            VStack {
                Text("Hello, we are QiShare!").foregroundColor(.blue).font(.system(size: 32.0))

                HStack {
                    Spacer()
                    Spacer()
                    Text("an iOS Team. ").fontWeight(.black).foregroundColor(.purple)
                    Spacer()
                    Text("We are learning SwiftUI.").foregroundColor(.blue)
                    Spacer()
                }
            }
        }
    }
    
    var rightNavButton: some View {
        Button(action: { self.isRightNav.toggle() }) {
            Image(systemName: "paperplane.fill")
                .imageScale(.large)
                .accessibility(label: Text("Right"))
                .padding()
        }
        .sheet(isPresented: $isRightNav, onDismiss: {
            print("dissmiss RrightNav")
        }) {
            ZStack {
                Text("This is the Right Navi Button.")
            }
        }
    }
}
  • ListRow:List对应的Cell
struct ListRow: View {
    var symbol: String
    var body: some View {
        NavigationLink(destination: ListDetail(symbol: symbol)) {
            
            HStack {
                //图片
                Image(systemName: symbol)
                    .resizable()
                    .frame(width: 60, height: 60)
                    .foregroundColor(Colors.randomElement())
                //分割
                Divider()
                Spacer()
                //文字
                Text(symbol)
                Spacer()
            }
        }
    }
}
  • ListDetail:
import SwiftUI

struct ListDetail: View {
    
    var symbol: String
    
    var body: some View {
        VStack {
            
            Text("Image:").font(.headline)
            
            Spacer()
            
            Image(systemName: symbol)
                .foregroundColor(Colors.randomElement())
                .imageScale(.large)
                .scaleEffect(3)
                .padding(.bottom, 100)
            
            Divider()
            
            Text("Image Name:").font(.headline)
            Spacer()
            Text(symbol)
                .font(.largeTitle)
            Spacer()
        }
        .navigationBarTitle(symbol)
    }
}

源码:本文Demo


推荐文章:
iOS 控制日志的开关
iOS App中可拆卸一个framework的两种方式
自定义WKWebView显示内容(一)
Swift 5.1 (6) - 函数
Swift 5.1 (5) - 控制流
Xcode11 新建工程中的SceneDelegate
iOS App启动优化(二)—— 使用“Time Profiler”工具监控App的启动耗时
iOS App启动优化(一)—— 了解App的启动流程
iOS WKWebView的基本使用
Swift 5.1 (4) - 集合类型

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,511评论 1 330
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,495评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,595评论 0 225
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,558评论 0 190
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,715评论 3 270
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,672评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,112评论 2 291
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,837评论 0 181
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,417评论 0 228
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,928评论 2 232
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,316评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,773评论 2 234
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,253评论 3 220
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,827评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,440评论 0 180
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,523评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,583评论 2 249

推荐阅读更多精彩内容

  • 前言:之前同事WYongW写了一篇《用Flutter写一个简单页面》,本篇将和大家一起调研一下苹果今年推出的Swi...
    齐舞647阅读 5,852评论 1 10
  • 夏的脚步愈来愈远 秋,静静地来临 我还未从夏的梦中清醒 他已再次萦绕在我的梦中 才发现我们本是亲密无间 惊喜在我心...
    野地百合_35b6阅读 444评论 9 5
  • 根据市场调查的数据显示,北京ui设计人才需求人数约为25731人,所占比例为全国之首,高达29.2%;上海UI设计...
    早晨午后阅读 74评论 0 0
  • 技能。 感觉这篇文会写的像个人简历啊,不过,比起明天的写自己十个优点,这篇稍微能承受一些。 我想,这次写作是陪伴,...
    静候师太阅读 263评论 0 0
  • 曾有人说 给他一根杠杆 就可以撬动地球 我说 给我一根棍子 就可以打遍天下狗狗 这些都是吹牛逼 很多狗,人面狗身 ...
    开心点金石阅读 230评论 2 4