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
右侧展示的就是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: "输入你想搜索的名字")
}
}
}
运行效果如下,当输入名字时筛选出适合的数据:
Searchable
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")
}
渐渐接触到的控件也越来越多了,这些都是做好项目必经之路,打好基础才能搭建高楼大厦,我们未完待续!!!
网友评论