Các Modifier và View mới trong SwiftUI iOS 15
This post hasn't been updated for 3 years
Khám phá một số tính năng mới nhất trong SwiftUI
Chào mọi người.
Hôm nay tôi sẽ nói về các tính năng được công bố tại WWDC mà tôi thấy quan trọng trong SwiftUI.
Và tôi muốn chỉ ra rằng tôi sẽ chỉ đề cập ngắn gọn đến các chủ đề. Nếu bạn muốn biết thêm thông tin về SwiftUI, hãy xem tài liệu của Apple.
Trong ví dụ của chúng ta, tôi sẽ sử dụng mô hình này:
struct User : Identifiable{
var id = UUID()
let name: String
let age: Int
}
.searchable
Có lẽ một trong những tính năng quan trọng nhất là searchable API. Vì khi muốn search List, chúng ta phải thực hiện rất nhiều thao tác với UIViewRepresentable. Nhờ API này, chúng ta có thể thoát khỏi tất cả những rắc rối này và thực hiện hàng chục thao tác code trong một dòng duy nhất. Ngoài ra, API này phải nằm trong NavigationView.
struct ContentView: View {
@State var users: [User] = [
User(name: "Eren", age: 20),
User(name: "James", age: 22),
User(name: "Mike", age: 30)
]
@State var searchedText : String = ""
var body: some View {
NavigationView {
List(users) { user in
VStack{
Text(user.name)
Text("\(user.age)")
}
}
.searchable("Search User", text: $searchedText, suggestions: {
ForEach(searchedData) { result in
Text("Are You Looking For \(result.name)")
.searchCompletion(result.name)
}
})
.navigationTitle("Search Bar")
}
}
var searchedData: [User] {
if searchedText.isEmpty{
return []
}else{
return users.filter {
$0.name.contains(searchedText.lowercased())
}
}
}
}
.refreshable
Theo tài liệu, “Khi bạn áp dụng modifier này trên iOS và iPadOS cho chế độ xem có thể cuộn như Danh sách, chế độ xem cung cấp một cách tiêu chuẩn để người dùng làm mới nội dung. Sử dụng await expression bên trong action để làm mới dữ liệu của bạn. Refresh indicator vẫn hiển thị trong suốt thời gian hoạt động await ”
NavigationView {
List(users) { user in
VStack{
Text(user.name)
Text("\(user.age)")
}
}
.navigationTitle("Refresh control")
.refreshable {
users.append(User(name: "New Data", age: 100))
}
}
.listStyle
Một trong những tính năng được tìm kiếm nhiều nhất là tùy chỉnh separators. Nhờ API này, chúng tôi có thể ẩn separators và thay đổi màu của chúng.
NavigationView {
List {
ForEach(users) { user in
VStack{
Text(user.name)
Text("\(user.age)")
}
}
.listRowSeparatorTint(.blue)
.listRowSeparator(.hidden)
}
}
AsyncImage
AsyncImage là một dạng xem tải và hiển thị một hình ảnh một cách không đồng bộ. Bạn cũng có thể thêm một placeholder. Đó là một đặc ân tuyệt vời để có thể làm điều này mà không cần bất kỳ packages nào. Tôi nghĩ những người sẽ bị ảnh hưởng bởi điều này là Kingfisher và SDWebImage.
@State var url : String = "https://images.unsplash.com/photo-1485217988980-11786ced9454?ixid=MnwxMjA3fDF8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=2250&q=80"
var body: some View {
NavigationView {
VStack {
AsyncImage(url: URL(string: url)!, scale: 1.0) { data in
if let image = data.image {
image
.resizable()
.cornerRadius(20)
.aspectRatio(contentMode: .fit)
.frame(width: 300, height: 300)
}else {
Text("Error Image")
}
}
}
.navigationTitle("Async Image")
}
}
Material Effects
Material Effects tạo hiệu ứng mờ, còn gọi là UIViewVisualEffect.
.overlay(.ultraThinMaterial, in: RoundedRectangle(cornerRadius: 12))
Badge
Điều này tạo ra một badge cho view từ một giá trị Int. Nhưng khi chúng ta viết một điều kiện if-else ngắn bên trong badge, nó không hoạt động. Tôi nghĩ rằng có một bug.
NavigationView {
TabView{
Color.red
.tabItem({
Image(systemName: "house.fill")
Text("AKA")
})
.badge(3)
Color.blue
.tabItem({Image(systemName: "house.fill")
Text("AKA")
})
.badge(2)
}
}
.onSubmit
.onSubmit thêm một hành động để thực hiện khi người dùng gửi một giá trị cho view này. Các view khác nhau có thể có các triggers khác nhau cho hành động được cung cấp. TextField hoặc SecureField sẽ trigger action này khi người dùng nhấn phím return. Modifier này cũng có thể bind action này với một phím tắt tác vụ mặc định. Bạn có thể đặt action này trên một view riêng lẻ hoặc toàn bộ view hierarchy.
VStack{
TextField("Type Here " , text: .constant(""))
.textFieldStyle(.roundedBorder)
.padding()
}
.onSubmit {
print("Submitted text")
}
.safeAreaInstest
.safeAreaInstest hiển thị nội dung được chỉ định bên trên hoặc bên dưới modified view, giống như hover view.
VStack{
Color.white
.frame(width: UIScreen.main.bounds.width)
}
.safeAreaInset(edge: .bottom) {
RoundedRectangle(cornerRadius: 20)
.frame(height: 50)
.foregroundColor(Color.red)
.padding()
}
.interactiveDismissDisabled
Dùng để thêm một điều kiện ngăn chặn việc loại bỏ tương tác khi view được chứa trong popover hoặc sheet.
.swipeActions
Điều này thêm các custom swipe action vào một row trong list. Và chúng ta có thể thêm một button fully customizable hoặc bất kỳ view nào.
.swipeActions(edge: .trailing, allowsFullSwipe: true) {
Button {
print("Deleted Item")
} label: {
Image(systemName: "xmark")
}
}
.tint(.red)
.submitLabel
Dùng để đặt submit label cho view. Có thể thay đổi nút submit trên bàn phím là một thay đổi nhỏ nhưng rất hay.
TextField(“text” , text: .constant(“”))
.submitLabel(.done)
@FocusState
Khi chúng ta nhìn vào @FocusState, nó giúp chúng ta dễ dàng tự động hướng dẫn người dùng trên TextField.
VStack{
TextField("name" , text: .constant(""))
.focused($focusState, equals: .name)
TextField("surname" , text: .constant(""))
.focused($focusState, equals: .surname)
TextField("age" , text: .constant(""))
.focused($focusState, equals: .age)
}
.onSubmit {
switch focusState{
case .name: focusState = .surname
case .surname: focusState = .age
case.age:
print("Done")
default:
break
}
Nguồn tham khảo: New SwiftUI Modifiers and Views in iOS 15
All Rights Reserved