使用onTapGesture更改UI的颜色



我一直在试图找到一种方法来更改应用程序中每个抽认卡的颜色。我很难理解如何使用@Binding,我认为这是最好的解决方案。我最终在我的卡结构中创建了静态变量,这允许我通过onTapGesture操作更改卡的颜色。这看起来很慢,我知道必须有更好的方法来实现这一点。下面是我的代码更改颜色的操作在ContentView中的Circle((中提前谢谢还有人能告诉我如何调整屏幕截图的大小以发布在这里吗?在线解决方案使我的屏幕截图超级模糊,所以我讨厌上传它们

CardView

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
}

绑定是更改颜色的一种可能方式,但我更喜欢使用ObservableObject以管理卡片集及其颜色选择。我答案中的代码显示了如何使用ObservableObjectCardManager更改所有卡片的颜色由您在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()
}
}
}

最新更新