Created
May 19, 2022 11:47
-
-
Save YusukeHosonuma/198dd1a187ca4d5a49600c555b834ccd to your computer and use it in GitHub Desktop.
GeometryReader と AnchorPreference で、matchedGeometry っぽいアニメーション処理。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 以下の記事について matchedGeoemetry を使用せずに書いたみた的なやつ。 | |
// https://hoshi0523.hatenablog.com/entry/2020/10/24/214520 | |
// | |
// 🍊 GeometryReader version. | |
// 🍎 AnchorPreference version. | |
// | |
import SwiftUI | |
enum ButtonType: String, CaseIterable { | |
case share = "square.and.arrow.up" | |
case trash = "trash" | |
case folder = "folder" | |
case person = "person" | |
} | |
extension ButtonType: Identifiable { | |
var id: String { rawValue } | |
} | |
struct ContentView: View { | |
@State var selected: ButtonType = .share | |
var body: some View { | |
VStack { | |
HStack { | |
ForEach(ButtonType.allCases) { type in | |
CustomButton(selected: $selected, type: type) | |
} | |
} | |
// | |
// 🍊 GeometryReader version. | |
// | |
.coordinateSpace(name: "container") | |
.backgroundPreferenceValue(BoundsPreferenceKey.self) { bounds in | |
HStack { | |
circleBackground(bounds) | |
Spacer() | |
} | |
} | |
// | |
// 🍎 AnchorPreference version. | |
// | |
.backgroundPreferenceValue(AnchorPreferenceKey.self) { anchor in | |
if let anchor = anchor { | |
GeometryReader { geometry in | |
borderBackground(geometry[anchor]) | |
} | |
} | |
} | |
} | |
.animation(.easeInOut, value: selected) | |
} | |
// 🍊 | |
private func circleBackground(_ bounds: CGRect) -> some View { | |
Circle() | |
.fill(.orange) | |
.opacity(0.3) | |
.offset(x: bounds.minX, y: bounds.minY) | |
.frame(width: bounds.width, height: bounds.height) | |
} | |
// 🍎 | |
private func borderBackground(_ bounds: CGRect) -> some View { | |
Rectangle() | |
.stroke(style: .init(lineWidth: 1, dash: [5])) | |
.stroke(.red) | |
.offset(x: bounds.minX - 4, y: bounds.minY - 4) | |
.frame(width: bounds.width + 8, height: bounds.height + 8) | |
} | |
} | |
struct CustomButton: View { | |
@Binding var selected: ButtonType | |
let type: ButtonType | |
var body: some View { | |
Button { | |
selected = type | |
} label: { | |
Image(systemName: type.rawValue) | |
.resizable() | |
.renderingMode(.original) | |
.aspectRatio(contentMode: .fit) | |
.frame(width: 44, height: 44) | |
} | |
.frame(width: 80, height: 80) | |
// | |
// 🍊 GeometryReader version. | |
// | |
.when(type == selected) { content in | |
content | |
.background( | |
GeometryReader { geometry in | |
Color.clear | |
.preference(key: BoundsPreferenceKey.self, value: geometry.frame(in: .named("container"))) | |
} | |
) | |
} | |
// | |
// 🍎 AnchorPreference version. | |
// | |
.when(type == selected) { content in | |
content | |
.anchorPreference(key: AnchorPreferenceKey.self, value: .bounds) { $0 } | |
} | |
} | |
} | |
// | |
// 🍊 GeometryReader version. | |
// | |
struct BoundsPreferenceKey: PreferenceKey { | |
static var defaultValue: CGRect = .zero | |
static func reduce(value: inout CGRect, nextValue: () -> CGRect) { | |
value = nextValue() | |
} | |
} | |
// | |
// 🍎 AnchorPreference version. | |
// | |
struct AnchorPreferenceKey: PreferenceKey { | |
static var defaultValue: Anchor<CGRect>? = nil | |
static func reduce(value: inout Anchor<CGRect>?, nextValue: () -> Anchor<CGRect>?) { | |
value = nextValue() | |
} | |
} | |
extension View { | |
@ViewBuilder | |
func when<Content: View>(_ condition: Bool, transform: (Self) -> Content) -> some View { | |
if condition { | |
transform(self) | |
} else { | |
self | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Simulator.Screen.Recording.-.iPhone.SE.3rd.generation.-.2022-05-19.at.20.47.53.mp4