Проблемы при попытке воссоздать приложение на основе SwiftUI из сеанса WWDC - PullRequest
4 голосов
/ 07 июня 2019

Я пытаюсь воссоздать проект SwiftUI, продемонстрированный в сеансе 204, но сталкиваюсь с некоторыми специфическими проблемами.

Я написал это, наблюдая за сеансом, найденным здесь: 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>()
}

Сообщения об ошибках, включенные в контекст, как комментарии, в приведенном выше коде.

Ответы [ 5 ]

8 голосов
/ 08 июня 2019

Для этой ошибки: // Ошибка: невозможно преобразовать значение типа '(Room) -> RoomCell' в ожидаемый тип аргумента '(_) -> _' Внедрите опознаваемый протокол в модель списка следующим образом:

struct Room: Identifiable

Для этого: // Ошибка: неверная метка аргумента в вызове (есть 'atOffsets:', ожидается 'at:') Я думаю, что это не ваша проблема :) Но вы можете использовать что-то вроде этого,

guard let index = Array(offset).first else { return }
store.rooms.remove(at: index)

И для этого: // Ошибка: значение типа '[Room]' не имеет члена 'move'; Вы имели в виду «удалить»? Как и раньше, вы можете использовать этот фрагмент кода для перемещения

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

2 голосов
/ 07 июня 2019

Вы пробовали пройти Void ()?

class RoomStore : BindableObject {
    var rooms: [Room] {
        didSet { didChange.send(Void()) } 
    }
    init(rooms: [Room] = []) {
        self.rooms = rooms
    }
    var didChange = PassthroughSubject<Void, Never>()
}
0 голосов
/ 08 июня 2019

Для удаления и перемещения вы можете использовать что-то вроде этого:

func delete(at offsets: IndexSet) {
    offsets.sorted { $0 > $1 }.forEach { store.rooms.remove(at: $0) }
}

func move(from source: IndexSet, to destination: Int) {
    source.sorted { $0 > $1 }.forEach { store.rooms.insert(store.rooms.remove(at: $0), at: destination) }
}
0 голосов
/ 07 июня 2019

Чтобы использовать Room в вашем Списке, он должен реализовывать Идентифицируемый протокол. Я тоже забыл об этом, и сообщение об ошибке // ЗДЕСЬ ОШИБКИ: невозможно преобразовать значение типа '(Room) -> RoomCell' в ожидаемый тип аргумента '(_) -> _'. Это не полезно.

import SwiftUI

struct Room: Identifiable {
    let id = UUID() 
    ...
}
0 голосов
/ 07 июня 2019

Согласно https://developer.apple.com/tutorials/swiftui/handling-user-input разделу 4, шаг 2, объект PassthroughSubject должен принимать тип связываемого вами класса, а не Void. Так что казалось бы правильнее (и работает).

var didChange = PassthroughSubject<RoomStore, Never>()

Что касается функций удаления и перемещения, так как в массивах Swift 5.1 нет ни методов перемещения, ни удаления (atOffsets :), я могу только предположить, что они забыли удалить некоторые пользовательские расширения для массива. Я также не смог найти упоминания об этих функциях в Swift Evolution.

Здесь надеются, что они просто забыли сообщить нам, что они выйдут в более позднем выпуске.

:) Тео

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...