2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめに

初めまして,大学1年生のkigです.
大学の課題でSwiftを使用して作成したアプリを,春休み中にリリースするため,機能追加や改良を行いました.今回は,Swiftでのアプリ制作でよく使用したコードや,必須なコードを誰にでも分かりやすいように,自分のアウトプットも兼ねてまとめてみました.サンプルコードもあるので,ご活用ください.
SwiftUIの文字表示(Stack)やボタン,@State,var,画面遷移...などの基盤部分は,Rikuto Sato様の【総集編】【SwiftUI開発講座】ゼロからのSwiftUI開発!~3時間ぶっ通しで基本をマスターしよう~にて,とても分かりやすい解説をしてくださっているので,こちらと併せることで様々なコードを書くことができるようになると思います.

目次

  1. データの永続化1(AppStorage)
  2. データの永続化2(UserDefault)
  3. Viewを表示する瞬間に処理を実行する(.onAppear)
  4. ボタンタップ,画面遷移と同時に処理を実行(.simultaneousGesture)
  5. ◯秒ごとに実行(Timer)
  6. おまけ 広告をつける(参考サイトの紹介のみ)

1. データの永続化1(AppStorage)

simulator_screenshot_80132327-5B52-47F8-A6D1-D0F430ACF683.png

ここでは,+ボタンを押すと1に1ずつ加算され,-ボタンを押すと,100から1ずつ減算されるアプリを作成しました.

ContentView.swift
import SwiftUI

struct ContentView: View {
    //変数宣言
    @State var up = 1
    @State var down = 100
    
    var body: some View {
        
        VStack {                                    //{}内を縦向きに表示する
            HStack{                                 //{}内を横向きに配置
                Text("\(up)")                      //変数oneを表示
                    .font(.system(size: 40))        //文字の大きさを指定
                Button(action: {
                    //ボタンが押された時の処理
                    up += 1
                }, label: {
                    //ボタンの見た目を記述
                    Text("+")
                        .font(.system(size: 40))
                })
            }
            .padding()      //1+ と -100の間に少しスペースを作る
            
            HStack{
                Button(action: {
                    down -= 1
                }, label: {
                    Text("-")
                        .font(.system(size: 40))
                })
                Text("\(down)")
                    .font(.system(size: 40))
            }
        }
    }
}
//ここから下は消しても問題ないため,次回から省略する(Xcode内のプレビューが消えるだけ)
#Preview {
    ContentView()
}


このコードでは,1度アプリを閉じるとupとdowwnの値が初期値に戻ります(up = 1とdown = 100).ここで,AppStorageを使うことで,アプリを1度閉じて再度開いても,upとdownが初期値に戻らなくなります.変更部分は

ContentView.swift
...
struct ContentView: View {
    //変数宣言
    @State var up = 1
    @State var down = 100
    
    var body: some View {...

の変数宣言をしている部分(2行だけ)です.下のように書き換えます.

ContentView.swift
...
    //変数宣言
    @AppStorage ("upKey") var up = 1
    @AppStorage ("downKey") var down = 100
    ...
@AppStorage ("キー値") var 変数名 = 

で変数宣言を行います.すると,値とキー値(文字列)が結びつき,アプリが削除されるまで端末内に保存されます.

しかし

このAppStorageですが,何でもかんでも保存できるわけではなく,配列など一部対応していない型があります.ここで登場するのがUserDefaultです.

参考サイト:【SwiftUI】@AppStorageでデータを永続化(端末保存)する

2. データの永続化2(UserDefault)

アプリ制作に配列は必須だと思います.が,UserDefaultの使い方を調べ,理解するのが一番大変でした.(難しそうなコードや単語が多かった...) まず,1.のコードで配列を使用します.

ContentView.swift
import SwiftUI

struct ContentView: View {
    //変数宣言
    @State var number = [1, 100]
    
    var body: some View {
        
        VStack {                                    //{}内を縦向きに表示する
            HStack{                                 //{}内を横向きに配置
                Text("\(number[0])")
                    .font(.system(size: 40))        //文字の大きさを指定
                Button(action: {
                    //ボタンが押された時の処理
                    number[0] += 1
                }, label: {
                    //ボタンの見た目を記述
                    Text("+")
                        .font(.system(size: 40))
                })
            }
            .padding()      //1+ と -100の間に少しスペースを作る
            
            HStack{
                Button(action: {
                    number[1] -= 1
                }, label: {
                    Text("-")
                        .font(.system(size: 40))
                })
                Text("\(number[1])")
                    .font(.system(size: 40))
            }
        }
    }
}

先ほどはupとdownという変数名でしたが,この2つの変数をnumber(Int型配列)に格納しました.見た目と動作は1.と全く同じです.では早速UserDefaultを使用して,配列を保存しましょう.

ContentView.swift
import SwiftUI

struct ContentView: View {
    //変数宣言
    @State var number = [1, 100]
    
    var body: some View {
        
        VStack {                                    //{}内を縦向きに表示する
            HStack{                                 //{}内を横向きに配置
                Text("\(number[0])")
                    .font(.system(size: 40))        //文字の大きさを指定
                Button(action: {
                    //ボタンが押された時の処理
                    number[0] += 1
                    //number全体を保存
                    UserDefaults.standard.set(number, forKey: "numberKey")
                }, label: {
                    //ボタンの見た目を記述
                    Text("+")
                        .font(.system(size: 40))
                })
            }
            .padding()      //1+ と -100の間に少しスペースを作る
            
            HStack{
                Button(action: {
                    number[1] -= 1
                    //number全体を保存
                    UserDefaults.standard.set(number, forKey: "numberKey")
                }, label: {
                    Text("-")
                        .font(.system(size: 40))
                })
                Text("\(number[1])")
                    .font(.system(size: 40))
            }
            .padding()
            //ボタン追加
            Button(action: {
                //ボタンを押したら,保存されたデータを読み込み
                guard let defaultNumber = UserDefaults.standard.array(forKey: "numberKey") as? [Int]
                else{return}
                number = defaultNumber
            }, label: {
                Text("保存されたデータを呼び出す")
                    .font(.system(size: 20))
            })
            
        }
    }
}

+または-ボタンを押し,加算or減算したと同時に,Number全体をNumberKeyに保存します.

//number全体を保存
UserDefaults.standard.set(number, forKey: "numberKey")
UserDefaults.standard.set(保存したい変数名, forKey: "キー値")

でAppStorageのように,キー値と変数を結びつけます.
例えば,+を4回,-を4回押したとしましょう.するとnumberは[5, 96]となります.一方で,ボタンを押すごとにnumberは保存されるため,numberKeyに結びついている値も[5, 96]になっています.この時点では保存しかしていないため,1度アプリを閉じて再度アプリを開くと,numberは[1, 100]となります(numberKeyに結びついている[5, 96]は保存されています).そのため,保存した値をnumberに代入する必要があります.([1, 100] に[5, 96]を代入する)

//ボタンを押したら,保存されたデータを読み込み
//.arrayや[Int]は,保存したい値の型によって変更する必要がある
guard let defaultNumber = UserDefaults.standard.array(forKey: "numberKey") as? [Int]
else{return}
Nnmber = defaultNumber

「保存されたデータを呼び出す」ボタンを押すことでdefaultNumberに,端末に保存してある[5, 96]を代入し,numberにdefaultNumberを代入するイメージです.以上で配列の保存,読み込みを行います.
simulator_screenshot_9861A8B0-B8E9-4B47-9E75-D47CFB952703.png

私がUserDefaultを使えるようになった際,参考にさせていただいた動画リンクも貼りますので,是非ご覧ください.
【初心者向け SwiftUI 】UserDefaults ってなに? ~ データを簡単に保存できる便利な方法を紹介します!

3. Viewを表示する瞬間に処理を実行する(.onAppear)

また先ほどのコードを再利用します.実行していただいた方は特に感じたかもしれませんが,2.のアプリは使いずらいと思います.例えば,ゲームで「オーブの数を更新する」なんてボタンありませんよね?こちらの操作がなくとも変数は更新されるのが一般的です.そこで,モディファイア「.onAppear」の登場です..onAppearはStackやViewのモディファイアとして使用することで,そのStack,Viewが表示される直前に好きな処理を行えるというものです.つまり!先ほどのコードにて,Numberの値を表示する直前に,Default_Numberの値を代入することで,ボタンを押さずにNumberの値を更新することができます.

ContentView.swift

import SwiftUI

struct ContentView: View {
    //変数宣言
    @State var number = [1, 100]
    
    var body: some View {
        
        VStack {                                //{}内を縦向きに表示する
            HStack{                                 //{}内を横向きに配置
                Text("\(number[0])")
                    .font(.system(size: 40))        //文字の大きさを指定
                Button(action: {
                    //ボタンが押された時の処理
                    number[0] += 1
                    //number全体を保存
                    UserDefaults.standard.set(number, forKey: "numberKey")
                }, label: {
                    //ボタンの見た目を記述
                    Text("+")
                        .font(.system(size: 40))
                })
            }
            .padding()      //1+ と -100の間に少しスペースを作る
            
            HStack{
                Button(action: {
                    number[1] -= 1
                    //number全体を保存
                    UserDefaults.standard.set(number, forKey: "numberKey")
                }, label: {
                    Text("-")
                        .font(.system(size: 40))
                })
                Text("\(number[1])")
                    .font(.system(size: 40))
            }
            .padding()
        }
        .onAppear(){
            //VStackが表示される直前に,保存されたデータを読み込み
            guard let defaultNumber = UserDefaults.standard.array(forKey: "numberKey") as? [Int]
            else{return}
            number = defaultNumber
        }
    }
}

VStackに.onAppearを付け,VStackが表示される直前にNumberの値を更新する処理を行っています.これで,値の更新が自動で行われるようになりました.

4. ボタンタップ,画面遷移と同時に処理を実行(.simultaneousGesture)

ここでは,ボタンタップ,または画面遷移ボタンが押された時に処理を行うことができるコードを紹介します.

simulator_screenshot_A6C13427-918A-445E-8F21-B7235BC14F6E.png simulator_screenshot_2DCCCC3B-463E-4351-9FEB-DD367D4B0798.png
ContentView.swift
import SwiftUI

struct ContentView: View {
    //変数宣言
    @State var count = 0
    
    var body: some View {
        NavigationStack{
            NavigationLink{
                //ここに count += 1 と書くとエラーになる
                NextView(NextViewCount: $count)
            } label: {
                Text("次の画面へ移動")
            }
            .simultaneousGesture(TapGesture().onEnded{
                //次の画面に移動するボタンを押したら,Countに1を加算
                count += 1
            })
        }
    }
}

次に,遷移後の画面のコードです.

NextView.swift
import SwiftUI

struct NextView: View {
    //ContentViewから変数の値を引き継ぐ
    @Binding var NextViewCount: Int
    
    var body: some View {
        Text("\(NextViewCount)")
            .padding()
    }
}

NavigationLinkの{}内に画面遷移処理以外(四則演算など)を記述するとエラーになります.そのため,NavigationLinkに以下のモディファイアをつけることで,画面遷移(ボタンが押される)と同時に,関数実行などの任意の処理を行うことができます.

.simultaneousGesture(TapGesture().onEnded{
    //次の画面に移動するボタンを押したら,countに1を加算
    count += 1
})

他のタップ処理の参考サイト:SwiftUIでのタップ処理の実装と応用

また,他の画面遷移時に処理を実行する方法として,3.の.onAppearを使用する方法もあります.この場合,NextView(遷移先の画面)を表示する直前にNextView_Countを+1することで実装できます.

5. ◯秒ごとに実行(Timer)

最後は,◯秒ごとに処理を行うコードです.時間計測などにも使用できます.
以下のコードでは,「開始」を押すと1秒おきにCountが+1され,「終了」を押すと,それが止まるアプリを書いています.

ContentView.swift
import SwiftUI

struct ContentView: View {
    //変数宣言
    @State var count = 0            //秒数
    @State var timer: Timer?
    @State var isRunning = false    //タイマーが動いているか,動いていないか
    
    var body: some View {
        //秒数表示
        Text("\(count)")
        //タイマー開始
        Button(action: {
            timeCount()
            isRunning = true
        }, label: {
            Text("開始")
        })
        //タイマーが動いているなら「開始」は押せない
        .disabled(isRunning == true)
        .padding()
        
        //タイマー終了
        Button(action: {
            //タイマーを無効にする
            timer?.invalidate()
            timer = nil
            isRunning = false
        }, label: {
            Text("終了")
        })
        //タイマーが動いていていないなら,「終了」は押せない
        .disabled(isRunning == false)
    }
    
    //1秒ごとにcountに1を加算(Intervalの値を変更することで◯秒ごとに実行,なども可能)
    func timeCount(){
        timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) {timer in
            count += 1
        }
    }
}

タイマーが起動している間は「開始」ボタンが押せなくなっています.この理由は,タイマーが重複して起動してしまうのを防ぐためです.タイマーが重複して起動した場合,各タイマーはそれぞれが独立して処理を行うため,1秒に何回も加算されてしまったりします.動作が重くなってしまうため,この辺りはよく動作確認をしましょう.

simulator_screenshot_2F11AD7A-3BF9-42F6-95EB-6055EE600276.png simulator_screenshot_2D624E86-663D-4AEC-9878-0D2805B9500A.png

6.おまけ 広告をつける

SwiftUIにてアプリに広告をつける際,参考にさせていただいたサイトを紹介させていただきます.
【SwiftUI】AdMobを使って広告を実装する方法!バナーViewの作成
【SwiftUI】リワード広告の表示

最後に

ここまでSwiftでアプリ制作に使用したコードをまとめました.各コードの使用方法を考えることで,実装できるものが増えると思います!

残りの春休みの時間はUnityを勉強し,作りたいゲームの構想もできているので,リリースに向けて1から頑張っていこうと思います.
宣伝にはなりますが,今回私が製作したアプリはAppStoreにて「Dot_Touch」というアプリ名でリリースしました.将来的にはDot_TouchもUnityで作り直せたらいいなと思います.

以上です,ご覧いただきありがとうございました!

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?