Today、わかりました:SwitUI FlashCardの作成
今日は、SwitUIでFlashCardを作成し、これらの概念をまとめます.
(プロジェクト完了リンク:https://github.com/kipsong133/TIL/tree/main/2022/02/22/SwiftUI_FlashCardExample)
インプリメンテーション
UI->観測可能オブジェクトの構成->Gesture接続を実装します.
UI実装
フラッシュメモリカードなので、カードUIがあるはずです.以下のUIを構成するSwitUIViewを作成しました.
今までに実現したのは1枚のカードで、使う時には何枚ものカードがあるはずです.ビューを再作成し、「DeckView」と名前を付けます.
UI実装はこれで終了する.次に、ObserverObjectまたはタイプを定義してバインドします.観測対象配置 まずstructを使用してカードのデータ型を定義します.
Quizタイプを定義します.
Equatableによる整合性の検証が可能な構造体を定義します.
最後に、すべてのカードを含むDeckクラスを定義します.
@Publishedを使用してプロパティを囲み、UIの更新時にオブジェクトがカードであることを確認します.
初期化方法では、タイプを入力した「質問」->「カード」に変更します.
DeckView->CardViewでカードごとのメッセージを伝えましょうだからカードビューからデータを受信するつもりです
まず、Cardをメンバー変数として定義します.
これで、ユーザーインタフェースの操作とデータ接続が完了しました.ZStackなので見えないカードを見てみましょうFlashCard機能の実装を完了するためにジェスチャーを追加します. Gesture カードを左または右に移動する場合は、カードが右または左に消えるように移動位置を決定します.そのためには、以下の事項を実施する必要があります.
ジェスチャー認識
ジェスチャーを区別(左または右)
Dragによるオブジェクト位置の移動(Transition)
Dragを終了したら、移動カード(DragGesture+onEnded)
ひとつずつ追加しよう
まず、enumを定義して、左に移動するか右に移動するかを決定します.
パンプロファイルは、開始位置と変更位置の差を持つプロファイルです.ドラッグオブジェクトの移動距離をoffsetに指定して位置を移動します.
アニメーションはspring効果を生成し、アニメーションの値はoffsetです.
ビューの位置はoffsetによって変更されます.
最後にジェスチャーを追加してみんなをインタラクティブにします
ここでは、次の操作を行います.
今、左に移動するか右に移動するかによって、カードを消したいと思っています.
まず、CardViewはドラッグに論理を追加し、値に応じてカードの位置を変更します.
initコードを変更します.
整理する
今回使ったコンセプトを整理してみます.
外部オブジェクトのプロパティからビューのUIを更新するためにObservableObjectが構成されています.
DragGestureとTranslation Propertyを使用して、オブジェクトの位置を変更するかどうかを決定します.
確認した場所にアニメーションを送信しました.
最後にoffsetとgeserを追加し、上で構成したドラッグと移動アニメーションを有効にします.
参考資料 https://www.raywenderlich.com/books/swiftui-by-tutorials/v4.0/chapters/11-gestures#toc-chapter-015-anchor-001 読んでくれてありがとう.
(プロジェクト完了リンク:https://github.com/kipsong133/TIL/tree/main/2022/02/22/SwiftUI_FlashCardExample)
インプリメンテーション
UI->観測可能オブジェクトの構成->Gesture接続を実装します.
UI実装
フラッシュメモリカードなので、カードUIがあるはずです.以下のUIを構成するSwitUIViewを作成しました.
struct CardView: View {
var body: some View {
ZStack {
// setup BackgroundColor
Rectangle()
.fill(Color.blue)
.frame(width: 320, height: 210)
.cornerRadius(12)
VStack {
Spacer()
Group {
Text("Flash Card Title")
.font(.largeTitle)
Text("Answer")
.font(.headline)
}
.foregroundColor(.white)
Spacer()
}
}
.shadow(radius: 8)
.frame(width: 320, height: 210)
}
}
今までに実現したのは1枚のカードで、使う時には何枚ものカードがあるはずです.ビューを再作成し、「DeckView」と名前を付けます.
struct DeckView: View {
var body: some View {
ZStack {
CardView()
CardView()
}
}
}
今ではZStackにすぎませんが、ここではデータをバインドし、ジェスチャーを追加します.UI実装はこれで終了する.次に、ObserverObjectまたはタイプを定義してバインドします.
Quizタイプを定義します.
struct Quiz {
let question: String
let answer: String
}
次にCardを定義します.struct Card: Identifiable {
var card: Quiz
var id = UUID()
}
extension Card: Equatable {
static func == (lhs: Card, rhs: Card) -> Bool {
return lhs.card.question == rhs.card.question
&& lhs.card.answer == rhs.card.answer
}
}
Identifiableは一意性を与えた.Equatableによる整合性の検証が可能な構造体を定義します.
最後に、すべてのカードを含むDeckクラスを定義します.
class Deck: ObservableObject {
@Published var cards: [Card]
init(from cards: [Quiz]) {
self.cards = cards.map { Card(card: $0) }
}
}
ビューで観察できるオブジェクトプロトコルを使用します.@Publishedを使用してプロパティを囲み、UIの更新時にオブジェクトがカードであることを確認します.
初期化方法では、タイプを入力した「質問」->「カード」に変更します.
DeckView->CardViewでカードごとのメッセージを伝えましょうだからカードビューからデータを受信するつもりです
まず、Cardをメンバー変数として定義します.
struct CardView: View {
var card: Card
...
}
テキストのStringProtocolパラメータ部分をbody property内部の変数に変更します.Text(card.quiz.question)
...
Text(card.quiz.answer)
必ずやるわけではありませんが、間違いを見たくないので、Previewも以下のように修正しました.struct CardView_Previews: PreviewProvider {
@State static var card = Card(quiz: quiz01)
static var previews: some View {
CardView(card: card)
.previewLayout(.device)
}
}
これで、DeckViewが初期化時にcard値をポイントに渡すと、データはCardViewに渡されます.struct DeckView: View {
@StateObject var deck = Deck(from: quizBundle)
var body: some View {
ZStack {
ForEach(deck.cards) { card in
CardView(card: card)
}
}
}
}
StateObject全体を伝えるのではなく、@Publishedで包まれたPropertyカードを渡すことに注意してください!これで、ユーザーインタフェースの操作とデータ接続が完了しました.ZStackなので見えないカードを見てみましょうFlashCard機能の実装を完了するためにジェスチャーを追加します.
ジェスチャー認識
ジェスチャーを区別(左または右)
Dragによるオブジェクト位置の移動(Transition)
Dragを終了したら、移動カード(DragGesture+onEnded)
ひとつずつ追加しよう
まず、enumを定義して、左に移動するか右に移動するかを決定します.
enum DismissCardDirection {
case left
case right
}
ドラッグしたビューはCardViewです.ドラッグに関連するジェスチャーとアニメーションを追加します.struct CardView: View {
...
@State var offset: CGSize = .zero
var body: some View {
let drag = DragGesture()
.onChanged { offset = $0.translation }
return ZStack {
...
}
...
.animation(.spring(), value: offset)
.offset(offset)
.gesture(drag)
}
}
offsetでカードの位置を移動するたびに、移動する位置に変更されます.パンプロファイルは、開始位置と変更位置の差を持つプロファイルです.ドラッグオブジェクトの移動距離をoffsetに指定して位置を移動します.
アニメーションはspring効果を生成し、アニメーションの値はoffsetです.
ビューの位置はoffsetによって変更されます.
最後にジェスチャーを追加してみんなをインタラクティブにします
ここでは、次の操作を行います.
今、左に移動するか右に移動するかによって、カードを消したいと思っています.
まず、CardViewはドラッグに論理を追加し、値に応じてカードの位置を変更します.
let drag = DragGesture()
.onChanged { offset = $0.translation }
.onEnded {
// move left
if $0.translation.width < -100 {
// dismiss left
offset = .init(width: -1000, height: 0)
// memorized card
dragged(card, .left)
// move right
} else if $0.translation.width > 100 {
// dismiss right
offset = .init(width: 1000, height: 0)
// memorized card
dragged(card, .right)
// move in the middle
} else {
// move base
offset = .zero
}
}
次に、追加されていないPropertyとtypealiasを追加します. typealias CardDrag = (_ card: Card,
_ direction: DismissCardDirection) -> Void
let dragged: CardDrag
今回の機能実装では、記憶されたカードと記憶されていないカードの論理は実装されませんが、それらの間のデータ交換を確保するために処理します.initコードを変更します.
init(
card: Card,
onDrag dragged: @escaping CardDrag = {_, _ in }) {
self.card = card
self.dragged = dragged
}
最後に、DeckViewでカードビューの宣言を変更します.struct DeckView: View {
@StateObject var deck = Deck(from: quizBundle)
let onMemorized: (Card) -> Void = { _ in }
var body: some View {
ZStack {
ForEach(deck.cards) { card in
CardView(card: card) { card, direction in
if direction == .left {
onMemorized(card)
} else {
// do something
}
}
}
}
}
}
次に、次の操作を行います.整理する
今回使ったコンセプトを整理してみます.
外部オブジェクトのプロパティからビューのUIを更新するためにObservableObjectが構成されています.
DragGestureとTranslation Propertyを使用して、オブジェクトの位置を変更するかどうかを決定します.
確認した場所にアニメーションを送信しました.
最後にoffsetとgeserを追加し、上で構成したドラッグと移動アニメーションを有効にします.
参考資料
Reference
この問題について(Today、わかりました:SwitUI FlashCardの作成), 我々は、より多くの情報をここで見つけました https://velog.io/@kipsong/Today-I-learned-SwiftUI-FlashCard-만들기テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol