尝试从 WWDC Session 重新创建 SwiftUI-based 应用程序时出现问题
Issues While Trying To Recreate SwiftUI-based App From WWDC Session
我正在尝试重新创建 Session 204 中演示的 SwiftUI 项目,但 运行 遇到了一些特殊问题。
我是在观看 session 时写下这篇文章的:https://developer.apple.com/videos/play/wwdc2019/204
这是我的代码-
ContentView.swift:
import SwiftUI
struct ContentView : View {
@ObjectBinding var store = RoomStore()
var body: some View {
NavigationView {
List {
Section {
Button(action: addRoom) {
Text("Add Room")
}
}
Section {
ForEach(store.rooms) { room in //Error: Cannot convert value of type '(Room) -> RoomCell' to expected argument type '(_) -> _'
RoomCell(room: room)
}
.onDelete(perform: delete)
.onMove(perform: move)
}
}
.navigationBarTitle(Text("Rooms") )
.NavigationBarItems(trailing: EditButton())
.listStyle(.grouped)
}
}
func addRoom() {
store.rooms.append(Room(name: "Hall 2", capacity: 2000))
}
func delete(at offsets: IndexSet) {
store.rooms.remove(atOffsets: offsets) //Error: Incorrect argument label in call (have 'atOffsets:', expected 'at:')
}
func move(from source: IndexSet, to destination: Int) {
store.rooms.move(fromOffsets: source, toOffset: destination) //Error: Value of type '[Room]' has no member 'move'; did you mean 'remove'?
}
}
#if DEBUG
struct ContentView_Previews : PreviewProvider {
static var previews: some View {
Group {
ContentView(store: RoomStore(rooms: testData))
ContentView(store: RoomStore(rooms: testData))
.environment(\.sizeCategory, .extraExtraExtraLarge)
ContentView(store: RoomStore(rooms: testData))
.environment(\.colorScheme, .dark)
ContentView(store: RoomStore(rooms: testData))
.environment(\.layoutDirection, .rightToLeft)
.environment(\.locale, Locale(identifier: "ar"))
}
}
}
#endif
struct RoomCell : View {
let room: Room
var body: some View {
return NavigationButton(destination: RoomDetail(room: room) )
{
Image(room.thumbnailName)
.cornerRadius(8)
VStack(alignment: .leading) {
Text (room.name)
Text ("\(room.capacity) peopje")
.font(.subheadline)
.foregroundColor(.secondary)
}
}
}
}
Room.swift:
import SwiftUI
struct Room {
var id = UUID()
var name: String
var capacity: Int
var hasVideo: Bool = false
var imageName: String { return name }
var thumbnailName: String { return name + "Thumb" }
}
#if DEBUG
let testData = [
Room(name: "Observation Deck", capacity: 6, hasVideo: true),
Room(name: "Executive Suite", capacity: 8, hasVideo: false),
Room(name: "Charter Jet", capacity: 16, hasVideo: true),
Room(name: "Dungeon", capacity: 10, hasVideo: true),
Room(name: "Panorama", capacity: 12, hasVideo: false),
Room(name: "Oceanfront", capacity: 8, hasVideo: false),
Room(name: "Rainbow Room", capacity: 10, hasVideo: true),
Room(name: "Pastoral", capacity: 7, hasVideo: false),
Room(name: "Elephant Room", capacity: 1, hasVideo: false),
]
#endif
RoomDetail.swift:
import SwiftUI
struct RoomDetail : View {
let room: Room
@State private var zoomed = false
var body: some View { //Error: Function declares an opaque return type, but has no return statements in its body from which to infer an underlying type
ZStack(alignment: .topLeading) {
Image(room.imageName )
.resizable()
.aspectRatio(contentMode: zoomed ? .fill : .fit)
.navigationBarTitle(Text(room.name), displayMode:
.inline)
.tapAction { withAnimation(.basic(duration: 2)) {
self.zoomed.toggle() } }
}
.frame(minWidth: 0, maxWidth: .infinity, minHeight:
0, maxHeight: .infinity)
if room.hasVideo && !zoomed {
Image(systemName: "video. fill")
.font(.title)
.padding(.all)
.transition(.move(edge: .leading) )
}
}
}
#if DEBUG
struct RoomDetail_Previews : PreviewProvider {
static var previews: some View {
Group {
NavigationView { RoomDetail(room: testData[0]) }
NavigationView { RoomDetail(room: testData[1]) }
}
}
}
#endif
RoomStore.swift:
import SwiftUI
import Combine
class RoomStore : BindableObject {
var rooms: [Room] {
didSet { didChange.send(Void()) } //Solved
}
init(rooms: [Room] = []) {
self.rooms = rooms
}
var didChange = PassthroughSubject<Void, Never>()
}
错误消息包含在上下文中,作为注释,在上面的代码中。
你试过通过 Void() 吗?
class RoomStore : BindableObject {
var rooms: [Room] {
didSet { didChange.send(Void()) }
}
init(rooms: [Room] = []) {
self.rooms = rooms
}
var didChange = PassthroughSubject<Void, Never>()
}
根据 https://developer.apple.com/tutorials/swiftui/handling-user-input 第 4 节第 2 步,PassthroughSubject 应采用您要绑定的 class 的类型,而不是 Void。所以它似乎更正确(并且有效)。
var didChange = PassthroughSubject<RoomStore, Never>()
至于删除和移动功能,因为 Swift 5.1 数组既没有移动方法也没有 remove(atOffsets:) 方法,我只能假设他们忘记删除数组的一些自定义扩展。我也找不到在 Swift Evolution 上提到这些功能。
希望他们只是忘了告诉我们他们将在以后的版本中推出。
:) 泰奥
要在 List
中使用 Room
,您必须实施 Identifiable
协议。我也忘了它和 SwiftUI 错误消息:
Cannot convert value of type '(Room) -> RoomCell' to expected argument type '(_) -> _'
没有帮助。
import SwiftUI
struct Room: Identifiable {
let id = UUID()
...
}
要删除和移动你可以使用这样的东西:
func delete(at offsets: IndexSet) {
offsets.sorted { [=10=] > }.forEach { store.rooms.remove(at: [=10=]) }
}
func move(from source: IndexSet, to destination: Int) {
source.sorted { [=10=] > }.forEach { store.rooms.insert(store.rooms.remove(at: [=10=]), at: destination) }
}
对于此错误://错误:无法将类型“(Room) -> RoomCell”的值转换为预期的参数类型“(_) -> _”
像这样为您的列表模型实施可识别协议,
struct Room: Identifiable
对于这个://错误:调用中的参数标签不正确(有 'atOffsets:',预期 'at:')
我认为这不是你的问题:) 但你可以使用这样的东西,
guard let index = Array(offset).first else { return }
store.rooms.remove(at: index)
对于这个://错误:类型“[Room]”的值没有成员'move';你是说 'remove'? 和以前一样,你可以使用那段代码移动
guard let sourceIndex = Array(source).first else { return }
store.rooms.insert(roomStore.rooms.remove(at: sourceIndex), at: destination)
您可以查看完整的源代码,
https://github.com/ilyadaberdil/iOS-Samples/tree/master/SwiftUI-Sample
在Xcode11测试版4
The BindableObject protocol’s requirement is now willChange
instead of didChange
, and should now be sent before the object changes rather than after it changes. This change allows for improved coalescing of change notifications.
class RoomStore: BindableObject {
var rooms: [Room] {
didSet { willChange.send() }
}
init(rooms: [Room] = []) {
self.rooms = rooms
}
var willChange = PassthroughSubject<Void, Never>()
}
要删除和移动,请使用以下代码。
func delete(at offsets: IndexSet) {
store.rooms.remove(atOffsets: offsets)
}
func move(from source: IndexSet, to destination: Int) {
store.rooms.move(fromOffsets: source, toOffset: destination)
}
Rooms
必须是 @Published
class RoomStore: ObservableObject {
@Published var rooms: [Room] {
didSet {
willChange.send()
}
}
init(rooms: [Room] = []) {
self.rooms = rooms
}
var willChange = PassthroughSubject<Void, Never>()
}
我正在尝试重新创建 Session 204 中演示的 SwiftUI 项目,但 运行 遇到了一些特殊问题。
我是在观看 session 时写下这篇文章的:https://developer.apple.com/videos/play/wwdc2019/204
这是我的代码-
ContentView.swift:
import SwiftUI
struct ContentView : View {
@ObjectBinding var store = RoomStore()
var body: some View {
NavigationView {
List {
Section {
Button(action: addRoom) {
Text("Add Room")
}
}
Section {
ForEach(store.rooms) { room in //Error: Cannot convert value of type '(Room) -> RoomCell' to expected argument type '(_) -> _'
RoomCell(room: room)
}
.onDelete(perform: delete)
.onMove(perform: move)
}
}
.navigationBarTitle(Text("Rooms") )
.NavigationBarItems(trailing: EditButton())
.listStyle(.grouped)
}
}
func addRoom() {
store.rooms.append(Room(name: "Hall 2", capacity: 2000))
}
func delete(at offsets: IndexSet) {
store.rooms.remove(atOffsets: offsets) //Error: Incorrect argument label in call (have 'atOffsets:', expected 'at:')
}
func move(from source: IndexSet, to destination: Int) {
store.rooms.move(fromOffsets: source, toOffset: destination) //Error: Value of type '[Room]' has no member 'move'; did you mean 'remove'?
}
}
#if DEBUG
struct ContentView_Previews : PreviewProvider {
static var previews: some View {
Group {
ContentView(store: RoomStore(rooms: testData))
ContentView(store: RoomStore(rooms: testData))
.environment(\.sizeCategory, .extraExtraExtraLarge)
ContentView(store: RoomStore(rooms: testData))
.environment(\.colorScheme, .dark)
ContentView(store: RoomStore(rooms: testData))
.environment(\.layoutDirection, .rightToLeft)
.environment(\.locale, Locale(identifier: "ar"))
}
}
}
#endif
struct RoomCell : View {
let room: Room
var body: some View {
return NavigationButton(destination: RoomDetail(room: room) )
{
Image(room.thumbnailName)
.cornerRadius(8)
VStack(alignment: .leading) {
Text (room.name)
Text ("\(room.capacity) peopje")
.font(.subheadline)
.foregroundColor(.secondary)
}
}
}
}
Room.swift:
import SwiftUI
struct Room {
var id = UUID()
var name: String
var capacity: Int
var hasVideo: Bool = false
var imageName: String { return name }
var thumbnailName: String { return name + "Thumb" }
}
#if DEBUG
let testData = [
Room(name: "Observation Deck", capacity: 6, hasVideo: true),
Room(name: "Executive Suite", capacity: 8, hasVideo: false),
Room(name: "Charter Jet", capacity: 16, hasVideo: true),
Room(name: "Dungeon", capacity: 10, hasVideo: true),
Room(name: "Panorama", capacity: 12, hasVideo: false),
Room(name: "Oceanfront", capacity: 8, hasVideo: false),
Room(name: "Rainbow Room", capacity: 10, hasVideo: true),
Room(name: "Pastoral", capacity: 7, hasVideo: false),
Room(name: "Elephant Room", capacity: 1, hasVideo: false),
]
#endif
RoomDetail.swift:
import SwiftUI
struct RoomDetail : View {
let room: Room
@State private var zoomed = false
var body: some View { //Error: Function declares an opaque return type, but has no return statements in its body from which to infer an underlying type
ZStack(alignment: .topLeading) {
Image(room.imageName )
.resizable()
.aspectRatio(contentMode: zoomed ? .fill : .fit)
.navigationBarTitle(Text(room.name), displayMode:
.inline)
.tapAction { withAnimation(.basic(duration: 2)) {
self.zoomed.toggle() } }
}
.frame(minWidth: 0, maxWidth: .infinity, minHeight:
0, maxHeight: .infinity)
if room.hasVideo && !zoomed {
Image(systemName: "video. fill")
.font(.title)
.padding(.all)
.transition(.move(edge: .leading) )
}
}
}
#if DEBUG
struct RoomDetail_Previews : PreviewProvider {
static var previews: some View {
Group {
NavigationView { RoomDetail(room: testData[0]) }
NavigationView { RoomDetail(room: testData[1]) }
}
}
}
#endif
RoomStore.swift:
import SwiftUI
import Combine
class RoomStore : BindableObject {
var rooms: [Room] {
didSet { didChange.send(Void()) } //Solved
}
init(rooms: [Room] = []) {
self.rooms = rooms
}
var didChange = PassthroughSubject<Void, Never>()
}
错误消息包含在上下文中,作为注释,在上面的代码中。
你试过通过 Void() 吗?
class RoomStore : BindableObject {
var rooms: [Room] {
didSet { didChange.send(Void()) }
}
init(rooms: [Room] = []) {
self.rooms = rooms
}
var didChange = PassthroughSubject<Void, Never>()
}
根据 https://developer.apple.com/tutorials/swiftui/handling-user-input 第 4 节第 2 步,PassthroughSubject 应采用您要绑定的 class 的类型,而不是 Void。所以它似乎更正确(并且有效)。
var didChange = PassthroughSubject<RoomStore, Never>()
至于删除和移动功能,因为 Swift 5.1 数组既没有移动方法也没有 remove(atOffsets:) 方法,我只能假设他们忘记删除数组的一些自定义扩展。我也找不到在 Swift Evolution 上提到这些功能。
希望他们只是忘了告诉我们他们将在以后的版本中推出。
:) 泰奥
要在 List
中使用 Room
,您必须实施 Identifiable
协议。我也忘了它和 SwiftUI 错误消息:
Cannot convert value of type '(Room) -> RoomCell' to expected argument type '(_) -> _'
没有帮助。
import SwiftUI
struct Room: Identifiable {
let id = UUID()
...
}
要删除和移动你可以使用这样的东西:
func delete(at offsets: IndexSet) {
offsets.sorted { [=10=] > }.forEach { store.rooms.remove(at: [=10=]) }
}
func move(from source: IndexSet, to destination: Int) {
source.sorted { [=10=] > }.forEach { store.rooms.insert(store.rooms.remove(at: [=10=]), at: destination) }
}
对于此错误://错误:无法将类型“(Room) -> RoomCell”的值转换为预期的参数类型“(_) -> _” 像这样为您的列表模型实施可识别协议,
struct Room: Identifiable
对于这个://错误:调用中的参数标签不正确(有 'atOffsets:',预期 'at:') 我认为这不是你的问题:) 但你可以使用这样的东西,
guard let index = Array(offset).first else { return }
store.rooms.remove(at: index)
对于这个://错误:类型“[Room]”的值没有成员'move';你是说 'remove'? 和以前一样,你可以使用那段代码移动
guard let sourceIndex = Array(source).first else { return }
store.rooms.insert(roomStore.rooms.remove(at: sourceIndex), at: destination)
您可以查看完整的源代码, https://github.com/ilyadaberdil/iOS-Samples/tree/master/SwiftUI-Sample
在Xcode11测试版4
The BindableObject protocol’s requirement is now
willChange
instead ofdidChange
, and should now be sent before the object changes rather than after it changes. This change allows for improved coalescing of change notifications.
class RoomStore: BindableObject {
var rooms: [Room] {
didSet { willChange.send() }
}
init(rooms: [Room] = []) {
self.rooms = rooms
}
var willChange = PassthroughSubject<Void, Never>()
}
要删除和移动,请使用以下代码。
func delete(at offsets: IndexSet) {
store.rooms.remove(atOffsets: offsets)
}
func move(from source: IndexSet, to destination: Int) {
store.rooms.move(fromOffsets: source, toOffset: destination)
}
Rooms
必须是 @Published
class RoomStore: ObservableObject {
@Published var rooms: [Room] {
didSet {
willChange.send()
}
}
init(rooms: [Room] = []) {
self.rooms = rooms
}
var willChange = PassthroughSubject<Void, Never>()
}