Open7
【SwiftUI】Viewが持てる責務の粒度について

久しぶりにSwiftを書くのでリハビリも兼ねて

ユーザーが移動可能なView(Stampという名前とする)を実装する際に全ての機能を盛り込んでいいのかどうか

例えば、ドラッグをする責務を持ったViewを定義してStampでそれを利用する方法が考えられる。
実装はAppleのDragGestureのドキュメントを参考に
struct Dragable<Content: View>: View {
private let content: Content
@GestureState private var dragState = DragState.inactive
@State private var latestTranslation: CGSize = .zero
init (_ contentBuilder: () -> Content) {
content = contentBuilder()
}
init (_ content: Content) {
self.content = content
}
var body: some View {
let swipeGesture = LongPressGesture().sequenced(before: DragGesture())
.updating($dragState) { gesture, current, transaction in
switch (gesture) {
case .first(true):
current = .pressing
case .second(true, let dragging):
if (dragging == nil) {
let impactMed = UIImpactFeedbackGenerator(style: .medium)
impactMed.impactOccurred()
}
current = .dragging(translation: dragging?.translation ?? .zero)
default:
current = .inactive
}
}.onEnded({ gesture in
guard case .second(true, let dragging?) = gesture else { return }
latestTranslation.width += dragging.translation.width
latestTranslation.height += dragging.translation.height
})
return content
.frame(alignment: .center)
.offset(dragState.translation)
.offset(latestTranslation)
.simultaneousGesture(swipeGesture)
}
}

回転をする責務を持ったView
struct Rotatable<Content: View>: View {
private let content: Content
@GestureState private var rotateState = Angle(degrees: 0.0)
@State private var angle = Angle(degrees: 0.0)
init (_ contentBuilder: () -> Content) {
content = contentBuilder()
}
init (_ content: Content) {
self.content = content
}
var body: some View {
let rotationGesture = RotationGesture()
.updating($rotateState, body: { angle, current, transaction in
current = angle
})
.onEnded({ angle in
self.angle += angle
})
return content
.rotationEffect(angle + rotateState, anchor: .center)
.simultaneousGesture(rotationGesture)
}
}
struct Rotatable_Previews: PreviewProvider {
static var previews: some View {
Rotatable(Text("Hello World").padding(20))
}
}

上記二つを利用するView
struct Stamp<Content: View>: View {
private let content: Content
init (_ contentBuilder: () -> Content) {
content = contentBuilder()
}
init (_ content: Content) {
self.content = content
}
var body: some View {
Dragable {
Rotatable {
content
}
}
}
}

メリットとしては再利用可能性の向上と可読性の向上があげられ、
デメリットは記述量が増える?

責務で処理が纏まる事も十分可読性の向上に繋がっているし、Stampの実装を読んだ時にコンテンツをドラッグ、回転させることができることが一目見てわかる