animationswiftui

How to animate a menu from circle to oval like this?


Consider this code:

import SwiftUI

struct ContentView: View {
  @State private var hidden = true
    var body: some View {
        HStack {
          Group{
            if !hidden {
              Image(systemName: "globe")
                .imageScale(.large)
                .foregroundStyle(Color.black)
                .font(.system(size: 50))
                .opacity(hidden ? 0 : 1)
                .animation(.easeInOut(duration: 0.5).delay(1), value: hidden)
            }
            Image(systemName: "globe")
              .imageScale(.large)
              .foregroundStyle(Color.white)
              .font(.system(size: 50))
            
            if !hidden {
              Image(systemName: "globe")
                .imageScale(.large)
                .foregroundStyle(Color.yellow)
                .font(.system(size: 50))
                .opacity(hidden ? 0 : 1)
                .animation(.easeInOut(duration: 0.5).delay(3), value: hidden)    
            }
          }
          .padding(.horizontal, 10)
          .padding(.vertical, 5)              
        }
        .onTapGesture {
          withAnimation{
            hidden.toggle()
          }
        }
        .background(
          RoundedRectangle(cornerSize: CGSize(width: 50,height: 50))
            .fill(.red)
        )
        .padding()
    }
}

#Preview {
    ContentView()
}

This code shows a white globe over a white circle like this, initially.

enter image description here

When you tap the circle the menu expands to be an ellipse with two other globes on it.

enter image description here

The black and the yellow globes appear fade in at the same time the ellipse is expanding but this is not what I want.

I want all globes to be piled at the same central position. At this point, only the central globe is visible and the other two are invisible. As the red circle is tapped and begins to expand, I want globe 1 to move to the left and globe 3 to move to the right, as they were chasing the new border which is expanding. At the same time they are moving they are changing transparency to 1.

If the now ellipse is tapped, I want the reverse to happen.

how do I do that?


Solution

  • Instead of animations, transitions are way better at this. Because they are made for animating hiding and showing of views.

    Result

    struct ContentView: View {
      @State private var hidden = true
        
        var body: some View {
            HStack {
                Group {
                    if !hidden {
                        Image(systemName: "globe")
                            .imageScale(.large)
                            .foregroundStyle(Color.black)
                            .font(.system(size: 50))
                            .zIndex(0)
                            .transition(.offset(x: 100, y: 0).combined(with: .opacity))
                    }
                    Image(systemName: "globe")
                        .imageScale(.large)
                        .foregroundStyle(Color.white)
                        .font(.system(size: 50))
                        .zIndex(1)
                    
                    if !hidden {
                        Image(systemName: "globe")
                            .imageScale(.large)
                            .foregroundStyle(Color.yellow)
                            .font(.system(size: 50))
                            .font(.system(size: 50))
                            .zIndex(0)
                            .transition(.offset(x: -100, y: 0).combined(with: .opacity))
                    }
                }
                .padding(.horizontal, 10)
                .padding(.vertical, 5)
            }
            .onTapGesture {
                withAnimation{
                    hidden.toggle()
                }
            }
            .background(
                RoundedRectangle(cornerSize: CGSize(width: 50,height: 50))
                    .fill(.red)
            )
            .padding()
        }
    }