SwiftUI 上按钮的动画修改器

可动画

最新版本

联系人:@lexkraev 电报组

SwiftUI 上按钮的另一个动画修改器。

🔍 您可以在此处找到完整的演示视频。

👨🏻 💻 随意订阅频道 SwiftUI 开发 在电报中。

要求

  • iOS 13.0 或 macOS 10.15

安装

Swift Package Manager

要使用 SwiftPM 集成到您的项目中,请将以下内容添加到您的 :AnimatablePackage.swift

dependencies: [
    .package(url: "https://github.com/c-villain/Animatable", from: "0.1.0"),
],

or via XcodeGen insert into your :project.yml

name: YourProjectName
options:
  deploymentTarget:
    iOS: 13.0
packages:
  Animatable:
    url: https://github.com/c-villain/Animatable
    from: 0.1.0
targets:
  YourTarget:
    type: application
    ...
    dependencies:
       - package: Animatable

Quick start

All examples you can find in demo project inside package.

There are different custom animation types provided by .Animatable

👇🏻 Tap on its name to see description and example of using.

Live comments effect

live comments

Use where is number of prints in animation activity, is flag to start animation..animate(.liveComments(stamps:),animate:)stampsanimate

@State var animate: Bool = false
...
Button {
    animate.toggle()
} label: {
    HStack(spacing: 8)  {
        Image(systemName: animate ? "heart.fill" : "heart")
            .resizable()
            .scaledToFit()
            .animate(.liveComments(stamps: 4),
                     animate: animate)
            .frame(width: 24, height: 24)
            .foregroundColor(.white)

        Text("Like")
            .font(.body)
            .fontWeight(.medium)
            .foregroundColor(.white)
    }
    .padding(12)
    .background(
        Rectangle()
            .fill(.pink.opacity(0.8))
            .cornerRadius(12)
    )
}
Explosion effect

explosion

Use where is color of explosion in animation activity, is flag to start animation..animate(.explosive(color:),animate:)coloranimate

@State var animate: Bool = false
...
Button {
  animate.toggle()
} label: {
  HStack(spacing: 8)  {
      Image(systemName: animate ? "power" : "poweroff")
          .resizable()
          .scaledToFit()
          .animate(.explosive(color: .white),
                   animate: animate)
          .frame(width: 24, height: 24)
          .foregroundColor(.white)

      Text(animate ? "On" : "Off")
          .font(.body)
          .fontWeight(.medium)
          .foregroundColor(.white)
  }
  .padding(12)
  .background(
      Rectangle()
          .fill(.gray.opacity(0.8))
          .cornerRadius(12)
  )
}
Tweak effect

tweak

Use where is tweak offset, is number of shakes in tweking, is flag to start animation..animate(.tweaking(amount:,shakesPerUnit:),animate:)amountshakesPerUnitanimate

@State var animate: Bool = false
...
Button {
  animate.toggle()
} label: {
  HStack(spacing: 8)  {
      Image(systemName: animate ? "hand.thumbsup.fill" : "hand.thumbsup")
          .resizable()
          .scaledToFit()
          .animate(.tweaking(),
                   animate: animate)
          .frame(width: 24, height: 24)
          .foregroundColor(.white)

      Text("Like")
          .font(.body)
          .fontWeight(.medium)
          .foregroundColor(.white)
  }
  .padding(12)
  .background(
      Rectangle()
          .fill(.blue.opacity(0.8))
          .cornerRadius(12)
  )
}
Scaling effect

scaling

Use where is scaling factor, is flag to start animation..animate(.scaling(scaling:),animate:)scalinganimate

@State var animate: Bool = false
...
Button {
  animate.toggle()
} label: {
  HStack(spacing: 8)  {
      Image(systemName: animate ? "plus.app.fill" : "plus.app")
          .resizable()
          .scaledToFit()
          .animate(.scaling(),
                   animate: animate)
          .frame(width: 24, height: 24)
          .foregroundColor(.white)

      Text("Add")
          .font(.body)
          .fontWeight(.medium)
          .foregroundColor(.white)
  }
  .padding(12)
  .background(
      Rectangle()
          .fill(.yellow.opacity(0.8))
          .cornerRadius(12)
  )
}
Rotating effect

scaling

Use where is flag to start animation..animate(.rotating,animate:)animate

@State var animate: Bool = false
...
Button {
  animate.toggle()
} label: {
  HStack(spacing: 8)  {
      Image(systemName: animate ? "arrow.triangle.2.circlepath.circle.fill" : "arrow.triangle.2.circlepath.circle")
          .resizable()
          .scaledToFit()
          .animate(.rotating,
                   animate: animate)
          .frame(width: 24, height: 24)
          .foregroundColor(.white)

      Text("Sync")
          .font(.body)
          .fontWeight(.medium)
          .foregroundColor(.white)
  }
  .padding(12)
  .background(
      Rectangle()
          .fill(.blue.opacity(0.8))
          .cornerRadius(12)
  )
}
Fireworks effect

fireworks

Use where is color of animation, is flag to start animation..animate(.fireworks(color:),animate:)coloranimate

@State var animate: Bool = false
...
Button {
  animate.toggle()
} label: {
  HStack(spacing: 8)  {
      Image(systemName: animate ? "sun.max.fill" : "sun.max")
          .resizable()
          .scaledToFit()
          .animate(.fireworks(color: .white),
                   animate: animate)
          .frame(width: 24, height: 24)
          .foregroundColor(.white)

      Text("Weather")
          .font(.body)
          .fontWeight(.medium)
          .foregroundColor(.white)
  }
  .padding(12)
  .background(
      Rectangle()
          .fill(.blue.opacity(0.8))
          .cornerRadius(12)
  )
}

👇🏻 You can easily join them together to combine animation.

Combining animation

combined

Use sequence of to get multiple animation effect..animate(type:,animate:)

@State var animate: Bool = false
...
Button {
  animate.toggle()
} label: {
  HStack(spacing: 8)  {
      Image(systemName: animate ? "sun.max.fill" : "sun.max")
          .resizable()
          .scaledToFit()
          .animate(.rotating,
                   animate: animate)
          .animate(.explosive(color: .red, factor: 2.0),
                   animate: animate)
          .animate(.explosive(color: .blue, factor: 1.4),
                   animate: animate)
          .animate(.fireworks(color: .yellow, factor: 3.5),
                   animate: animate)
          .frame(width: 24, height: 24)
          .foregroundColor(.red)

      Text("Combined")
          .font(.body)
          .fontWeight(.medium)
          .foregroundColor(.white)
  }
  .padding(12)
  .background(
      Rectangle()
          .fill(.blue.opacity(0.6))
          .cornerRadius(12)
  )
}

Recommendations for use

in combining animation

Order in sequence of is really important!.animate(type:,animate:)

Feel the difference in the next example:

@State var animate: Bool = false
...
 Button {
    animate.toggle()
} label: {
    HStack(spacing: 8)  {
        Image(systemName: multiple ? "sun.max.fill" : "sun.max")
            .resizable()
            .scaledToFit()
            .animate(.liveComments(stamps: 4),
                     animate: animate)
            .animate(.rotating,
                     animate: animate)
            .animate(.explosive(color: .red, factor: 2.0),
                     animate: animate)
            .animate(.explosive(color: .blue, factor: 1.4),
                     animate: animate)
            .animate(.fireworks(color: .yellow, factor: 3.0),
                     animate: animate)
            .frame(width: 24, height: 24)
            .foregroundColor(.red)

        Text("Weather")
            .font(.body)
            .fontWeight(.medium)
            .foregroundColor(.white)
    }
    .padding(12)
    .background(
        Rectangle()
            .fill(.blue.opacity(0.6))
            .cornerRadius(12)
    )
}

Using this sequence of leads to such behaviour:.animate(...)

erro1

To get expected behaviour this we should change the order in chain:

@State var animate: Bool = false
...
 Button {
    animate.toggle()
} label: {
    HStack(spacing: 8)  {
        Image(systemName: multiple ? "sun.max.fill" : "sun.max")
            .resizable()
            .scaledToFit()
            .animate(.rotating,                // <== Look here!
                     animate: animate)
            .animate(.liveComments(stamps: 4), // <== Look here!
                     animate: animate)
            .animate(.explosive(color: .red, factor: 2.0),
                     animate: animate)
            .animate(.explosive(color: .blue, factor: 1.4),
                     animate: animate)
            .animate(.fireworks(color: .yellow, factor: 3.0),
                     animate: animate)
            .frame(width: 24, height: 24)
            .foregroundColor(.red)

        Text("Weather")
            .font(.body)
            .fontWeight(.medium)
            .foregroundColor(.white)
    }
    .padding(12)
    .background(
        Rectangle()
            .fill(.blue.opacity(0.6))
            .cornerRadius(12)
    )
}

The result:

erro1

in group of views

Use can use not only for one view but for group of views.animate(...)

@State var animate: Bool = false
...
Button {
  animate.toggle()
} label: {
  HStack(spacing: 8)  {
      Image(systemName: animate ? "heart.fill" : "heart")
          .resizable()
          .scaledToFit()
          .frame(width: 24, height: 24)
          .foregroundColor(.red)

      Text("Like")
          .font(.body)
          .fontWeight(.medium)
          .foregroundColor(.red)
  }
  .animate(.liveComments(stamps: 4), // <== Look here!
           animate: animate)
  .padding(12)
  .background(
      Rectangle()
          .fill(.blue.opacity(0.8))
          .cornerRadius(12)
  )
}

The result:

example1

Communication

  • If you found a bug, open an issue or submit a fix via a pull request.
  • If you have a feature request, open an issue or submit a implementation via a pull request or hit me up on lexkraev@gmail.com or telegram.
  • If you want to contribute, submit a pull request onto the master branch.

License

Animatable package is released under an MIT license.

GitHub

点击跳转