[英]Changing color of UI with onTapGesture
I've been trying to find a way to change the color of every flashcard in my app.我一直在尝试找到一种方法来更改我的应用程序中每张抽认卡的颜色。 I am struggling with understanding how to use @Binding, which I believe is the best solution.我正在努力理解如何使用@Binding,我认为这是最好的解决方案。 I ended up creating static variables in my Card struct, which allows me to change the card colors via onTapGesture actions.我最终在我的 Card 结构中创建了 static 变量,这允许我通过 onTapGesture 操作更改卡 colors。 It seems very slow and I know there has to be a better way to accomplish this.这似乎很慢,我知道必须有更好的方法来实现这一点。 Below is my code.下面是我的代码。 The action for changing the color is inside the Circle() in ContentView.更改颜色的操作在 ContentView 中的 Circle() 内部。 Thank you in advance.先感谢您。 Also could someone please tell me how to resize screenshots to post in here?也有人可以告诉我如何调整屏幕截图的大小以在此处发布吗? The solutions online make my screenshots super blurry so I hate uploading them在线解决方案使我的屏幕截图超级模糊,所以我讨厌上传它们
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)
}
}
ContentView内容视图
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()
}
}
Card卡片
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
}
binding is a possible way to change colors, but I prefer the use of ObservableObject
to manage the set of Cards and their color selection.绑定是更改 colors 的一种可能方式,但我更喜欢使用ObservableObject
来管理卡片集及其颜色选择。 The code in my answer shows how to use a ObservableObject
CardManager
to change all cards color resulting from your selection in Circle
.我的答案中的代码显示了如何使用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.