1、添加单击手势 -- .onTapGesture || TapGesture
import SwiftUI
struct ContentView : View
{
@State var isPressed = false
var body: some View
{
let tapGesture = TapGesture().onEnded
{ _ in
self.isPressed.toggle()
}
return Circle()
.fill(Color.orange)
.frame(width: 240, height: 240)
.scaleEffect(isPressed ? 1.5 : 1)
.animation(.default)
// .onTapGesture {
// }
.gesture(tapGesture)
}
}
2、添加双击手势 -- onTapGesture(count: 2)
import SwiftUI
struct ContentView : View
{
@State var isPressed = false
var body: some View
{
Circle()
.fill(Color.orange)
.frame(width: 240, height: 240)
.scaleEffect(isPressed ? 1.4 : 1)
.animation(.default)
.onTapGesture(count: 2)
{
self.isPressed.toggle()
}
}
}
3、添加长按手势 -- LongPressGesture
import SwiftUI
struct ContentView : View
{
@GestureState var isLongPressing = false
@State var isLongPressed = false
var body: some View
{
let longPressGesture = LongPressGesture()
.updating($isLongPressing)
{ value, state, transcation in
print(value, state, transcation)
state = value
}
.onEnded { (value) in
print(value)
self.isLongPressed = true
}
return Circle()
.fill(Color.orange)
.frame(width: 240, height: 240)
.scaleEffect(isLongPressed ? 1.4 : 1)
.animation(.default)
.gesture(longPressGesture)
}
}
4、添加旋转手势 -- RotationGesture
import SwiftUI
struct ContentView : View
{
@State var angle = 0.0
var body: some View
{
let rotationGesture = RotationGesture(minimumAngleDelta: Angle.init(degrees: 20))
.onChanged({ (angle) in
self.angle = angle.degrees
})
.onEnded { (angle) in
print(self.angle)
}
return Image("couples")
.rotationEffect(Angle.init(degrees: self.angle))
.gesture(rotationGesture)
}
}
5、添加拖动手势 -- DragGesture
import SwiftUI
struct ContentView : View
{
@State var offset: CGSize = .zero
var body: some View
{
let dragGesture = DragGesture()
.onChanged { (value) in
print(value.startLocation, value.location, value.translation)
self.offset = value.translation
}
.onEnded { (value) in
if(abs(value.translation.width) >= 40 ||
abs(value.translation.height - (-260)) >= 40)
{
self.offset = .zero
}
else
{
self.offset = CGSize(width: 0, height: -260)
}
}
return VStack
{
Circle()
.fill(Color.black)
.opacity(0.1)
.frame(width: 200, height: 200)
.offset(CGSize(width: 0, height: -50))
Circle()
.fill(Color.orange)
.frame(width: 200, height: 200)
.offset(offset)
.gesture(dragGesture)
.animation(.spring())
}
}
}
6、添加多种组合手势 -- simultaneously
import SwiftUI
struct ContentView : View
{
@State var offset: CGSize = .zero
@GestureState var isLongPressing = false
@State var isLongPressed = false
var body: some View
{
let longPressGesture = LongPressGesture()
.updating($isLongPressing)
{ value, state, transcation in
print(value, state, transcation)
state = value
}
.onEnded
{ (value) in
print(value)
self.isLongPressed = true
}
let dragGesture = DragGesture()
.onChanged
{ (value) in
print(value.startLocation, value.location, value.translation)
self.offset = value.translation
}
.onEnded
{ (value) in
if(abs(value.translation.width) >= 40
|| abs(value.translation.height - (-220)) >= 40)
{
self.offset = .zero
}
else
{
self.offset = CGSize(width: 0, height: -220)
}
}
.simultaneously(with: longPressGesture)
return VStack
{
Circle()
.fill(Color.black)
.opacity(0.1)
.frame(width: 200, height: 200)
.offset(CGSize(width: 0, height: -50))
Circle()
.fill(Color.orange)
.frame(width: 200, height: 200)
.offset(offset)
.gesture(dragGesture)
.scaleEffect(isLongPressed ? 1.2 : 1)
.animation(.spring())
}
}
}
7、添加序列手势 -- SequenceGesture
import SwiftUI
struct ContentView : View
{
@State var offset: CGSize = .zero
var body: some View
{
let longPressGesture = LongPressGesture()
.onEnded
{ (value) in
print("First gesture.")
}
let dragGesture = DragGesture()
.onChanged
{ (value) in
print(value.startLocation, value.location, value.translation)
self.offset = value.translation
}
.onEnded
{ (value) in
if(abs(value.translation.width) >= 40
|| abs(value.translation.height - (-260)) >= 40)
{
self.offset = .zero
}
else
{
self.offset = CGSize(width: 0, height: -260)
}
}
let sequenceGesture = SequenceGesture(longPressGesture, dragGesture)
.onEnded
{ _ in
print("-- SequenceGesture end.")
}
return VStack
{
Circle()
.fill(Color.black)
.opacity(0.1)
.frame(width: 200, height: 200)
.offset(CGSize(width: 0, height: -50))
Circle()
.fill(Color.orange)
.frame(width: 200, height: 200)
.offset(offset)
.animation(.spring())
.gesture(sequenceGesture)
}
}
}
8、sizeCategory预览不同字体下的文本视图 -- .environment(.sizeCategory, .extraSmall)
import SwiftUI
struct ContentView : View
{
var body: some View
{
VStack
{
Text("Dynamic Type sizes")
.font(.system(size: 36))
Text("Dynamic Type sizes")
}
}
}
#if DEBUG
struct ContentView_Previews : PreviewProvider
{
static var previews: some View
{
VStack
{
Spacer()
ContentView()
.environment(\.sizeCategory, .extraSmall)
Spacer()
ContentView()
Spacer()
ContentView()
.environment(\.sizeCategory, .accessibilityExtraExtraExtraLarge)
Spacer()
}
}
}
#endif
9、动态调整字号、视图间距 -- @ScaledMetric
@ScaledMetric:被@ScaledMetric修饰的属性,当系统的Dynamic Type size发生变化时,属性的值也会发生变化。
注意: 调试时需要开启Dynamic Type size
import SwiftUI
struct ContentView: View
{
@ScaledMetric var iconSize: CGFloat = 60
@ScaledMetric private var padding: CGFloat = 10.0
var body: some View
{
VStack
{
Image(systemName: "hare")
.resizable()
.frame(width: iconSize, height: iconSize)
Text("A rabbit")
.font(.system(size: 32))
Text("\(Image(systemName: "eurosign.circle")) 38")
.font(.system(size: 17))
}
.padding(padding)
.background(Color.orange)
}
}
#if DEBUG
struct ContentView_Previews : PreviewProvider
{
static var previews: some View
{
ContentView()
.environment(\.sizeCategory, .extraExtraExtraLarge)
}
}
#endif
10、在预览窗口使用不同的模拟器预览用户界面 -- previewDevice & previewDisplayName
import SwiftUI
struct ContentView : View
{
var body: some View
{
VStack
{
Image("couples")
Text("Two items of the same kind")
.font(.system(size: 30))
Text("The couple were married last week. Only one couple left on the dance floor.")
}
.padding()
}
}
#if DEBUG
struct ContentView_Previews : PreviewProvider
{
static var previews: some View
{
Group
{
ContentView()
.previewDevice(PreviewDevice(rawValue: "iPhone 12 Pro Max"))
.previewDisplayName("Device-Max")
ContentView()
.previewDevice(PreviewDevice(rawValue: "iPhone 12 mini"))
.previewDisplayName("Device-Mini🍎")
}
}
}
#endif
11、正常模式和黑暗模式 -- .environment(.colorScheme, .light)
import SwiftUI
struct ContentView : View
{
var body: some View
{
VStack(alignment: .center, spacing: 20)
{
Text("Dynamic Type sizes")
.font(.system(size: 48))
.foregroundColor(Color.secondary)
Text("Dynamic Type sizes")
.foregroundColor(Color.accentColor)
Image(systemName: "star.fill")
.foregroundColor(Color.secondary)
.font(.system(size: 64))
Image("couples")
}
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
.background(Color.black)
.edgesIgnoringSafeArea(.all)
}
}
#if DEBUG
struct ContentView_Previews : PreviewProvider
{
static var previews: some View
{
HStack(spacing:0)
{
ContentView()
.environment(\.colorScheme, .light)
ContentView()
.environment(\.colorScheme, .dark)
}
}
}
#endif
12、上下文菜单 -- contextMenu
import SwiftUI
struct ContentView : View
{
var body: some View
{
VStack(spacing: 20)
{
Image(systemName: "gear")
.font(.system(size: 64))
Text("Settings")
.font(.system(size: 14))
}
.contextMenu
{
VStack
{
Button(action: {
print("Leave a message.")
}){
Text("Leave a message")
Image(systemName: "message")
}
Button(action: {})
{
Text("Rate the app")
Image(systemName: "star")
}
}
}
}
}
13、向用户推荐其他应用 -- StoreKit & appStoreOverlay
import SwiftUI
import StoreKit
struct ContentView: View
{
@State var showAppStoreOverlay = false
var body: some View
{
Button(action: {
self.showAppStoreOverlay.toggle()
}, label: {
Label("Show the app in app store", systemImage: "gift")
})
.appStoreOverlay(isPresented: self.$showAppStoreOverlay)
{
SKOverlay.AppConfiguration(appIdentifier: "1063100471", position: .bottomRaised)
}
}
}
14、将文档导出到iCloud -- UniformTypeIdentifiers、FileDocument & fileExporter
import SwiftUI
import UniformTypeIdentifiers
struct TextDocument: FileDocument
{
static var readableContentTypes: [UTType]
{
[.plainText]
}
var content: String
init(content: String)
{
self.content = content
}
init(filePath: String)
{
self.content = try! String(contentsOfFile:filePath, encoding: String.Encoding.utf8)
}
init(configuration: ReadConfiguration) throws
{
guard let data = configuration.file.regularFileContents,
let value = String(data: data, encoding: .utf8)
else
{
throw CocoaError(.fileReadCorruptFile)
}
content = value
}
func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper
{
return FileWrapper(regularFileWithContents: content.data(using: .utf8)!)
}
}
struct ContentView: View
{
@State private var document = TextDocument(content: "Interactive tutorials!")
@State private var document2 = TextDocument(filePath: Bundle.main.path(forResource:"demoText", ofType:"txt")!)
@State private var isPresented: Bool = false
var body: some View
{
Button(action: {
self.isPresented.toggle()
}, label: {
Label("Export the text file", systemImage: "paperplane")
})
.fileExporter(
isPresented: $isPresented,
document: document,
contentType: UTType.plainText,
defaultFilename: "demoText"
) { result in
if case .success = result {
print("Success.")
}
else {
print("Failure.")
}
}
}
}
15、实现点餐功能 -- DisclosureGroup
import SwiftUI
struct ContentView : View
{
let foodArray = ["Legumes", "Edible plants", "Baked goods", "Breads", "Dairy products", "Eggs", "Meat", "Cereals", "Rice", "Other", "Seafood"]
@State var visible = false
@State var food = ""
var body: some View
{
VStack
{
Text("Your choose: \(food)")
DisclosureGroup("Choose food", isExpanded: self.$visible)
{
ScrollView
{
LazyVStack(alignment: .leading, spacing: 20, pinnedViews: /*@START_MENU_TOKEN@*/[]/*@END_MENU_TOKEN@*/, content:
{
ForEach(foodArray, id: \.self)
{ item in
Text("* \(item)")
.onTapGesture
{
self.food = "\(item)"
self.visible.toggle()
}
}
})
.padding()
}
.frame(height: 200)
}
.padding()
.background(Color.gray.opacity(0.4))
.cornerRadius(10)
}
.padding()
}
}
16、对输入字符串进行过滤 -- onChange
import SwiftUI
struct ContentView : View
{
private let foodArray = ["Legumes", "Edible plants", "Baked goods", "Breads", "Salads", "Dairy products", "Sauces", "Eggs", "Meat", "Cereals", "Rice", "Other", "Seafood", "Sandwiches"]
@State var keyword = ""
@State var filtedFood : [String] = []
var body: some View
{
VStack
{
TextField("Choose food", text: $keyword)
.onChange(of: self.keyword, perform:
{ value in
self.filtedFood = self.foodArray.filter({
(food) -> Bool in
if self.keyword != "" && food.hasPrefix(self.keyword)
{
return true
}
else
{
return false
}
})
})
LazyVStack(alignment:.leading, content:
{
ForEach(filtedFood, id: \.self)
{ item in
Text("* \(item)")
.onTapGesture
{
self.keyword = item
}
}
})
}
.font(.system(size: 32))
.padding()
}
}