見出し画像

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を加えます。

図1.NavigationView付加
import SwiftUI

struct ListViewView {
    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_PreviewsPreviewProvider {
    static var previews: some View {
        ListView()
    }
}

List配下にNavigationLinkを加えます。

図2.NavigationLink付加
import SwiftUI

struct ListViewView {
    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_PreviewsPreviewProvider {
    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を修正していきます。
前回の記事の改善点として挙げています。

前回の記事 ↓  

下記の様に修正します。

図3.ForEachで格納されているデータを表示
import SwiftUI

struct ListRowView {
    
    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_PreviewsPreviewProvider {
    static var previews: some View {
        ListRow(model: [UmamusumeModel(name: "オグリキャップ",imageName: "umamusume_oguri")])
    }
}

次にListView.swiftをしていきます。
下記の様に修正します。

図4.List()の応用を活用
import SwiftUI

struct ListViewView {
    
    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_PreviewsPreviewProvider {
    static var previews: some View {
        ListView()
    }
}

List()はRangeを指定することが出来ます。
使える様になるとかなり便利です。

最後はImageView.swiftを修正していきます。
下記の様に修正します。

import SwiftUI

struct ImageViewView {
    
    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_PreviewsPreviewProvider {
    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を実行)

図5.配列番号2番のオグリキャップの画像

なんかサイズが違うけどなんでだ…( ・∇・)
考えます…(´ω`)

ImageView.swiftの.scaledToFit()を.scaledToFill()に変更すれば、大きさが統一されるはず…

試しに変更します。

図6..scaledToFill()付加後のImageView
import SwiftUI

struct ImageViewView {
    
    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_PreviewsPreviewProvider {
    static var previews: some View {
        ImageView(model: [UmamusumeModel(name: "オグリキャップ", imageName: "umamusume_oguri_run")])
    }
}

うん…、デカくねかい( ・∇・)
オグリキャップの圧を感じるんだが…(´ω`)

.frame()を付けるのを忘れていました。
下記、.frame(width: 200, height: 200, alignment: .center)で指定した時の表示です。

図7..frame(width: 200, height: 200, alignment: .center)で指定した時のImageView
import SwiftUI

struct ImageViewView {
    
    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_PreviewsPreviewProvider {
    static var previews: some View {
        ImageView(model: [UmamusumeModel(name: "オグリキャップ", imageName: "umamusume_oguri_run")])
    }
}

動作確認してみます。
▶️のマークを押下してみましょう。(Runを実行)

問題なそうですね( ̄∇ ̄)

ソースコード全体

ListRow.swift

import SwiftUI

struct ListRowView {
    
    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_PreviewsPreviewProvider {
    static var previews: some View {
        ListRow(model: [UmamusumeModel(name: "オグリキャップ",imageName: "umamusume_oguri")])
    }
}

ListView.swift

import SwiftUI

struct ListViewView {
    
    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_PreviewsPreviewProvider {
    static var previews: some View {
        ListView()
    }
}

ImageView.swift

import SwiftUI

struct ImageViewView {
    
    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_PreviewsPreviewProvider {
    static var previews: some View {
        ImageView(model: [UmamusumeModel(name: "オグリキャップ", imageName: "umamusume_oguri")])
    }
}

ContentView.swift

import SwiftUI

struct ContentViewView {
    
    var body: some View {
        VStack {
            ListView()
        }
    }
}

struct ContentView_PreviewsPreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

おわりに


今回は以下に関して、学習しました。

● NavigationView + NavigationLinkの使い方 
● ForEach()の使い方

前回の改善点を修正できたので、一歩成長することが出来ました(^ω^)
次回も丁寧に書く様に努力します(´ω`)

参考文献


今回使用した画像の引用場所↓
佐伯先生、「食戟のソーマ」面白かったです!(^ω^)

天気海苔さん、可愛いオグリちゃんを描いてくれてありがとうございます(´ω`)

Mishiroさんいつもありがとうございます( ^ω^ )

この記事が参加している募集

この記事が気に入ったらサポートをしてみませんか?