Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom modal transitions in SwiftUI

Tags:

ios

swift

swiftui

I'm trying to recreate the iOS 11/12 App Store with SwiftUI. Let's imagine the "story" is the view displayed when tapping on the card.

I've done the cards, but the problem I'm having now is how to do the animation done to display the "story".

As I'm not good at explaining, here you have a gif:

Gif 1 Gif 2

I've thought of making the whole card a PresentationLink, but the "story" is displayed as a modal, so it doesn't cover the whole screen and doesn't do the animation I want.

The most similar thing would be NavigationLink, but that then obliges me to add a NavigationView, and the card is displayed like another page.

I actually do not care whether its a PresentationLink or NavigationLink or whatever as long as it does the animation and displays the "story".

Thanks in advance.

My code:

Card.swift

struct Card: View {
    var icon: UIImage = UIImage(named: "flappy")!
    var cardTitle: String = "Welcome to \nCards!"
    var cardSubtitle: String = ""
    var itemTitle: String = "Flappy Bird"
    var itemSubtitle: String = "Flap That!"
    var cardCategory: String = ""
    var textColor: UIColor = UIColor.white
    var background: String = ""
    var titleColor: Color = .black
    var backgroundColor: Color = .white

    var body: some View {
        VStack {
            if background != "" {
                Image(background)
                    .resizable()
                    .frame(width: 380, height: 400)
                    .cornerRadius(20)

            } else {
                RoundedRectangle(cornerRadius: 20)
                    .frame(width: 400, height: 400)
                    .foregroundColor(backgroundColor)
            }

            VStack {
                HStack {
                    VStack(alignment: .leading) {
                        if cardCategory != "" {
                            Text(verbatim: cardCategory.uppercased())
                                .font(.headline)
                                .fontWeight(.heavy)
                                .opacity(0.3)
                                .foregroundColor(titleColor)
                            //.opacity(1)
                        }

                        HStack {
                            Text(verbatim: cardTitle)
                                .font(.largeTitle)
                                .fontWeight(.heavy)
                                .lineLimit(3)
                                .foregroundColor(titleColor)
                        }

                    }

                    Spacer()
                }.offset(y: -390)
                    .padding(.bottom, -390)

                HStack {
                    if cardSubtitle != "" {
                        Text(verbatim: cardSubtitle)
                            .font(.system(size: 17))
                            .foregroundColor(titleColor)
                    }
                    Spacer()
                }
                .offset(y: -50)
                    .padding(.bottom, -50)
            }
            .padding(.leading)


        }.padding(.leading).padding(.trailing)
    }

}

So

Card(cardSubtitle: "Welcome to this library I made :p", cardCategory: "CONNECT", background: "flBackground", titleColor: .white)

displays: Card 1

like image 667
iAlex11 Avatar asked Jul 12 '19 10:07

iAlex11


1 Answers

SwiftUI in iOS/tvOS 14 and macOS 11 has matchedGeometryEffect(id:in:properties:anchor:isSource:) to animate view transitions between different hierarchies.

Link to Official Documentation

Here's a minimal example:

struct SomeView: View {
    @State var isPresented = false
    @Namespace var namespace
 
    var body: some View {
        VStack {
            Button(action: {
                withAnimation {
                    self.isPresented.toggle()
                }
            }) {
                Text("Toggle")
            }

            SomeSourceContainer {
                MatchedView()
                .matchedGeometryEffect(id: "UniqueViewID", in: namespace, properties: .frame, isSource: !isPresented)
            }

            if isPresented {
                SomeTargetContainer {
                    MatchedTargetView()
                    .matchedGeometryEffect(id: "UniqueViewID", in: namespace, properties: .frame, isSource: isPresented)
                }
            }
        }
    }
}
like image 98
Palle Avatar answered Oct 23 '22 13:10

Palle