SwiftUI: невозможно вызвать .frame внутри цикла ForEach - PullRequest
0 голосов
/ 31 января 2020

TL; DR: Как изменить содержимое внутри структуры ForEach?

Ниже приведена отдельная игровая площадка, в которой вызов frame() в порядке на простом body метод, но является синтаксической ошибкой при переносе в ZStack/ForEach l oop.

import UIKit
import SwiftUI

struct Item: Identifiable {
    var id = UUID()
    var name: String
    init(_ name:String) { self.name = name }
}

let items = [
    Item("a"), Item("b"), Item("c")
]

struct ContentView: View {    
    var body: some View {
        return Image("imageName")
            .resizable()
            .frame(width:0, height:0)          // This compiles.
    }

   var body2: some View {
        ZStack {
            ForEach(items) { item -> Image in   // Return type required...
            let i = item                        // ...because of this line.
            return Image(i.name)
                    .resizable()                // Parens required.
                    .frame(width: 0, height: 0) // Compile error.
            }
        }
    }
}

Обратите внимание на строку let i = item. Он поддерживает код в моем приложении, который выполняет некоторые вычисления. Тот факт, что ForEach замыкание не является одиночным выражением , привело к тому, что компилятор пожаловался

Невозможно определить тип возврата сложного замыкания; добавить явный тип для устранения неоднозначности.

, что побудило меня добавить тип возвращаемого значения. Но , что вызывает топи c этого вопроса, ошибка компилятора:

Невозможно преобразовать возвращаемое выражение типа 'some View' в возвращаемый тип 'Image'

Похоже, что возвращаемое значение вызова frame() не Image, а ModifiedContent<SwiftUI.Image, SwiftUI._FrameLayout>.

Я обнаружил (благодаря комментаторам!) Я могу обойти это, (каким-то образом) сократив замыкание ForEach до одного выражения, что делает выводным тип возвращаемого значения, который я затем могу удалить. Это мой единственный выход?

Ответы [ 3 ]

0 голосов
/ 31 января 2020

Это здесь

ForEach(items) { item -> Image in

вы явно указываете, что замыкание возвращает Image, но frame возвращает some View, поэтому несоответствие типов и ошибка компилятора.

Использовать так же, как ниже и будет работать

ForEach(items) { item in
0 голосов
/ 04 февраля 2020

Насколько я могу судить, это может быть только ограничением Swift, аналогичным или частью этой проблемы вывода типов: Почему компилятор Swift не может определить тип этого замыкания?

Мой обходной путь - добавить функциональность в структуру Item. Теперь все вычисления, необходимые внутри замыкания ForEach, предоставляются экземпляром item, встроенным в инициализатор Image, например:

var body3: some View {
        ZStack {
            ForEach(items) { item in        // No return type specified.

            // let (width, height) = ...    // Remove pre-calculations that
                                            // confused the compiler.
            Image(item.name)
                .resizable()
                .frame(
                    width : item.width,     // All needed data are
                    height: item.height     // provided in closure param.
                )
            }
        }
    }
}

Я допускаю, что это более функционально идиоматически Хотя я предпочитаю ясность нескольких хорошо подобранных заданий, предшествующих звонку. (Если расчеты для заданий не имеют побочных эффектов, то это по сути SSA-стиль, который должен пройти тест на запах FP).

Поэтому я называю это «ответом», даже «решением» », Хотя я все еще могу пожаловаться на бесполезные сообщения об ошибках. Но это известная проблема, и более умные люди, чем я, уже занимаются этим.

0 голосов
/ 31 января 2020

Теперь я sh, мне придется подумать. Но чтобы получить эту первую ошибку на go, необходимо согласовать Item с идентифицируемым.

struct Item: Identifiable {
  var id = UUID()
  var name: String
  var size: CGSize = .zero
}

Мне также пришлось написать собственный модификатор для использования элемента для создания фрейма. Мы, вероятно, захотим использовать CGRect для создания me sh, чтобы каким-то образом связываться с источником изображения.

extension View {
  func frame(with size: CGSize) -> some View {
    frame(width: size.width, height: size.height)
  }
}

Тогда ваше тело будет выглядеть примерно так.

var body: some View {
  ZStack {
    ForEach(items) { item in
      Image(item.name).resizable().frame(with: item.size)
    }
  }
}
...