ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [SwiftUI] ViewModel 에 Command Pattern 사용해보기
    Swift/SwiftUI 2023. 4. 25. 22:29
    struct ContentView: View {
        
        @StateObject var viewModel = ContentViewModel()
        
        var body: some View {
            NavigationView {
                ScrollView {
                    LazyVStack {
                        ForEach(Array(viewModel.items.enumerated()), id: \.offset) { offset, item in
                            // MARK: - Cell
                            NavigationLink {
                                DetailItemView(item: item, offset: offset, delegate: viewModel)
                            } label: {
                                Cell(item: item, offset: 0)
                            }
                        }
                    }
                }
            }
        }
        
        @ViewBuilder
        func Cell(item: Item, offset: Int) -> some View {
            HStack(spacing: 16) {
                Circle()
                    .frame(width: 50, height: 50)
                    .foregroundColor(item.color)
                Spacer()
                Text("\(String(item.id.prefix(8)))")
                    .font(.body)
                Image(systemName: item.isLiked ? "heart.fill" : "heart")
                    .resizable()
                    .frame(width: 20, height: 20)
                    .foregroundColor(item.isLiked ? .red : .black)
            }
            .padding(.horizontal, 16)
            .frame(maxWidth: .infinity)
            .frame(height: 80)
        }
    }
    class ContentViewModel: ObservableObject, ItemDelegate {
        
        @Published var items: [Item] = []
        
        init() { getItems() }
    
        func getItems() {
            for _ in 1...30 {
                DispatchQueue.main.async { [weak self] in
                    self?.items.append(Item.init())
                }
            }
        }
        
        func didTapIsLike(offset: Int) {
            DispatchQueue.main.async { [weak self] in
                self?.items[offset].isLiked.toggle()
            }
        }
    }
    struct DetailItemView: View {
        
        @State var item: Item
        let offset: Int
        
        weak var delegate: ItemDelegate?
        
        init(item: Item, offset: Int, delegate: ItemDelegate? = nil) {
            self._item = State(wrappedValue: item)
            self.offset = offset
            self.delegate = delegate
        }
        
        var body: some View {
            VStack {
                Image(systemName: item.isLiked ? "heart.fill" : "heart")
                    .resizable()
                    .frame(width: 50, height: 50)
                    .foregroundColor(item.isLiked ? .red : .black)
                    .onTapGesture { didTapItem() }
            }
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .background(item.color.opacity(0.3))
        }
        
        func didTapItem() {
            item.isLiked.toggle()
            delegate?.didTapIsLike(offset: offset)
        }
    }
    protocol ItemDelegate: AnyObject {
        func didTapIsLike(offset: Int)
    }
    
    struct Item: Identifiable, Hashable {
        let id = UUID().uuidString
        let color = Color.random
        var isLiked = Bool.random()
    }
    
    extension Color {
        static var random: Color {
            return Color(
                red: .random(in: 0...1),
                green: .random(in: 0...1),
                blue: .random(in: 0...1)
            )
        }
    }
Designed by Tistory.