swiftUI
OK,我们先学习官方的swiftUI教程
创建一个项目
interface中的storyboard可以使用swift或者objective-C进行编写
项目初建
File > New > File (文件 > New > File) 以再次打开模板选择器。在用户界面部分,选择“SwiftUI 视图”,然后单击下一步。将文件命名为 CircleImage.swift
,然后单击 Create。
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 import SwiftUI@main struct LandmarksApp : App { var body: some Scene { WindowGroup { ContentView () } } } struct ContentView : View { var body: some View { VStack { Image (systemName: "globe" ) .imageScale(.large) .foregroundStyle(.tint) Text ("Hello, world!" ) } .padding() } } #Preview { ContentView () }
@main
属性标识应用程序的入口点。
结构的 body
属性返回一个或多个场景
视图声明结构和预览要符合 View
协议
preview 声明将为该视图创建预览。
使用inspector 检查器调整视图
默认情况下,画布以实时模式显示预览,实时模式可以与它们进行交互,但是不能编辑 **。在 Live 模式下工作,可以在 Source 中进行编辑时轻松跟踪视图行为.使用 Option 键单击并拖动控件查看周围区域。
点击这个可以改为 Selectable (可选) 模式来启用编辑
在Selectable (可选) 模式的预览中,按住 Command-Control 键点需要调整的模块 调出结构化编辑弹出窗口,然后选择**“Show SwiftUI Inspector”。**检查器更改或移除修饰符时,Xcode 会立即更新您的代码以匹配
stacks
body
属性仅返回单个视图。可以将多个视图组合并嵌入到堆栈 中,这些堆栈将视图水平、垂直或从后到前分组在一起。
按住 Control 键单击编辑器的模块 以显示上下文菜单,然后选择“Embed in VStack”(嵌入到 VStack 中)。
单击 Xcode 窗口右上角的加号按钮 (+) 打开库,然后将 Text
视图拖动到代码中“Turtle Rock”文本视图正下方的位置。 (这个好方便)
import SwiftUI
struct ContentView: View {
var body: some View {
// 按视图的前导边缘对齐视图。
// 默认情况下,堆栈沿其轴将其内容居中
VStack(alignment: .leading) {
Text("Turtle Rock")
.font(.title)
HStack {
Text("Joshua Tree National Park")
.font(.subheadline)
// 将 Spacer 添加到包含两个文本视图的水平堆栈中来分隔 park 和 state。
Spacer()
Text("California")
.font(.subheadline)
}
}
//使用 padding() 修饰符将vstack在两边有空隙
.padding()
}
}
#Preview {
ContentView()
}
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 ## 添加一个圆形发光图片 在项目文件的 Resources 文件夹中找到 `turtlerock@2x.jpg`;将其拖动到 Asset Catalog 的编辑器中。Resource随便改,但是Asset是固定位置。 ```swift import SwiftUI struct CircleImage: View { var body: some View { // Image(_:) Image("turtlerock") // Circle 类型是一种形状 .clipShape(Circle()) // 创建另一个带有白色描边的圆圈,然后将其添加为叠加层 // 白色在一个阴影上面,就会表现出发光的效果 .overlay { Circle().stroke(.white, lineWidth: 4) } .shadow(radius: 7) } } #Preview { CircleImage() }
使用MapKit
使用来自其他框架的 SwiftUI 视图-使用 MapKit 中的地图
视图来渲染地图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import SwiftUIimport MapKitstruct MapView : View { var body: some View { Map (initialPosition: .region(region)) } private var region: MKCoordinateRegion { MKCoordinateRegion ( center: CLLocationCoordinate2D (latitude: 34.011_286 , longitude: - 116.166_868 ), span: MKCoordinateSpan (latitudeDelta: 0.2 , longitudeDelta: 0.2 ) ) } } #Preview { MapView () }
融合各个view
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 31 32 33 34 35 36 37 38 import SwiftUIstruct ContentView : View { var body: some View { VStack { MapView () .frame(height: 300 ) CircleImage () .offset(y: - 130 ) .padding(.bottom, - 130 ) VStack (alignment: .leading) { Text ("Turtle Rock" ) .font(.title) HStack { Text ("Joshua Tree National Park" ) .font(.subheadline) Spacer () Text ("California" ) .font(.subheadline) } Divider () Text ("About Turtle Rock" ) .font(.title2) Text ("Descriptive text goes here." ) } .padding() Spacer () } } } #Preview { ContentView () }
创建数据
首先将下载文件的 Resources 文件夹中的 landmark.json 拖动到
项目的导航窗格中;在出现的对话框中,选择“Copy items if needed”和“Landmark”目标,然后单击“完成”。
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 import Foundationimport SwiftUIimport CoreLocationstruct Landmark : Hashable , Codable { var id: Int var name: String var park: String var state: String var description: String private var imageName: String var image: Image { Image (imageName) } private var coordinates: Coordinates var locationCoordinate: CLLocationCoordinate2D { CLLocationCoordinate2D ( latitude: coordinates.latitude, longitude: coordinates.longitude) } struct Coordinates : Hashable , Codable { var latitude: Double var longitude: Double } }
创建一个 load(_:)
方法,用于从应用程序的 main bundle 中获取具有给定名称的 JSON 数据。
load 方法依赖于返回类型对 Decodable
协议的一致性,该协议是 Codable
协议的一个组件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import Foundationfunc load <T : Decodable >(_ filename : String ) -> T { let data: Data guard let file = Bundle .main.url(forResource: filename, withExtension: nil ) else { fatalError ("Couldn't find \(filename) in main bundle." ) } do { data = try Data (contentsOf: file) } catch { fatalError ("Couldn't load \(filename) from main bundle:\n \(error) " ) } do { let decoder = JSONDecoder () return try decoder.decode(T .self , from: data) } catch { fatalError ("Couldn't parse \(filename) as \(T.self ) :\n \(error) " ) } }
一个简单的列表中的一个元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import SwiftUIstruct LandmarkRow : View { var landmark: Landmark var body: some View { HStack { landmark.image .resizable() .frame(width: 50 , height: 50 ) Text (landmark.name) Spacer () } } } #Preview { LandmarkRow (landmark: landmarks[0 ]) }
改成一个滚动的列表
1 2 3 4 5 6 7 // `Group` 是用于对视图内容进行分组的容器。Xcode 在画布中将组的子视图堆叠为一个预览。 #Preview { Group { LandmarkRow(landmark: landmarks[0]) LandmarkRow(landmark: landmarks[1]) } }
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 import SwiftUIstruct LandmarkList : View { var body: some View { List (landmarks, id: \.id) { landmark in LandmarkRow (landmark: landmark) } } } #Preview { LandmarkList () } import SwiftUIstruct LandmarkList : View { var body: some View { List (landmarks) { landmark in LandmarkRow (landmark: landmark) } } } #Preview { LandmarkList () }
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 import Foundationimport SwiftUIimport CoreLocationstruct Landmark : Hashable , Codable , Identifiable { var id: Int var name: String var park: String var state: String var description: String private var imageName: String var image: Image { Image (imageName) } private var coordinates: Coordinates var locationCoordinate: CLLocationCoordinate2D { CLLocationCoordinate2D ( latitude: coordinates.latitude, longitude: coordinates.longitude) } struct Coordinates : Hashable , Codable { var latitude: Double var longitude: Double } }
创建列表元素和另一个页面的链接
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 import SwiftUIstruct LandmarkList : View { var body: some View { NavigationSplitView { List (landmarks) { landmark in NavigationLink { LandmarkDetail () } label: { LandmarkRow (landmark: landmark) } } .navigationTitle("Landmarks" ) } detail: { Text ("Select a Landmark" ) } } } #Preview { LandmarkList () }
将数据传递到子视图
LandmarkDetail
视图仍然使用硬编码的详细信息来显示其地标。与 LandmarkRow
一样,LandmarkDetail
类型及其包含的视图需要使用 landmark
属性作为其数据源。
就是说刚才的list只有列表元素接收到landmark这个数据,现在子视图也需要这个数据
这个不看,就是简单的工程问题
渲染不同设备配置的列表视图预览
如果用户点击了收藏,则本地要储存收藏
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 31 32 33 34 35 36 37 38 39 40 import SwiftUI struct LandmarkList: View { // 添加一个名为 showFavoritesOnly 的 @State 属性,其初始值设置为 false //当您更改视图的结构(例如添加或修改属性)时,画布会自动刷新。 @State private var showFavoritesOnly = false var filteredLandmarks: [Landmark] { landmarks.filter { landmark in (!showFavoritesOnly || landmark.isFavorite) } } var body: some View { NavigationSplitView { List { // 先设置一个恩牛 Toggle(isOn: $showFavoritesOnly) { Text("Favorites only") } // 恩牛下面才是浏览 ForEach(filteredLandmarks) { landmark in NavigationLink { LandmarkDetail(landmark: landmark) } label: { LandmarkRow(landmark: landmark) } } } // 如果这个值改变则使用动画 .animation(.default, value: filteredLandmarks) .navigationTitle("Landmarks") } detail: { Text("Select a Landmark") } } } #Preview { LandmarkList() }
使用 observation 进行存储
在视图中采用 model 对象
为每个路标创建收藏夹按钮