100 Days of SwiftUI(DAY 88(Project17 part 3))

単一カードビューの設計
Designing a single card view

flashcards(要調査!!)

このプロジェクトでは、ユーザーに「スコットランドの首都は何ですか?」など、知りたいことについてのプロンプトテキストが付いたカードを表示し、タップすると答えが表示されます。この場合はもちろんエジンバラです。

ほとんどのプロジェクトで開始する賢明な場所は、使用するデータモデルを定義することです。

Portrait, Landscape Left, and Landscape Right(横表示)
(「PROJECT」「TARGET」)
デフォルト値は、ポートレート、ランドスケープレフト、ランドスケープライトをチェックすることですが、ポートレートのチェックを外して、2つのランドスケープの向きのみがサポートされるようにしてください。

450の幅は偶然ではありません。最小のiPhoneの横幅は480ポイントであるため、このカードはすべてのデバイスで完全に表示されます。

//
//  Card.swift
//  Flashzilla

import Foundation

struct Card {
    let prompt: String
    let answer: String

    static var example: Card {
        Card(prompt: "Who played the 13th Doctor in Doctor Who?", answer: "Jodie Whittaker")
    }
}

//
//  CardView.swift
//  Flashzilla

import SwiftUI

struct CardView: View {
    @State private var isShowingAnswer = false
    let card: Card

    var body: some View {
        ZStack {
            RoundedRectangle(cornerRadius: 25, style: .continuous)
                .fill(Color.white)
                .shadow(radius: 10)

            VStack {
                Text(card.prompt)
                    .font(.largeTitle)
                    .foregroundColor(.black)

                if isShowingAnswer {
                    Text(card.answer)
                        .font(.title)
                        .foregroundColor(.gray)
                }
            }
            .padding(20)
            .multilineTextAlignment(.center)
        }
        .frame(width: 450, height: 250)
        .onTapGesture {
            self.isShowingAnswer.toggle()
        }
    }
}

struct CardView_Previews: PreviewProvider {
    static var previews: some View {
        CardView(card: Card.example)
    }
}

//
//  ContentView.swift
//  Flashzilla

import SwiftUI

struct ContentView: View {
    var body: some View {
        CardView(card: Card.example)
    }
    
}

カードのスタックを構築する
Building a stack of cards

Swiftの配列には便利な初期化子があります。初期化子init(repeating:count:)は1つの値を取り、それを何度も繰り返して配列を作成します。今回の例では、これを使用してCard簡単なテスト配列を作成できます。

@State private var cards = [Card](repeating: Card.example, count: 10)

SwiftUIコードを記述する最善の方法は、乱雑な計算を切り分けて、メソッドまたは修飾子として処理することです。今回は、はstacked()

//
//  ContentView.swift
//  Flashzilla


import SwiftUI
//-------------
struct ContentView: View {
    @State private var cards = [Card](repeating: Card.example, count: 10)
    
    var body: some View {
        ZStack {
            Image("background")
            .resizable()
            .scaledToFill()
            .edgesIgnoringSafeArea(.all)
            VStack {
                ZStack {
                    ForEach(0..<cards.count, id: \.self) { index in
                        CardView(card: self.cards[index])
                            .stacked(at: index, in: self.cards.count)
                    }
                }
            }
        }
    }
    
}
//-------------
extension View {
    func stacked(at position: Int, in total: Int) -> some View {
        //let offset = CGFloat(total - position)
        //配列内の場所ごとにビューを10ポイント押し下げます(0、次に10、20、30など)
        // return self.offset(CGSize(width: 0, height: position * 10))
        let offset = CGFloat(total - position)
        return self.offset(CGSize(width: 0, height: offset * 10))
    }
}
//-------------
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}


DragGestureとoffset()によるビューの移動
Moving views with DragGesture and offset()

DragGestureでCardView移動できるようにします。
ジェスチャーによって生成された値を使用して、ビューの不透明度と回転を制御します。

移動と回転に関しては、ビューを回転させながら、ビューを真の西に(その回転に関係なく)直接スライドさせる場合は、最初に回転を配置し、次にオフセットを配置する必要があります。

3つの修飾子
rotationEffect
offset
opacity


//
//  ContentView.swift
//  Flashzilla

import SwiftUI
//-------------
struct ContentView: View {
    @State private var cards = [Card](repeating: Card.example, count: 10)
    
    func removeCard(at index: Int) {
        cards.remove(at: index)
    }

    
    var body: some View {
        ZStack {
            Image("background")
            .resizable()
            .scaledToFill()
            .edgesIgnoringSafeArea(.all)
            VStack {
                ZStack {
                    ForEach(0..<cards.count, id: \.self) { index in
                        CardView(card: self.cards[index]) {
                           withAnimation {
                               self.removeCard(at: index)
                           }
                        }
                        .stacked(at: index, in: self.cards.count)
                    }
                }
            }
        }
    }
    
}
//-------------
extension View {
    func stacked(at position: Int, in total: Int) -> some View {
        //let offset = CGFloat(total - position)
        //配列内の場所ごとにビューを10ポイント押し下げます(0、次に10、20、30など)
        // return self.offset(CGSize(width: 0, height: position * 10))
        let offset = CGFloat(total - position)
        return se

lf.offset(CGSize(width: 0, height: offset * 10))
    }
}
//-------------


//
//  CardView.swift
//  Flashzilla
//
//  Created by Naoki Abe on 2020/09/09.
//  Copyright © 2020 Naoki Abe. All rights reserved.
//

import SwiftUI

struct CardView: View {
    @State private var isShowingAnswer = false
    @State private var offset = CGSize.zero
    
    let card: Card
    var removal: (() -> Void)? = nil
    
    var body: some View {
        ZStack {
            RoundedRectangle(cornerRadius: 25, style: .continuous)
                .fill(Color.white)
                .shadow(radius: 10)

            VStack {
                Text(card.prompt)
                    .font(.largeTitle)
                    .foregroundColor(.black)

                if isShowingAnswer {
                    Text(card.answer)
                        .font(.title)
                        .foregroundColor(.gray)
                }
            }
            .padding(20)
            .multilineTextAlignment(.center)
        }
        .frame(width: 450, height: 250)
        .rotationEffect(.degrees(Double(offset.width / 5)))
        .offset(x: offset.width * 5, y: 0)
        .opacity(2 - Double(abs(offset.width / 50)))
        .gesture(
            DragGesture()
                .onChanged { gesture in
                    self.offset = gesture.translation
                }

                .onEnded { _ in
                    if abs(self.offset.width) > 100 {
                        // remove the card
                        self.removal?()
                    } else {
                        self.offset = .zero
                    }
                }
        )
        .onTapGesture {
            self.isShowingAnswer.toggle()
        }
    }
}

struct CardView_Previews: PreviewProvider {
    static var previews: some View {
        CardView(card: Card.example)
    }
}