在 Viewbuilder 中使用变量 (Swift)
我是 swift 和 Viewbuilder 的新手,我正在尝试正确使用 Viewbuilder 中的变量。此代码的目的是使用动画揭示问题的答案。问题出现在一张卡片上,当点击卡片上的按钮时,就会显示答案。如果我使用一张卡,一切都很好,但是当我将多张卡放入滚动视图并点击按钮时,所有答案都会同时呈现。我相信这是因为我对所有卡片使用一个显示变量(因此它会同时展开所有卡片)。我需要为每张卡都有一个显示变量,但我正在努力实现这一点。我该怎么办呢?
我的视图的代码是:
struct Home: View {
// MARK: Animation Properties
@State var expandTheCard: Bool = false
@State var bottomLiquidView: AnimationView = AnimationView(name: "LiquidWave", bundle: .main)
@State var topLiquidView: AnimationView = AnimationView(name: "LiquidWave", bundle: .main)
// Avoiding Multitapping
@State var isfinished: Bool = false
var body: some View {
NavigationView {
ScrollView {
LazyVStack(spacing: 200) {
// MARK: Animated Liquid Transition Cards
LiquidCard(title: "What Is Hello In French?", subTitle: "", detail: "Hello In French Is...", description: "Bonjour!"){
if isfinished{return}
isfinished = true
// Animating Lottie View with little Delay
DispatchQueue.main.asyncAfter(deadline: .now() + (expandTheCard ? 0 : 0.2)) {
// So that it will finish soon...
bottomLiquidView.play(fromProgress: expandTheCard ? 0 : 0.45, toProgress: expandTheCard ? 0.6 : 0)
topLiquidView.play(fromProgress: expandTheCard ? 0 : 0.45, toProgress: expandTheCard ? 0.6 : 0){status in
isfinished = false
}
}
// Toggle Card
withAnimation(.interactiveSpring(response: 0.7, dampingFraction: 0.8, blendDuration: 0.8)){
expandTheCard.toggle()
}
}
.frame(maxHeight: .infinity)
}
}
}
}
ViewBuilder 的代码是:
@ViewBuilder
func LiquidCard(title: String,subTitle: String,detail: String,description: String,color: SwiftUI.Color = Color("Blue"),onExpand: @escaping ()->())->some View{
ZStack{
VStack(spacing: 20){
Text(title)
.font(.largeTitle.bold())
.foregroundColor(.white)
HStack(spacing: 10){
Text(subTitle)
.fontWeight(.semibold)
.foregroundColor(.white)
}
}
.padding()
.frame(maxWidth: .infinity)
.frame(height: expandTheCard ? 250 : 350)
.background{
GeometryReader{proxy in
let size = proxy.size
let scale = size.width / 1000
RoundedRectangle(cornerRadius: 35, style: .continuous)
.fill(color)
// To get Custom Color simply use Mask Technique
RoundedRectangle(cornerRadius: 35, style: .continuous)
.fill(color)
.mask {
ResizableLottieView(lottieView: $bottomLiquidView)
// Scaling it to current Size
.scaleEffect(x: scale, y: scale, anchor: .leading)
}
.rotationEffect(.init(degrees: 180))
.offset(y: expandTheCard ? size.height / 1.43 : 0)
}
}
// MARK: Expand Button
.overlay(alignment: .bottom) {
Button {
onExpand()
} label: {
Image(systemName: "chevron.down")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 30.0, height: 30.0)
.font(.title3.bold())
.foregroundColor(color)
.padding(30)
.background(.white,in: RoundedRectangle(cornerRadius: 20, style: .continuous))
// Shadows
.shadow(color: .black.opacity(0.15), radius: 5, x: 5, y: 5)
.shadow(color: .black.opacity(0.15), radius: 5, x: -5, y: -5)
}
.padding(.bottom,-25)
}
.zIndex(1)
// MARK: Expanded Card
VStack(spacing: 20){
Text(detail)
.font(.largeTitle.bold())
Text(description)
.font(.title3)
.lineLimit(3)
.padding(.horizontal)
.multilineTextAlignment(.center)
}
.foregroundColor(.white)
.padding(.vertical,40)
.padding(.horizontal)
.frame(maxWidth: .infinity)
.background{
GeometryReader{proxy in
let size = proxy.size
let scale = size.width / 1000
RoundedRectangle(cornerRadius: 35, style: .continuous)
.fill(color)
// To get Custom Color simply use Mask Technique
RoundedRectangle(cornerRadius: 35, style: .continuous)
.fill(color)
.mask {
ResizableLottieView(lottieView: $topLiquidView)
// Scaling it to current Size
.scaleEffect(x: scale, y: scale, anchor: .leading)
}
.offset(y: expandTheCard ? -size.height / 1.2 : -size.height / 1.4)
}
}
.zIndex(0)
.offset(y: expandTheCard ? 280 : 0)
}
.offset(y: expandTheCard ? -120 : 0)
}
I'm new to swift and Viewbuilder and I am trying to use variables in the Viewbuilder correctly. The objective of this code is to reveal the answer to a question using an animation. The question is presented on a card and when the button on the card is tapped the answer is revealed. It all works well if I use one card however when I put multiple cards in a scrollView and tap the button, all of the answers are presented at the same time. I believe this is because I am using one show variable for all the cards (therefore it expands all the cards when at once). I need to have a show variable for each card but am struggling to implement this. How would I go about this?
The code for my View is:
struct Home: View {
// MARK: Animation Properties
@State var expandTheCard: Bool = false
@State var bottomLiquidView: AnimationView = AnimationView(name: "LiquidWave", bundle: .main)
@State var topLiquidView: AnimationView = AnimationView(name: "LiquidWave", bundle: .main)
// Avoiding Multitapping
@State var isfinished: Bool = false
var body: some View {
NavigationView {
ScrollView {
LazyVStack(spacing: 200) {
// MARK: Animated Liquid Transition Cards
LiquidCard(title: "What Is Hello In French?", subTitle: "", detail: "Hello In French Is...", description: "Bonjour!"){
if isfinished{return}
isfinished = true
// Animating Lottie View with little Delay
DispatchQueue.main.asyncAfter(deadline: .now() + (expandTheCard ? 0 : 0.2)) {
// So that it will finish soon...
bottomLiquidView.play(fromProgress: expandTheCard ? 0 : 0.45, toProgress: expandTheCard ? 0.6 : 0)
topLiquidView.play(fromProgress: expandTheCard ? 0 : 0.45, toProgress: expandTheCard ? 0.6 : 0){status in
isfinished = false
}
}
// Toggle Card
withAnimation(.interactiveSpring(response: 0.7, dampingFraction: 0.8, blendDuration: 0.8)){
expandTheCard.toggle()
}
}
.frame(maxHeight: .infinity)
}
}
}
}
The code for the ViewBuilder is:
@ViewBuilder
func LiquidCard(title: String,subTitle: String,detail: String,description: String,color: SwiftUI.Color = Color("Blue"),onExpand: @escaping ()->())->some View{
ZStack{
VStack(spacing: 20){
Text(title)
.font(.largeTitle.bold())
.foregroundColor(.white)
HStack(spacing: 10){
Text(subTitle)
.fontWeight(.semibold)
.foregroundColor(.white)
}
}
.padding()
.frame(maxWidth: .infinity)
.frame(height: expandTheCard ? 250 : 350)
.background{
GeometryReader{proxy in
let size = proxy.size
let scale = size.width / 1000
RoundedRectangle(cornerRadius: 35, style: .continuous)
.fill(color)
// To get Custom Color simply use Mask Technique
RoundedRectangle(cornerRadius: 35, style: .continuous)
.fill(color)
.mask {
ResizableLottieView(lottieView: $bottomLiquidView)
// Scaling it to current Size
.scaleEffect(x: scale, y: scale, anchor: .leading)
}
.rotationEffect(.init(degrees: 180))
.offset(y: expandTheCard ? size.height / 1.43 : 0)
}
}
// MARK: Expand Button
.overlay(alignment: .bottom) {
Button {
onExpand()
} label: {
Image(systemName: "chevron.down")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 30.0, height: 30.0)
.font(.title3.bold())
.foregroundColor(color)
.padding(30)
.background(.white,in: RoundedRectangle(cornerRadius: 20, style: .continuous))
// Shadows
.shadow(color: .black.opacity(0.15), radius: 5, x: 5, y: 5)
.shadow(color: .black.opacity(0.15), radius: 5, x: -5, y: -5)
}
.padding(.bottom,-25)
}
.zIndex(1)
// MARK: Expanded Card
VStack(spacing: 20){
Text(detail)
.font(.largeTitle.bold())
Text(description)
.font(.title3)
.lineLimit(3)
.padding(.horizontal)
.multilineTextAlignment(.center)
}
.foregroundColor(.white)
.padding(.vertical,40)
.padding(.horizontal)
.frame(maxWidth: .infinity)
.background{
GeometryReader{proxy in
let size = proxy.size
let scale = size.width / 1000
RoundedRectangle(cornerRadius: 35, style: .continuous)
.fill(color)
// To get Custom Color simply use Mask Technique
RoundedRectangle(cornerRadius: 35, style: .continuous)
.fill(color)
.mask {
ResizableLottieView(lottieView: $topLiquidView)
// Scaling it to current Size
.scaleEffect(x: scale, y: scale, anchor: .leading)
}
.offset(y: expandTheCard ? -size.height / 1.2 : -size.height / 1.4)
}
}
.zIndex(0)
.offset(y: expandTheCard ? 280 : 0)
}
.offset(y: expandTheCard ? -120 : 0)
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您对问题的评估是正确的:您只有一个变量来控制
LiquidCard
的所有实例。您应该有一个仅特定于LiquidCard
的expandTheCard
变量,而不连接到Home
视图。要实现此目的,一个好方法是将
LiquidCard
定义为新视图,而不是@ViewBuilder func
。顺便说一句,该名称已经是初始大小写,不应该用于函数名称。以下是您应该如何更改视图(我只会在这里提到更改的部分):
Home
视图中删除expandTheCard
:func
具有View
类型的struct
,其中包含@State var ExpandTheCard
:Home
不再查看,删除切换:You are right in the assessment of the problem: you have only one variable that is governing all instances of
LiquidCard
. You should have anexpandTheCard
variable that is specific only toLiquidCard
, not connected to theHome
view.To achieve this, a good way is to define
LiquidCard
as a new view, not as a@ViewBuilder func
. By the way, the name is already in initial cap, which shouldn't be used for function names.Here's how you should change your views (I will only mention here the parts that change):
expandTheCard
from theHome
view:func
with astruct
of typeView
that contains the@State var expandTheCard
:Home
view anymore, delete the toggle: