単一カードビューの設計
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)
}
}