1、Gradient
漸變效果有四種,AngularGradient(時鐘樣式漸變)
、EllipticalGradient(橢圓漸變)
、LinearGradient(線性漸變)
、RadialGradient(雷射漸變)
,都可以直接在右上角添加視圖中找到:
//startPoint endPoint 控制漸變方向,是從上往下還是從左往右,還是左上到右下
LinearGradient(gradient: Gradient(colors: [Color.red, Color.blue, .red]), startPoint: .top, endPoint: .bottom)
//colors 從內向外發射漸變,最后一個是背景色
//startRadius 控制第一個顏色的半徑
//endRadius 控制倒數第二個半徑,中間的顏色均勻分布漸變效果
RadialGradient(gradient: Gradient(colors: [Color.red, Color.blue, .yellow, .purple]), center: .center, startRadius: 10, endRadius: 150)
其中的.center
是設置漸變從起始點(中心點)的位置,可以在代碼中試試。
2、Badge / TabView
看到Badge,就能想到是和Tabbar小圖標
相關的,SwiftUI里的Badge除了可以作為底部Tabbar小圖標外,還能在List中使用,如下所示:
List(0..<100){ i in
Text("Hello World").badge(2)
}
右側展示的就是badge,可用作
未讀消息
條數展示功能。另外不只是展示數字,也可以展示一些簡單的自定義Text:
List(0..<100){ i in
Text("Hello World").badge(Text("111").font(.largeTitle)
.foregroundColor(.orange).bold())
}
TabView就是之前的Tabbar,而之前的UITableView在SwiftUI里面被List替代了,這兩個不要搞混了。
TabView() {
Text("Tab1").badge(3).tabItem {
Image(systemName: "person")
Text("Tab1")
}.tag(1)
Text("Tab2").tabItem {
Image(systemName: "circle")
Text("Tab2")
}.tag(2)
}
如果給TabView添加.tabViewStyle(.page)
之后,TabView就變成了類似UIScrollView的左右滑動輪播圖的效果,而且還是自帶UIPageControll小圓點,如果想隱藏小圓點的話加上.page(indexDisplayMode: .never)
就行了。TabView具體的使用細節以后項目中再細說...
3、OnOpenUR
用作瀏覽器跳轉到此App的鏈接,可以根據鏈接跳轉到目標界面,緊接上面的TabView:
@State var selectionIndex = 1
@State var show = false
TabView(selection: $selectionIndex) {
Text("Tab1").badge(3).tabItem {
Image(systemName: "person")
Text("Tab1")
}.tag(1)
Text("Tab2").tabItem {
Image(systemName: "circle")
Text("Tab2")
}.tag(2)
}
.onOpenURL { url in
switch url.host {
case "Tab1":
selectionIndex = 1
case "Tab2":
selectionIndex = 2
default:
show.toggle()
}
}
.sheet(isPresented: $show) {
Text("參數錯誤")
}
info里面在URL Types中添加外鏈host(Lcr)
:
運行項目,打開瀏覽器輸入
Lcr://Tab1
即可進入App的Tab1界面,輸入Lcr://Tab2
即可進入Tab2界面,如果輸入Lcr://Tab3
就會跳出報錯彈窗。拓展
:
- URL 刪除的話可以在info.plist文件中刪除,然后重啟項目就看不到剛剛添加的URL Types了。
-
interactiveDismissDisab
上面例子中的參數錯誤sheet為presentViewController
,自帶下滑隱藏功能,如果需要關閉此功能,可以添加interactiveDismissDisab()
即可:
Text("參數錯誤").interactiveDismissDisabled()
4、Animation
SwiftUI里動畫寫法很簡潔,代碼量少,此處我們以scaleEffect
為示例:
@State var scaleAmount: CGFloat = 1
//scaleEffect 縮放動畫
//easeInOut 動畫方式 如深入淺出
//repeatForever 重復動畫
//onAppear 初始化為2時會自動開始動畫,如果為1是否已經動畫需要再研究,視覺上是沒有
Button("animation"){
scaleAmount += scaleAmount <= 1 ?1 : -1
}.font(.title).padding().background(.green).cornerRadius(20)
.scaleEffect(scaleAmount).animation(Animation.easeInOut(duration: 3).repeatForever(), value: scaleAmount)
.onAppear {
scaleAmount = 2
}
就能實現一個綠色按鈕放大縮小的動畫。
如果動畫效果不需要原路返回,加上.repeatForever(autoreverses: false)
就可以了。
5、searchale
searchable就是我們經常用的UISearchBar+UISearchController
,下面我們利用一個例子來簡單用一下:
struct ItemModel: Identifiable {
var id = UUID()
var name : String
var detailView: DetailView
}
struct DetailView: View, Identifiable {
var id = UUID()
var detail: String
var body: some View {
Text(detail).font(.largeTitle).foregroundColor(.gray).bold()
}
}
let datas: [ItemModel] = [
ItemModel(name: "Tom", detailView: DetailView(detail: "Tom喜歡吃蘋果")),
ItemModel(name: "Jim", detailView: DetailView(detail: "Jim喜歡吃香蕉")),
ItemModel(name: "Lily", detailView: DetailView(detail: "Lily喜歡吃梨")),
ItemModel(name: "Lucy", detailView: DetailView(detail: "Lucy喜歡吃橘子")),
]
class ViewModel: ObservableObject {
@Published var allItems: [ItemModel] = datas
@Published var searchString: String = ""
var filteredItems: [ItemModel] {
searchString.isEmpty ? allItems :
allItems.filter({ item in
item.name.lowercased().contains(searchString.lowercased())
})
}
}
struct ContentView: View {
@ObservedObject var vm = ViewModel()
var body: some View {
NavigationView{
List{
ForEach(vm.filteredItems) { item in
NavigationLink(item.name, destination: item.detailView)
}
}
.navigationTitle(Text("搜索頁面"))
.searchable(text: $vm.searchString,prompt: "輸入你想搜索的名字")
}
}
}
運行效果如下,當輸入名字時篩選出適合的數據:
6、NavigationView / NavigationLink
NavigationView導航欄,上方搜索示例中,List需要放入NavigationView中才有效,因為搜索框是和NavigationView綁定的,和之前的UISearchBar類似。
//第二界面界面
struct DetailView: View {
var body: some View {
//此處不用再包一層NavigationView
VStack {
//前往詳情界面
NavigationLink("detail"){
Text("Detail")
}
}
}
}
//第一界面
var body: some View {
NavigationView {
NavigationLink("look detail"){
DetailView()
}
}.navigationTitle("navi").navigationBarTitleDisplayMode(.inline)
}
NavigationLink可以理解為一種點擊可以跳轉界面的控件。
有一點讓我不理解的是,上方搜索示例中的.navigationTitle和.searchable為什么不是加在NavigationView上,而是加在List上的。在查看NavigationView源碼后,源碼中也有使用示例,我猜可能是NavigationView就是起一個包裝容器作用吧,相關的title、樣式設置放在了第一層子View上。
注意:
NavigationView只需要在最外層寫一個就好,內層界面不用再寫。
7、DatePicker
日期選擇器,樣式比之前的系統自帶的樣式好看多了,操作也方便:
@State var date = Date()
let dateRange: ClosedRange<Date> = {
let calender = Calendar.current
let startComponents = DateComponents(year: 2021, month:1, day: 1)
let endComponents = DateComponents(year: 2021, month:12, day:31, hour:23, minute:59, second:59)
return calender.date(from: startComponents)!
...
calender.date(from: endComponents)!
}()
//selection 當前選擇的日期時間
//in 范圍,設定只能在這個范圍內選擇
//displayedComponents 模式,日期事件的模式
DatePicker(selection: $date, in: dateRange, displayedComponents: [.date, .hourAndMinute]) {
//Text("\(date.description)")
}
//.frame(width: 200,height: 50)
加上選擇的范圍后,就不能選擇范圍外的日期及時間。
ClosedRange 不可數的一個范圍,SwiftUI里提供了四種表示范圍的結構:
-
CountableClosedRange
: 可數的一個閉區間范圍 支持for循環 -
CountableRange
:可數的一個開區間范圍 支持for循環 -
ClosedRange
:不可數 不支持for 循環 -
Range
:不可數 不支持for循環
8、contextMenu / Menu
一種彈窗式的選擇器,類似于微信右上角點擊?的PopView
。
@State var backgroundColor = Color.red
@State var isShow = true
Text("Hello Lcr").bold().font(.largeTitle).foregroundColor(.white).background(backgroundColor)
.contextMenu(isShow ? ContextMenu{
Button("Red"){
backgroundColor = .red
}
Button("Blue"){
backgroundColor = .blue
}
Button("Yellow"){
backgroundColor = .yellow
}
Button("Green"){
backgroundColor = .green
}
} : nil)
長按Text,會彈出選擇器,切換Text背景顏色,不妨動手試試吧。選項的顯示順序,從上往下排列。
Menu功能和樣式上也是和contextMenu一樣的,但單擊就行,不需長按,寫法也簡便點。
Menu("Menu"){
Text("選項一")
Text("選項二")
Text("選項三")
Button("Blue"){
backgroundColor = .blue
}
Button("Yellow"){
backgroundColor = .yellow
}
Button("Green"){
backgroundColor = .green
}
}.font(.largeTitle)
Menu選項的顯示順序,從下往上排列。Menu可以無限嵌套。
9、Map
地圖的使用很簡單,導入MapKit框架,把傳統的MKMapView
轉化成SwiftUI里的View
,那就需要自定義的MapView遵循UIViewRepresentable
協議,實現相關方法:
import SwiftUI
import MapKit
struct MapView: UIViewRepresentable {
func makeUIView(context: UIViewRepresentableContext<MapView>) -> MKMapView {
let mapView = MKMapView()
let locationManager = CLLocationManager()
mapView.delegate = context.coordinator
mapView.userTrackingMode = .followWithHeading
locationManager.requestAlwaysAuthorization()
return mapView
}
func updateUIView(_ view: MKMapView, context: UIViewRepresentableContext<MapView>) {
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
}
class Coordinator: NSObject, MKMapViewDelegate {
var parent: MapView
init(_ parent: MapView) {
self.parent = parent
}
func mapViewDidChangeVisibleRegion(_ mapView: MKMapView) {
print("----", mapView.centerCoordinate)
}
func mapView(_ mapView: MKMapView, didUpdate userLocation: MKUserLocation) {
mapView.centerCoordinate = userLocation.coordinate
}
}
struct ContentView: View {
var body: some View {
MapView().ignoresSafeArea()
}
}
協議中的makeUIView
方法返回一個MKMapView對象。
如果需要用到地圖的相關代理方法,就要添加代理類Coordinator去遵守MKMapViewDelegate
協議,然后設置代理及在makeCoordinator
方法將二者綁定。
10、List
SwiftUI里List 替代了UITableView
,結構上也模仿了UITableView及Cell之間的結構,使用起來效率是真的高,代碼量少:
List{
ForEach(0..<100){
Text("Cell \($0)").listRowSeparator(.hidden)
}
}.font(.largeTitle).listStyle(.plain)
幾句代碼就完成了一個列表的創建。
分組用法如下:
List{
ForEach(0..<3) { _ in
Section {
ForEach(0..<6){
Text("Cell \($0)")
.listRowSeparator(.hidden)
}
} header: {
Text("Header").foregroundColor(.white)
.padding().background(.blue)
}
}
}.font(.largeTitle).listStyle(.grouped)
即可創建分組List,.listStyle
為列表展示樣式,當設置為.listStyle(.plain)時,組頭可懸停在頂部;當設置.listStyle(.sidebar)時每一組都可以收縮及放開,很方便。
如果需要給每個“Cell”添加操作比如刪除,那就需要給"Cell"的內容添加.swipeActions()
:
//edge 設置按鈕的位置,是左側還是右側滑出
//allowsFullSwipe 滑動的距離長短區別
Text("Cell \($0)")
.listRowSeparator(.hidden)
.swipeActions(edge: .trailing, allowsFullSwipe: true) {
Button("delete"){
print("delete")
}
}
List默認自帶頂部刷新功能(底部加載功能暫時沒有提供),給List添加.refreshable()
即可滿足一般的刷新需求,如果想要加文字圖片啥的就需要自定義了。
.refreshable {
print("refresh")
}
漸漸接觸到的控件也越來越多了,這些都是做好項目必經之路,打好基礎才能搭建高樓大廈,我們未完待續!!!