同时匹配几何效果和旋转3D效果
我想要制作一张卡片的动画,它从屏幕的上半部分飞到下半部分,并在飞行过程中翻转。
我使用自定义 .cardify
修饰符控制翻转逻辑。它似乎单独工作,例如,当我通过 onTapGesture { withAnimation { ... } }
翻转卡片时。
我还使用 matchedGeometryEffect
制作了一张卡片从屏幕顶部飞到底部,反之亦然。
但是,当我点击卡片时,它会在不旋转的情况下飞行。
我尝试对两个 if 分支应用 .transition(.assymetric(...))
(参见下面的代码)但这没有帮助。
那么,代码
import SwiftUI
struct ContentView: View {
@State var cardOnTop = true
@State var theCard = Card(isFaceUp: true)
@Namespace private var animationNameSpace
var body: some View {
VStack {
if cardOnTop {
CardView(card: theCard)
.matchedGeometryEffect(id: 1, in: animationNameSpace)
.onTapGesture {
withAnimation {
theCard.toggle()
cardOnTop.toggle() // comment me to test flipping alone
}
}
Color.white
} else {
Color.white
CardView(card: theCard)
.matchedGeometryEffect(id: 1, in: animationNameSpace)
.onTapGesture {
withAnimation {
theCard.toggle()
cardOnTop.toggle()
}
}
}
}
.padding()
}
struct Card {
var isFaceUp: Bool
mutating func toggle() {
isFaceUp.toggle()
}
}
struct CardView: View {
var card: Card
var body: some View {
Rectangle()
.frame(width: 100, height: 50)
.foregroundColor(.red)
.cardify(isFaceUp: card.isFaceUp)
}
}
}
/* Cardify ViewModifier */
struct Cardify: ViewModifier, Animatable {
init(isFaceUp: Bool){
rotation = isFaceUp ? 0 : 180
}
var rotation: Double // in degrees
var animatableData: Double {
get { return rotation }
set { rotation = newValue }
}
func body(content: Content) -> some View {
ZStack {
let shape = RoundedRectangle(cornerRadius: DrawingConstants.cornerRadius)
if rotation < 90 {
shape.fill().foregroundColor(.white)
shape.strokeBorder(lineWidth: DrawingConstants.lineWidth).foregroundColor(.gray)
} else {
shape.fill().foregroundColor(.gray)
}
content
.opacity(rotation < 90 ? 1 : 0)
}
.rotation3DEffect(Angle.degrees(rotation), axis: (0, 1, 0))
}
private struct DrawingConstants {
static let cornerRadius: CGFloat = 15
static let lineWidth: CGFloat = 2
}
}
extension View {
func cardify(isFaceUp: Bool) -> some View {
return self.modifier(Cardify(isFaceUp: isFaceUp))
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
如何使卡片旋转飞行?
另外,我发现这是可行的(如果单独放入VStack
主体中,则没有if分支)
CardView(card: theCard)
.offset(x: 0, y: cardOnTop ? 0 : 100)
.onTapGesture {
withAnimation {
theCard.toggle()
cardOnTop.toggle() // comment me to test flipping alone
}
}
但在我的应用程序中,我有一副向下翻转的卡片,这些卡片被分配给向上翻转的棋盘。我尝试通过上面 ContentView
代码中的 if-branch 来模拟行为(CardView
出现和消失)。
I want to animate a card, which flies from top half of screen to the bottom half and flips during the fly.
I control the flipping logic using custom .cardify
modifier. It seems working alone, e.g. when I flip a card by onTapGesture { withAnimation { ... } }
.
I also made a card fly from top of screen to the bottom and vice versa using matchedGeometryEffect
.
But, when I tap card it flies without rotation.
I tried to apply .transition(.assymetric(...))
for both if-branches (see code below) but it did not help.
So, the code
import SwiftUI
struct ContentView: View {
@State var cardOnTop = true
@State var theCard = Card(isFaceUp: true)
@Namespace private var animationNameSpace
var body: some View {
VStack {
if cardOnTop {
CardView(card: theCard)
.matchedGeometryEffect(id: 1, in: animationNameSpace)
.onTapGesture {
withAnimation {
theCard.toggle()
cardOnTop.toggle() // comment me to test flipping alone
}
}
Color.white
} else {
Color.white
CardView(card: theCard)
.matchedGeometryEffect(id: 1, in: animationNameSpace)
.onTapGesture {
withAnimation {
theCard.toggle()
cardOnTop.toggle()
}
}
}
}
.padding()
}
struct Card {
var isFaceUp: Bool
mutating func toggle() {
isFaceUp.toggle()
}
}
struct CardView: View {
var card: Card
var body: some View {
Rectangle()
.frame(width: 100, height: 50)
.foregroundColor(.red)
.cardify(isFaceUp: card.isFaceUp)
}
}
}
/* Cardify ViewModifier */
struct Cardify: ViewModifier, Animatable {
init(isFaceUp: Bool){
rotation = isFaceUp ? 0 : 180
}
var rotation: Double // in degrees
var animatableData: Double {
get { return rotation }
set { rotation = newValue }
}
func body(content: Content) -> some View {
ZStack {
let shape = RoundedRectangle(cornerRadius: DrawingConstants.cornerRadius)
if rotation < 90 {
shape.fill().foregroundColor(.white)
shape.strokeBorder(lineWidth: DrawingConstants.lineWidth).foregroundColor(.gray)
} else {
shape.fill().foregroundColor(.gray)
}
content
.opacity(rotation < 90 ? 1 : 0)
}
.rotation3DEffect(Angle.degrees(rotation), axis: (0, 1, 0))
}
private struct DrawingConstants {
static let cornerRadius: CGFloat = 15
static let lineWidth: CGFloat = 2
}
}
extension View {
func cardify(isFaceUp: Bool) -> some View {
return self.modifier(Cardify(isFaceUp: isFaceUp))
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
How can I make the card flying with rotation?
Also, I found that this works (if put in the body of VStack
alone, no if-branches)
CardView(card: theCard)
.offset(x: 0, y: cardOnTop ? 0 : 100)
.onTapGesture {
withAnimation {
theCard.toggle()
cardOnTop.toggle() // comment me to test flipping alone
}
}
but in my app I have a deck of flipped down cards, which are dealt to board flipping up. I tried to model the behavior by if-branch in the code of ContentView
above (CardView
s appear and dissapear).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
这是一个纯粹使用
.transition
使用自定义过渡的解决方案:Here is a solution purely with
.transition
using a custom transition:由于您移除了该卡并将另一张卡插入到视图中,因此不会触发旋转,因此仅保留
.matchedGeometryEffect
。您想要的是一张卡片留在视野中,以便它可以旋转,但也可以改变其位置。
您可以使用
.offset
来实现这一点。 GeometryReader 只是为了找到正确的偏移值。The rotation isn't triggered because you remove the card and insert another one into the view, so only the
.matchedGeometryEffect
remains.What you want is one card that stays in view so it can rotate, but also change its position.
You can achieve this e.g. with
.offset
. TheGeometryReader
is just to find the right offset value.