programing

ForEach를 사용하여 사전을 통해 반복되는 Swift UI

megabox 2023. 8. 7. 22:27
반응형

ForEach를 사용하여 사전을 통해 반복되는 Swift UI

다음을 반복할 수 있는 방법이 있습니까?Dictionary순식간에ForEach루프? Xcode는 말합니다.

일반 구조 'ForEach'를 사용하려면 '[String:Int]'가 'RandomAccessCollection'을(를) 따라야 합니다.

그래서 스위프트 사전이 다음을 준수하도록 만드는 방법이 있습니까?RandomAccessCollection아니면 사전이 순서가 없기 때문에 가능하지 않습니까?

제가 시도한 한 가지는 사전의 키를 반복하는 것입니다.

let dict: [String: Int] = ["test1": 1, "test2": 2, "test3": 3]
...
ForEach(dict.keys) {...}

그렇지만keys의 배열이 아닙니다.Strings, 유형은Dictionary<String, Int>.Keys(언제 변경되었는지 확실하지 않음).제가 사전을 가져와서 키 배열을 반환하는 도우미 기능을 작성할 수 있다는 것을 알고 있습니다. 그리고 나서 그 배열을 반복할 수 있습니다. 하지만 그것을 하는 기본적인 방법이 없을까요, 아니면 더 우아한 방법이 있을까요?연장할 수 있습니까?Dictionary그리고 그것을 준수하게 하라.RandomAccessCollection아니면 뭔가?

사전을 정렬하여 (키, 값) 쌍의 튜플 배열을 얻은 다음 사용할 수 있습니다.

struct ContentView: View {
    let dict = ["key1": "value1", "key2": "value2"]
    
    var body: some View {
        List {
            ForEach(dict.sorted(by: >), id: \.key) { key, value in
                Section(header: Text(key)) {
                    Text(value)
                }
            }
        }
    }
}

또는 사전 키를 루프합니다.

struct ContentView: View {
    let dict = ["key1": "value1", "key2": "value2"]
    
    var body: some View {
        List {
            ForEach(Array(dict.keys), id: \.self) { key in
                Section(header: Text(key)) {
                    Text(dict[key] ?? "")
                }
            }
        }
    }
}

순서가 없기 때문에, 그것을 배열로 넣는 것 밖에 방법이 없습니다. 꽤 간단합니다.하지만 배열 순서는 다를 것입니다.

struct Test : View {
let dict: [String: Int] = ["test1": 1, "test2": 2, "test3": 3]
var body: some View {
    let keys = dict.map{$0.key}
    let values = dict.map {$0.value}

    return List {
        ForEach(keys.indices) {index in
            HStack {
                Text(keys[index])
                Text("\(values[index])")
            }
        }
    }
}
}

#if DEBUG
struct Test_Previews : PreviewProvider {
    static var previews: some View {
        Test()
    }
}
#endif

정렬된 사전

WWDC21에서 Apple은 다음을 포함하는 패키지를 발표했습니다.OrderedDictionary(그 중에서도)

이제, 우리는 다음과 같이 교체하면 됩니다.

let dict: [String: Int] = ["test1": 1, "test2": 2, "test3": 3]

포함:

let dict: OrderedDictionary = ["test1": 1, "test2": 2, "test3": 3]

또는 다음 중 하나를 다른 하나에서 시작할 수 있습니다.

let dict: [String: Int] = ["test1": 1, "test2": 2, "test3": 3]
let orderedDict = OrderedDictionary(uniqueKeys: dict.keys, values: dict.values)

참고만 해주세요.dict순서가 없습니다. 정렬할 수 있습니다.orderedDict일관된 명령을 실행합니다.


Swift에서 사용할 수 있는 예는 다음과 같습니다.UI 보기:

import Collections
import SwiftUI

struct ContentView: View {
    let dict: OrderedDictionary = ["test1": 1, "test2": 2, "test3": 3]

    var body: some View {
        VStack {
            ForEach(dict.keys, id: \.self) {
                Text("\($0)")
            }
        }
    }
}

참고: 현재Collections별도의 패키지로 사용할 수 있으므로 프로젝트로 가져와야 합니다.


자세한 내용은 여기에서 확인할 수 있습니다.

간단한 대답: 아니오.

당신이 정확히 지적했듯이, 사전은 순서가 없습니다.ForEach는 컬렉션의 변경 사항을 확인합니다.이러한 변경사항에는 삽입, 삭제, 이동 및 업데이트가 포함됩니다.이러한 변경 사항이 발생하면 업데이트가 트리거됩니다.참조: https://developer.apple.com/videos/play/wwdc2019/204/ 46:10:

A For Each 컬렉션의 변경 사항을 자동으로 확인합니다.

저는 당신이 그 강연을 보는 것을 추천합니다 :)

다음과 같은 이유로 ForEach를 사용할 수 없습니다.

  1. 그것은 수집품을 보고 움직임을 감시합니다.정리되지 않은 사전으로는 불가능합니다.
  2. 할 때(예 뷰예재때할용사를예()UITableView될 수 때 합니다.List의 지원을 받습니다.UITableViewCells그리고 내 생각엔ForEach동일한 작업을 수행하고 있음), 표시할 셀을 계산해야 합니다.데이터 원본에서 인덱스 경로를 쿼리하여 이 작업을 수행합니다.논리적으로 말하자면, 데이터 소스가 순서가 없으면 인덱스 경로가 쓸모가 없습니다.

Xcode: 11.4.1~

    ...
    var testDict: [String: Double] = ["USD:": 10.0, "EUR:": 10.0, "ILS:": 10.0]
    ...
    ForEach(testDict.keys.sorted(), id: \.self) { key in
        HStack {
            Text(key)
            Text("\(testDict[key] ?? 1.0)")
        }
    }
    ...

자세한 내용:

final class ViewModel: ObservableObject {
    @Published
    var abstractCurrencyCount: Double = 10
    @Published
    var abstractCurrencytIndex: [String: Double] = ["USD:": 10.0, "EUR:": 15.0, "ILS:": 5.0]
}

struct SomeView: View {
    @ObservedObject var vm = ViewModel()
    var body: some View {
        NavigationView {
            List {
                ForEach(vm.abstractCurrencytIndex.keys.sorted(), id: \.self) { key in
                    HStack {
                        Text(String(format: "%.2f", self.vm.abstractCurrencyCount))
                        Text("Abstract currency in \(key)")
                        Spacer()
                        Text(NumberFormatter.localizedString(from: NSNumber(value: self.vm.abstractCurrencytIndex[key] ?? 0.0), number: .currency))
                    }
                }
            }
        }
    }
}

swift-collection의 Ordered Dictionary(주문된 컬렉션의 일부)를 사용하는 경우 다음 작업을 수행할 수 있습니다.

var states: OrderedDictionary<Player, String>
ForEach(states.elements, id:\.key) { player, state in
    PlayerRow(player: player, state: state)
}

이 작업에 사용할 키 개체가 식별되는지 확인하고, 그렇지 않으면 ForEach(각각)에서 id 매개 변수를 조정합니다.

이 게시물에 대한 일부 답변에서 코드를 사용하여 발생한 구문 오류는 제가 제 문제에 대한 해결책을 찾는 데 도움이 되었습니다.

다음이 포함된 사전 사용:

  • key 꾸짖음CoreDataEntity;
  • value해당 수 관(유)에서의엔인수스턴스티터해NSSet).

저는 @J의 답을 강조합니다.순서가 지정되지 않은/무작위 수집에 대한 실험계획법 및 주석Dictionary 셀 테보셀과기 (AppKit))과 에 가장 수 .NSTableView 유이킷UITableView스위프트 UIList행)을 선택합니다.

이후에는 어레이로 작업할 수 있도록 코드를 재구성할 예정입니다.

하지만 만약 당신이 일을 해야 한다면,Dictionary다음은 제 작업 솔루션입니다.

struct MyView: View {
    
    var body: some View {
        
        // ...code to prepare data for dictionary...

        var dictionary: [CoreDataEntity : Int] = [:]

        // ...code to create dictionary...

        let dictionarySorted = dictionary.sorted(by: { $0.key.name < $1.key.name })
        // where .name is the attribute of the CoreDataEntity to sort by
        
        VStack {  // or other suitable view builder/s
            ForEach(dictionarySorted, id: \.key) { (key, value) in
                Text("\(value) \(key.nameSafelyUnwrapped)")
            }
        }
    }
}

extension CoreDataEntity {
    var nameSafelyUnwrapped: String {
        guard let name = self.name else {
            return String() // or another default of your choosing
        }
        return name
    }
}

가장 깨끗한 방법은 (이것만을 위해 패키지 종속성을 설정하지 않으려면) 대신 Tuples를 대신 사용하는 것이라고 생각합니다.

let myTuple: [(key: String, value: Int)] = [("test1": 1), ("test2": 2), ("test3": 3)]
...
ForEach(myTuple, id:\.0) {...}

저는 열거형/Int 쌍 사전을 통해서도 이 문제를 해결하려고 했습니다.Andre가 제안한 대로 효과적으로 배열로 변환하고 있지만, 지도를 사용하는 대신 키를 주조합니다.

enum Fruits : Int, CaseIterable {
    case Apple = 0
    case Banana = 1
    case Strawberry = 2
    case Blueberry = 3
}

struct ForEachTest: View {
    var FruitBasket : [Fruits: Int] = [Fruits.Apple: 5, Fruits.Banana : 8, Fruits.Blueberry : 20]
    var body: some View {
        VStack {
            ForEach([Fruits](FruitBasket.keys), id:\Fruits.hashValue) { f in
                Text(String(describing: f) + ": \(self.FruitBasket[f]!)")
            }
        }
    }
}

struct ForEachTest_Previews: PreviewProvider {
    static var previews: some View {
        ForEachTest()
    }
}

구현 방법은 다음과 같습니다.

struct CartView: View {
    var cart:[String: Int]

    var body: some View {
        List {
            ForEach(cart.keys.sorted()) { key in
                Text(key)
                Text("\(cart[key]!)")
            }
        }
    }
}

첫 번째 텍스트 보기는 문자열인 키를 출력합니다.두 번째 텍스트 보기는 해당 키의 Int 값을 출력합니다.다음에 나오는 !는 이 Int가 포함된 Optional(옵션)의 포장을 해제하는 것입니다.프로덕션에서는 이 옵션을 보다 안전하게 포장을 풀 수 있도록 검사를 수행하지만 이는 개념 증명입니다.

아니요, 사용할 수 없습니다.ForEach View와 함께Dictionary시도할 수 있지만 특히 사용하는 경우 충돌할 가능성이 높습니다.id: .\self열거되거나 해킹을 나타냅니다.설명서에 나와 있는 것처럼 다음을 사용합니다.ForEach View정확하게는 식별 가능한 사용자 지정 구조를 만들고 다음과 같은 구조를 포함하는 배열을 사용하여 생성할 수 있는 "식별된 데이터 모음"이 필요합니다.

private struct NamedFont: Identifiable {
    let name: String
    let font: Font
    var id: String { name } // or let id = UUID()
}

private let namedFonts: [NamedFont] = [
    NamedFont(name: "Large Title", font: .largeTitle),
    NamedFont(name: "Title", font: .title),
    NamedFont(name: "Headline", font: .headline),
    NamedFont(name: "Body", font: .body),
    NamedFont(name: "Caption", font: .caption)
]

var body: some View {
    ForEach(namedFonts) { namedFont in
        Text(namedFont.name)
            .font(namedFont.font)
    }
}

언급URL : https://stackoverflow.com/questions/56675532/swiftui-iterating-through-dictionary-with-foreach

반응형