2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

メモ保存~swiftUI備忘録~

2
Posted at

今回はSwiftUIでは以下のようなiphoneユーザならよく目にする保存UIをコードを追って作っていきます。
SwiftUIは開発者ライクと言われていますが、深堀して行くとやはり難しいですね

このUI機能の仕様

1.ユーザーが入力できるテキスト部分
2.保存機能
3.読み込みボタン

笑っちゃうくらい単純な機能ですね。

スクリーンショット 2021-06-13 16.06.55.png

全体のコード

//
//  ContentView.swift
//  Shared
//
//  Created by 市川マサル on 2021/06/12.
//

import SwiftUI
//キーボードを下げる
extension UIApplication {//UIApplicationクラス拡張してキーボードを下げる。
    func endEditing() {
        sendAction(
            #selector(UIResponder.resignFirstResponder),
            to:nil,from:nil,for:nil
        )
    }
}

struct ContentView: View {
    @State var theText: String = ""
    var body: some View {
        NavigationView {
            TextEditor(text: $theText)
                .lineSpacing(10)
                .border(Color.gray)
                .padding([.leading,.bottom,.trailing])
                .navigationTitle("メモ")
                .toolbar{
                    //読み込みボタン
                    ToolbarItem(placement: .navigationBarTrailing) {
                        Button {
                            if let data  = loadText("sample.txt") {
                                theText = data
                            }
                            } label: {
                                Text("読み込み")
                            }
                    }
                    //保存ボタン
                    ToolbarItem(placement: .navigationBarTrailing) {
                        Button {
                            UIApplication.shared.endEditing()//キーボードを下げる
                            saveText(theText, "sample.txt")//ユーザー定義関数のsaveText()テキストデータを保存する
                        } label: {
                            Text("保存")
                        }
                    }
                    
                }
        }
    }
    
    func saveText(_ textData:String, _ fileName:String){
        //docURLはユーザー定義変数
        guard let url = docURL(fileName) else {
            return
        }
        //ファイルパスへの保存
        do {
            let path = url.path
            try textData.write(toFile: path, atomically: true, encoding:  .utf8)
        } catch let error as NSError {
            print(error)
        }
    }
    
    func docURL(_ fileName:String) -> URL? {
        let fileManager = FileManager.default
        do {
            //Documentsフォルダ
            let docsUrl = try fileManager.url(
                //Documentフォルダを指定する。
                for: .documentDirectory,
                in: .userDomainMask,
                appropriateFor: nil,
                create: false)
            let url = docsUrl.appendingPathComponent(fileName)
            return url
        }catch {
            return nil
        }
    }
    
    //テキストデータを読み込んで返す。
    func loadText(_ fileName:String) -> String? {
        //URLを得られたらアンラップしてurlに代入
        guard let url = docURL(fileName) else {
            return nil
        }
        //urlからの読み込み
        do {
            let textData = try String(contentsOf: url, encoding: .utf8)
            return textData
        } catch {
            return nil
        }
    }
}

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



ひとつづつ説明していきます。

1.キーボードを下げるコード

appleから提供している便利なコードですこれを実装して呼び出すだけで下げてくれます。大変便利ですし汎用性も高いんではないでしょうか??
呼び出し先としてはテキストの.toolbar内に記述します。toolbarの保存ボタンを押すと閉じる処理と考えるとわかりやすいです。

extension UIApplication {//UIApplicationクラス拡張してキーボードを下げる。
    func endEditing() {
        sendAction(
            #selector(UIResponder.resignFirstResponder),
            to:nil,from:nil,for:nil
        )
    }
}

2.メインビジュアルのコード

struct ContentView: View {
    @State var theText: String = ""
    var body: some View {
        NavigationView {
            TextEditor(text: $theText)
                .lineSpacing(10)
                .border(Color.gray)
                .padding([.leading,.bottom,.trailing])
                .navigationTitle("メモ")
                .toolbar{
                    //保存ボタン
                    ToolbarItem(placement: .navigationBarTrailing) {
                        Button {
                            UIApplication.shared.endEditing()//キーボードを下げる
                            saveText(theText, "sample.txt")//ユーザー定義関数のsaveText()テキストデータを保存する
                        } label: {
                            Text("保存")
                        }
                    }
                }
        }
    }

2-1.ユーザー入力の値を保存する変数

@Stateをつけて変更可能な変数にする。@Stateをつけて宣言された変数は値の更新が見張られるようになり、値が変更したならば値を反映させるためにViewも更新される。
ユーザーの目線からも動向を追跡できる変数宣言といったところだろうか??
今回は言及しません。

@State var theText: String = ""

2-2NavigtionViewでデザイン調整

TextEditorの引数に$theTextを渡すことでユーザーの入力した文字が表示されます。
細かいレイアウトを引数の後に記述しています。
.toolbarで保存ボタンの詳細なデザインや挙動を記載しています。

NavigationView {
            TextEditor(text: $theText)
                .lineSpacing(10)
                .border(Color.gray)
                .padding([.leading,.bottom,.trailing])
                .navigationTitle("メモ")
                .toolbar{
                    //保存ボタン
                    ToolbarItem(placement: .navigationBarTrailing) {
                        Button {
                            UIApplication.shared.endEditing()//キーボードを下げる
                            saveText(theText, "sample.txt")//ユーザー定義関数のsaveText()テキストデータを保存する
                        } label: {
                            Text("保存")
                        }
                    }
                }

2-3保存ボタンの設定とテキスト保存のコード

保存ボタン設定しています。
ボタンを配置するツールバーはTexteddditorにモディファイアのtoolbarを追加して作ります。ツールバーにボタンを追加するには、toolbarのコンテンツとしてToolbarItem()の引数placementを指定するとツールバーの右端に寄った位置に表示されます。
先ほど記述したUIApllicationを呼び出し、endEditingを実行します。
saveText(theText,"sample.txt")ユーザー定義関数を呼び出し、メモを保存します。

                .toolbar{
                    //保存ボタン
                    ToolbarItem(placement: .navigationBarTrailing) {
                        Button {
                            UIApplication.shared.endEditing()//キーボードを下げる
                            saveText(theText, "sample.txt")//ユーザー定義関数のsaveText()テキストデータを保存する
                        } label: {
                            Text("保存")
                        }
                    }
                }

3.saveText()定義:テキストファイルへの保存を例外処理の中で行う。

.toolbarモディファイア内に記述したユーザー定義関数です。呼び出し側ではsaveText(ユーザーの入力値が入った変数,"ファイル名")でテキストデータを保存しています。
saveTextの第一引数でユーザーの入力値を受け取り、第二引数fileNameでファイル名を受け取ります。この例ではsample.txtという名前で受け取りますが、これだけでは保存する場所が決まりません。そこでユーザー定義関数docURL(filename)を実行して保存するURlを取得して、そのURLのパス(url.path)に書き込みます。なんらかの理由でnilになった時のためにguard let-elseを使います。

3-1.aveTextユーザー定義関数

 func saveText(_ textData:String, _ fileName:String){
        //docURLはユーザー定義変数
        guard let url = docURL(fileName) else {
            return
        }
        //ファイルパスへの保存
        do {
            let path = url.path
            try textData.write(toFile: path, atomically: true, encoding:  .utf8)
        } catch let error as NSError {
            print(error)
        }
    }


3-2.docURLユーザー定義関数

第一引数で(filename)を受け取ります。返り値はURL?で設定します。ファイルはiphoneデバイス内のどこでも保存できるというものではなく、目的に応じて決まっています。ユーザーが作って読み書きするファイルはDocumentsフォルダ内に保存します。DocumentsフォルダまでのURLはFileManager.defaultでfileManagerオブジェクトを作りfileManager.url()で取得する事ができます。DocumentsフォルダまでのURLを取得したならば、appendingPathComponent(fileName)で保存ファイル名を追加したURLを作り、そのURLをdocURL(fileName)の値として返します。
尚、ここでもエラーを返す場合があるのでdo-try-catchの例外処理の中で実行し、エラーになったならURLではなくnilを返します。nilを返す可能性があることから、戻り値の型はURL?のようにOputional型にします。

func docURL(_ fileName:String) -> URL? {
        let fileManager = FileManager.default
        do {
            //Documentsフォルダ
            let docsUrl = try fileManager.url(
                //Documentフォルダを指定する。
                for: .documentDirectory,
                in: .userDomainMask,
                appropriateFor: nil,
                create: false)
            let url = docsUrl.appendingPathComponent(fileName)
            return url
        }catch {
            return nil
        }
    }

4.ツールバーに読み込みボタンを追加する。

読み込みボタンも保存ボタンと同じようにToolbarItemとしてツールバーに追加します。placementで指定する配置する位置は保存ボタンと同じく.navigationBarTralingですが、後から指定したバーアイテムの方が右端に来るので、コード順に従って「読み込み」「保存」の順番で並びます。
読み込みボタンではloadText("sample.txt")を実行してsample.txtからテキストデータを読み込みます。読み込みに失敗した場合はnilが返されるので、if let-elseのオプショナルバインディングを使って読み込んだデータを変数dataに代入し、nilでなければdataをtheTextに代入してテキストエディタに表示します。
loadTextユーザー定義関数については後述します。

4-1読み込みボタン設置

読み込みボタンも保存ボタン同様,ToolbarItemとして.toolbar内に記述します。

.toolbar{
                    //読み込みボタン
                    ToolbarItem(placement: .navigationBarTrailing) {
                        Button {
                            if let data  = loadText("sample.txt") {
                                theText = data
                            }
                            } label: {
                                Text("読み込み")
                            }
                    }
                    //保存ボタン
                    ToolbarItem(placement: .navigationBarTrailing) {
                    ~~

4-2loadText()

読み込みボタンではユーザー定義関数のloadText("sample.txt")でsample.txtからテキストデータを読み込んでいます。saveText()同様、docURL(fileName)で読み込むファイルのURLを作り、String(contentsOf: url,encoding:.utf8)でurlからテキストデータを読み込んでいます。saaveText同様、do-try-catchの例外処理の中で実行します。
もしエラーになったならば関数の値としてnilを戻すので,戻り値の型はString?のようにoptional型にします。

 func loadText(_ fileName:String) -> String? {
        //URLを得られたらアンラップしてurlに代入
        guard let url = docURL(fileName) else {
            return nil
        }
        //urlからの読み込み
        do {
            let textData = try String(contentsOf: url, encoding: .utf8)
            return textData
        } catch {
            return nil
        }
    }


メモ帳保存機能、読み込み機能、書き込み機能に必要なものはURLを作るdocURL(),保存のsaveText(),読み込みのloadText(),キーボードを下げるextension UIApplication,テキストエディタ,保存ボタン,読み込みボタン,を作るstract ContentView,そして最後にプレビュー画面を作るstructContentView_previewsがあります。
以上。

2
1
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
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?