SwiftUI Tutorialsを見ながら、SwiftUIを学習する④
はじめに
前回の記事↓
今回もApple Developer公式にあるSwiftUIの概要 - Xcode - Apple Developerを参考に学習したことを記載して行こうと思います。
使用環境
● OS:macOS Big Sur 11.3.1
● Xcode:12.5
● Swift:5.4
● SwiftUI
今回学習したこと
NavigationView + NavigationLink
NavigationViewを使用して、ナビゲーション画面を管理できる様にします。
NavigationView配下にListを加えます。
import SwiftUI
struct ListView: View {
var body: some View {
NavigationView {
List {
ListRow(model: [UmamusumeModel(name: "オグリキャップ",imageName: "umamusume_oguri")])
ListRow(model: [UmamusumeModel(name: "オグリ", imageName: "umamusume_oguri_run")])
ListRow(model: [UmamusumeModel(name: "オグリちゃん", imageName: "umamusume_oguri_cute")])
}.navigationTitle("推しのウマ娘")
}
}
}
struct ListView_Previews: PreviewProvider {
static var previews: some View {
ListView()
}
}
List配下にNavigationLinkを加えます。
import SwiftUI
struct ListView: View {
var body: some View {
NavigationView {
List {
NavigationLink(
destination: Text("Destination"),
label: {
ListRow(model: [UmamusumeModel(name: "オグリキャップ",imageName: "umamusume_oguri")])
ListRow(model: [UmamusumeModel(name: "オグリ", imageName: "umamusume_oguri_run")])
ListRow(model: [UmamusumeModel(name: "オグリちゃん", imageName: "umamusume_oguri_cute")])
})
}.navigationTitle("推しのウマ娘")
}
}
}
struct ListView_Previews: PreviewProvider {
static var previews: some View {
ListView()
}
}
あれ…、なんか可笑しいぞ???( ・∇・)
なぜか考えよう…( ̄∇ ̄)
NavigationLinkの引数Labelのスコープ内では、Objectを一つ分として構成する決まりみたいです。
なので、もう一つListRowを作成したい場合は、NavigationLinkをもう一つ作成し引数LabelにListRowを加える必要があります。
NavigationLink(destination: Text("Destination"),
label: {
ListRow(model: [UmamusumeModel(name: "オグリキャップ",
imageName: "umamusume_oguri")])
})
NavigationLink(destination: Text("Destination"),
label: {
ListRow(model: [UmamusumeModel(name: "オグリキャップ",
imageName: "umamusume_oguri")])
})
今回、この書き方ではデータが増える度に、NavigationLinkも比例して増加しますので、ソースコード数が肥大してしまいます。
なので、ForEachとListの応用を活用してデータが増えても、NavigationLinkが増加しない様にします。
まずはListRow.swiftを修正していきます。
前回の記事の改善点として挙げています。
前回の記事 ↓
下記の様に修正します。
import SwiftUI
struct ListRow: View {
var model = [UmamusumeModel]()
var body: some View {
HStack {
// model.startIndex: 0
// model.endIndex: 2
// 許容範囲: 0..< 2
ForEach(model.startIndex ..< model.endIndex) { num in
Image(model[num].imageName)
.resizable()
.scaledToFit()
.clipShape(Circle())
.frame(width: 80, height: 80)
Text(model[num].name)
Spacer()
}
}
}
}
struct ListRow_Previews: PreviewProvider {
static var previews: some View {
ListRow(model: [UmamusumeModel(name: "オグリキャップ",imageName: "umamusume_oguri")])
}
}
次にListView.swiftをしていきます。
下記の様に修正します。
import SwiftUI
struct ListView: View {
var model = [UmamusumeModel(name: "オグリキャップ",imageName: "umamusume_oguri"),
UmamusumeModel(name: "オグリ", imageName: "umamusume_oguri_run"),
UmamusumeModel(name: "オグリちゃん", imageName: "umamusume_oguri_cute")]
var body: some View {
NavigationView {
// model.startIndex: 0
// model.endIndex: 2
// 許容範囲: 0..< 2
List(model.startIndex ..< model.endIndex) { i in
NavigationLink(
destination: ImageView(model: [model[i]]),
label: {
ListRow(model: [model[i]])
})
}.navigationTitle("推しのウマ娘")
}
}
}
struct ListView_Previews: PreviewProvider {
static var previews: some View {
ListView()
}
}
List()はRangeを指定することが出来ます。
使える様になるとかなり便利です。
最後はImageView.swiftを修正していきます。
下記の様に修正します。
import SwiftUI
struct ImageView: View {
var model = [UmamusumeModel]()
var body: some View {
VStack {
// model.startIndex: 0
// model.endIndex: 2
// 許容範囲: 0..< 2
ForEach(model.startIndex ..< model.endIndex) { num in
Image(model[num].imageName)
.resizable()
.scaledToFit()
.clipShape(Circle())
.overlay(Circle()
.stroke(Color.white, lineWidth: 4)
.shadow(radius: 3))
Text(model[num].name)
.font(.title3)
.fontWeight(.regular)
.underline()
}
}
.padding(.init(top: 50, leading: 50, bottom: 50, trailing: 50))
}
}
struct ImageView_Previews: PreviewProvider {
static var previews: some View {
ImageView(model: [UmamusumeModel(name: "オグリキャップ", imageName: "umamusume_oguri")])
}
}
ForEachの引数numは、Int型になります。
引数numに入る値は、0、1、2 ですので、3個分の値を生成したことになります。
Image()には以下の値が入ります。
● model[0].imageName
● model[1].imageName
● model[2].imageName
Text()も同様に以下の様になります。
● model[0].name
● model[1].name
● model[2].name
動作確認してみます。
▶️のマークを押下してみましょう。(Runを実行)
なんかサイズが違うけどなんでだ…( ・∇・)
考えます…(´ω`)
ImageView.swiftの.scaledToFit()を.scaledToFill()に変更すれば、大きさが統一されるはず…
試しに変更します。
import SwiftUI
struct ImageView: View {
var model = [UmamusumeModel]()
var body: some View {
VStack {
ForEach(model.startIndex ..< model.endIndex) { num in
Image(model[num].imageName)
.resizable()
.scaledToFill()
.clipShape(Circle())
.overlay(Circle()
.stroke(Color.white, lineWidth: 4)
.shadow(radius: 3))
Text(model[num].name)
.font(.title3)
.fontWeight(.regular)
.underline()
}
}
.padding(.init(top: 50, leading: 50, bottom: 50, trailing: 50))
}
}
struct ImageView_Previews: PreviewProvider {
static var previews: some View {
ImageView(model: [UmamusumeModel(name: "オグリキャップ", imageName: "umamusume_oguri_run")])
}
}
うん…、デカくねかい( ・∇・)
オグリキャップの圧を感じるんだが…(´ω`)
.frame()を付けるのを忘れていました。
下記、.frame(width: 200, height: 200, alignment: .center)で指定した時の表示です。
import SwiftUI
struct ImageView: View {
var model = [UmamusumeModel]()
var body: some View {
VStack {
ForEach(model.startIndex ..< model.endIndex) { num in
Image(model[num].imageName)
.resizable()
.scaledToFill()
.clipShape(Circle())
.frame(width: 200, height: 200, alignment: .center)
.overlay(Circle()
.stroke(Color.white, lineWidth: 4)
.shadow(radius: 3))
Text(model[num].name)
.font(.title3)
.fontWeight(.regular)
.underline()
}
}
.padding(.init(top: 50, leading: 50, bottom: 50, trailing: 50))
}
}
struct ImageView_Previews: PreviewProvider {
static var previews: some View {
ImageView(model: [UmamusumeModel(name: "オグリキャップ", imageName: "umamusume_oguri_run")])
}
}
動作確認してみます。
▶️のマークを押下してみましょう。(Runを実行)
問題なそうですね( ̄∇ ̄)
ソースコード全体
ListRow.swift
import SwiftUI
struct ListRow: View {
var model = [UmamusumeModel]()
var body: some View {
HStack {
ForEach(model.startIndex ..< model.endIndex) { num in
Image(model[num].imageName)
.resizable()
.scaledToFit()
.clipShape(Circle())
.frame(width: 80, height: 80)
Text(model[num].name)
Spacer()
}
}
}
}
struct ListRow_Previews: PreviewProvider {
static var previews: some View {
ListRow(model: [UmamusumeModel(name: "オグリキャップ",imageName: "umamusume_oguri")])
}
}
ListView.swift
import SwiftUI
struct ListView: View {
var model = [UmamusumeModel(name: "オグリキャップ",imageName: "umamusume_oguri"),
UmamusumeModel(name: "オグリ", imageName: "umamusume_oguri_run"),
UmamusumeModel(name: "オグリちゃん", imageName: "umamusume_oguri_cute")]
var body: some View {
NavigationView {
List(model.startIndex ..< model.endIndex) { i in
NavigationLink(
destination: ImageView(model: [model[i]]),
label: {
ListRow(model: [model[i]])
})
}.navigationTitle("推しのウマ娘")
}
}
}
struct ListView_Previews: PreviewProvider {
static var previews: some View {
ListView()
}
}
ImageView.swift
import SwiftUI
struct ImageView: View {
var model = [UmamusumeModel]()
var body: some View {
VStack {
ForEach(model.startIndex ..< model.endIndex) { num in
Image(model[num].imageName)
.resizable()
.scaledToFill()
.clipShape(/*@START_MENU_TOKEN@*/Circle()/*@END_MENU_TOKEN@*/)
.frame(width: 200, height: 200, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/)
.overlay(Circle()
.stroke(Color.white, lineWidth: 4)
.shadow(radius: 3))
Text(model[num].name)
.font(.title3)
.fontWeight(.regular)
.underline()
}
}
.padding(.init(top: 50, leading: 50, bottom: 50, trailing: 50))
}
}
struct ImageView_Previews: PreviewProvider {
static var previews: some View {
ImageView(model: [UmamusumeModel(name: "オグリキャップ", imageName: "umamusume_oguri")])
}
}
ContentView.swift
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
ListView()
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
おわりに
今回は以下に関して、学習しました。
● NavigationView + NavigationLinkの使い方
● ForEach()の使い方
前回の改善点を修正できたので、一歩成長することが出来ました(^ω^)
次回も丁寧に書く様に努力します(´ω`)
参考文献
今回使用した画像の引用場所↓
佐伯先生、「食戟のソーマ」面白かったです!(^ω^)
天気海苔さん、可愛いオグリちゃんを描いてくれてありがとうございます(´ω`)
Mishiroさんいつもありがとうございます( ^ω^ )