繁体   English   中英

使用 onTapGesture 更改 UI 的颜色

[英]Changing color of UI with onTapGesture

我一直在尝试找到一种方法来更改我的应用程序中每张抽认卡的颜色。 我正在努力理解如何使用@Binding,我认为这是最好的解决方案。 我最终在我的 Card 结构中创建了 static 变量,这允许我通过 onTapGesture 操作更改卡 colors。 这似乎很慢,我知道必须有更好的方法来实现这一点。 下面是我的代码。 更改颜色的操作在 ContentView 中的 Circle() 内部。 先感谢您。 也有人可以告诉我如何调整屏幕截图的大小以在此处发布吗? 在线解决方案使我的屏幕截图超级模糊,所以我讨厌上传它们

卡片视图

import SwiftUI

struct CardView: View {
    let card: Card
    var removal: (() -> Void)? = nil
    
    @State private var isShowingAnswer = false
    @State private var changeColors = false
    @State private var offset = CGSize.zero
    
    var body: some View {
        ZStack {
            RoundedRectangle(cornerRadius: 25, style: .continuous)
                .fill(LinearGradient(colors: [Card.gradCs[Card.selec][0].opacity(1 - Double(abs(offset.width / 50))), Card.gradCs[Card.selec][1].opacity(1 - Double(abs(offset.width / 50)))], startPoint: .topLeading, endPoint: .bottomTrailing))
                .background(RoundedRectangle(cornerRadius: 25, style: .continuous).fill(offset.width > 0 ? .green : .red))
                .shadow(color: Color(#colorLiteral(red: 0, green: 0, blue: 0, alpha: 0.25)), radius:6, x:1, y:8)
            
            VStack(spacing: 20){
                Text(card.prompt)
                    .font(.largeTitle)
                    .fontWeight(.bold)
                    .foregroundColor(.white.opacity(0.8))
                
                if isShowingAnswer {
                    Text(card.answer)
                        .font(.title)
                        .fontWeight(.semibold)
                        .foregroundColor(Color(#colorLiteral(red: 0.8947916626930237, green: 0.8666666746139526, blue: 1, alpha: 1)))
                }
            }
            .padding()
            .multilineTextAlignment(.center)
        }
        .frame(width: 450, height: 250)
        .rotationEffect(.degrees(Double(offset.width / 5)))
        .offset(x: offset.width * 5, y: 0)
        .opacity(2 - Double(abs(offset.width / 50)))
        .gesture(
            DragGesture()
                .onChanged { gesture in
                    offset = gesture.translation
                }
                .onEnded { _ in
                    if abs(offset.width) > 100 {
                        removal?()
                    } else {
                        offset = .zero
                    }
                }
            )
        .onTapGesture {
            isShowingAnswer.toggle()
        }
    }
}

struct CardView_Previews: PreviewProvider {
    static var previews: some View {
        CardView(card: Card.example)
            .previewInterfaceOrientation(.portraitUpsideDown)
    }
}

内容视图

import SwiftUI

extension View {
    func stacked(at position: Int, in total: Int) -> some View {
        let offset = Double(total - position)
        return self.offset(x: 0, y: offset * 10)
    }
}

struct ContentView: View {
    @Environment(\.accessibilityDifferentiateWithoutColor) var differentiateWithoutColor
    @State private var cards = Array(repeating: Card.example, count: 10)
    
    @State private var timeRemaining = 100

    let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
    
    @Environment(\.scenePhase) var scenePhase
    @State private var isActive = true
    
    var body: some View {
        ZStack {
            VStack {
                HStack {
                    Text("\(timeRemaining / 60):\(timeRemaining % 60, specifier: "%02d")")
                        .font(.title)
                        .fontWeight(.semibold)
                        .foregroundColor(.white)
                        .padding(.horizontal, 20)
                        .padding(.vertical, 5)
                        .background(.black.opacity(0.75))
                        .clipShape(Capsule())
                    
                    Circle()
                        .fill(LinearGradient(colors: Card.gradCs[Card.selec], startPoint: .topLeading, endPoint: .bottomTrailing))
                        .onTapGesture {
                            withAnimation {
                                if Card.selec == 0 {
                                    Card.selec = 1
                                } else {
                                    Card.selec = 0
                                }
                            }
                        }
                        .frame(width: 40, height: 40)
                }
                ZStack {
                    ForEach(0 ..< cards.count, id: \.self) { index in
                        CardView(card: cards[index]) {
                            withAnimation {
                                removeCard(at: index)
                                print(self.offset())
                            }
                        }
                        .stacked(at: index, in: cards.count)
                    }
                }
                .allowsHitTesting(timeRemaining > 0)
                
                if cards.isEmpty {
                    Button("Start Again", action: resetCards)
                        .padding()
                        .background(.white)
                        .foregroundColor(.black)
                        .clipShape(Capsule())
                }
            }
            if differentiateWithoutColor {
                VStack {
                    Spacer()
                    
                    HStack {
                        Image(systemName: "xmark.circle")
                            .padding()
                            .background(.black.opacity(0.7))
                            .clipShape(Circle())
                        
                        Spacer()
                        
                        Image(systemName: "checkmark.circle")
                            .padding()
                            .background(.black.opacity(0.7))
                            .clipShape(Circle())
                    }
                    .foregroundColor(.white)
                    .font(.largeTitle)
                    .padding()
                }
            }
        }
        .onReceive(timer) { time in
            guard isActive else { return }
            // Use if let when the non-nil case is valid. Use guard when the nil case represents some sort of error.
            // use guard when there should only be one result.. use if when you need ELSE to do something
            if timeRemaining > 0 {
                timeRemaining -= 1
            }
        }
        .onChange(of: scenePhase) { newPhase in
            if newPhase == .active {
                if cards.isEmpty == false {
                    isActive = true
                }
            } else {
                isActive = false
            }
        }
    }
    
    func removeCard(at index: Int) {
        cards.remove(at: index)
        
        if cards.isEmpty {
            isActive = false
        }
    }
    
    func resetCards() {
        cards = Array(repeating: Card.example, count: 10)
        timeRemaining = 100
        isActive = true
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

卡片

import Foundation
import SwiftUI

struct Card {
    let prompt: String
    let answer: String
    
    static var example = Card(prompt: "What is the name of Chicago's NFL Team?", answer: "Da Bears")
    static var gradCs = [[Color(#colorLiteral(red: 0.6039215922355652, green: 0.49803921580314636, blue: 1, alpha: 1)), Color(#colorLiteral(red: 1, green: 0.49803924560546875, blue: 1, alpha: 1))], [Color(#colorLiteral(red: 0.6039215922355652, green: 1, blue: 1, alpha: 1)), Color(#colorLiteral(red: 0.6039215922355652, green: 0.49803921580314636, blue: 1, alpha: 1))]]
    static var selec = 0
}

绑定是更改 colors 的一种可能方式,但我更喜欢使用ObservableObject来管理卡片集及其颜色选择。 我的答案中的代码显示了如何使用ObservableObject CardManager更改因您在Circle中的选择而产生的所有卡片颜色。

// manages the cards and all cards color selection
class CardManager: ObservableObject {
    let gradCs = [[Color(#colorLiteral(red: 0.6039215922355652, green: 0.49803921580314636, blue: 1, alpha: 1)), Color(#colorLiteral(red: 1, green: 0.49803924560546875, blue: 1, alpha: 1))], [Color(#colorLiteral(red: 0.6039215922355652, green: 1, blue: 1, alpha: 1)), Color(#colorLiteral(red: 0.6039215922355652, green: 0.49803921580314636, blue: 1, alpha: 1))]]
    
    @Published var cards = [Card]()
    @Published var selec = 0
  
    func getColorSet() -> [Color] {
        return gradCs[selec]
    }
}

struct Card {
    let prompt: String
    let answer: String

    static var example = Card(prompt: "What is the name of Chicago's NFL Team?", answer: "Da Bears")
}

extension View {
    func stacked(at position: Int, in total: Int) -> some View {
        let offset = Double(total - position)
        return self.offset(x: 0, y: offset * 10)
    }
}

struct ContentView: View {
    @Environment(\.accessibilityDifferentiateWithoutColor) var differentiateWithoutColor
    @StateObject var manager = CardManager()  // <--- here
    
    @State private var timeRemaining = 100

    let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
    
    @Environment(\.scenePhase) var scenePhase
    @State private var isActive = true
    
    var body: some View {
        ZStack {
            VStack {
                HStack {
                    Text("\(timeRemaining / 60):\(timeRemaining % 60, specifier: "%02d")")
                        .font(.title)
                        .fontWeight(.semibold)
                        .foregroundColor(.white)
                        .padding(.horizontal, 20)
                        .padding(.vertical, 5)
                        .background(.black.opacity(0.75))
                        .clipShape(Capsule())
                    
                    Circle()
                        .fill(LinearGradient(colors: manager.getColorSet(), startPoint: .topLeading, endPoint: .bottomTrailing))
                        .onTapGesture {
                            withAnimation {
                                if manager.selec == 0 {
                                    manager.selec = 1
                                } else {
                                    manager.selec = 0
                                }
                            }
                        }
                        .frame(width: 40, height: 40)
                }
                ZStack {
                    ForEach(0 ..< manager.cards.count, id: \.self) { index in
                        CardView(card: manager.cards[index]) {
                            withAnimation {
                                removeCard(at: index)
                                print(self.offset())
                            }
                        }
                        .stacked(at: index, in: manager.cards.count)
                    }
                }
                .allowsHitTesting(timeRemaining > 0)
                
                if manager.cards.isEmpty {
                    Button("Start Again", action: resetCards)
                        .padding()
                        .background(.white)
                        .foregroundColor(.black)
                        .clipShape(Capsule())
                }
            }
            if differentiateWithoutColor {
                VStack {
                    Spacer()
                    
                    HStack {
                        Image(systemName: "xmark.circle")
                            .padding()
                            .background(.black.opacity(0.7))
                            .clipShape(Circle())
                        
                        Spacer()
                        
                        Image(systemName: "checkmark.circle")
                            .padding()
                            .background(.black.opacity(0.7))
                            .clipShape(Circle())
                    }
                    .foregroundColor(.white)
                    .font(.largeTitle)
                    .padding()
                }
            }
        }
        .onReceive(timer) { time in
            guard isActive else { return }
            // Use if let when the non-nil case is valid. Use guard when the nil case represents some sort of error.
            // use guard when there should only be one result.. use if when you need ELSE to do something
            if timeRemaining > 0 {
                timeRemaining -= 1
            }
        }
        .onChange(of: scenePhase) { newPhase in
            if newPhase == .active {
                if manager.cards.isEmpty == false {
                    isActive = true
                }
            } else {
                isActive = false
            }
        }
        .onAppear {
            manager.cards = Array(repeating: Card.example, count: 10)  // <--- here
        }
        .environmentObject(manager)  // <--- here
    }
    
    func removeCard(at index: Int) {
        manager.cards.remove(at: index)
        
        if manager.cards.isEmpty {
            isActive = false
        }
    }
    
    func resetCards() {
        manager.cards = Array(repeating: Card.example, count: 10)
        timeRemaining = 100
        isActive = true
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

struct CardView: View {
    @EnvironmentObject var manager: CardManager // <--- here
    let card: Card
    var removal: (() -> Void)? = nil
    
    @State private var isShowingAnswer = false
    @State private var changeColors = false
    @State private var offset = CGSize.zero
    
    var body: some View {
        ZStack {
            RoundedRectangle(cornerRadius: 25, style: .continuous)
                .fill(LinearGradient(colors: [manager.getColorSet()[0].opacity(1 - Double(abs(offset.width / 50))), manager.getColorSet()[1].opacity(1 - Double(abs(offset.width / 50)))], startPoint: .topLeading, endPoint: .bottomTrailing))
                .background(RoundedRectangle(cornerRadius: 25, style: .continuous).fill(offset.width > 0 ? .green : .red))
                
                .background(RoundedRectangle(cornerRadius: 25, style: .continuous).fill(offset.width > 0 ? .green : .red))
                .shadow(color: Color(#colorLiteral(red: 0, green: 0, blue: 0, alpha: 0.25)), radius:6, x:1, y:8)
            
            VStack(spacing: 20){
                Text(card.prompt)
                    .font(.largeTitle)
                    .fontWeight(.bold)
                    .foregroundColor(.white.opacity(0.8))
                
                if isShowingAnswer {
                    Text(card.answer)
                        .font(.title)
                        .fontWeight(.semibold)
                        .foregroundColor(Color(#colorLiteral(red: 0.8947916626930237, green: 0.8666666746139526, blue: 1, alpha: 1)))
                }
            }
            .padding()
            .multilineTextAlignment(.center)
        }
        .frame(width: 450, height: 250)
        .rotationEffect(.degrees(Double(offset.width / 5)))
        .offset(x: offset.width * 5, y: 0)
        .opacity(2 - Double(abs(offset.width / 50)))
        .gesture(
            DragGesture()
                .onChanged { gesture in
                    offset = gesture.translation
                }
                .onEnded { _ in
                    if abs(offset.width) > 100 {
                        removal?()
                    } else {
                        offset = .zero
                    }
                }
            )
        .onTapGesture {
            isShowingAnswer.toggle()
        }
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM