見出し画像

【じっくりSw1ftUI65】実践編35〜第47章 SwiftUI Documentチュートリアル

さてと、前回概要だらけで眠くなる🥱

で、

SwiftUI Document Groupシーンの概要

については触れたので、今回は

SwiftUI Documentチュートリアル

をやってく🕺

毎度、オイラの学びなんざ要らないって人は

に全部載ってるから、そっちを見たらいいんじゃね?👀
ではでは早速、

じっくり第47章を読んでく👓

概要も今話した話を書いてるだけっぽいので〜〜〜

早速動かす

今回まずは、前回作ったプロジェクトファイル

コイツな

を起動

コイツ

でもって〜〜〜今回用のドキュメントタイプ確認

サンプルのレイアウトとは異なるけど、必要なところは同じだね。ここを〜〜〜

てな感じで変更〜〜〜

次はインポートタイプ

前回のデフォルト


今回用に変更

ここでポイント①:各項目の設定値

  • Description – Example Image

  • Identifier – com.example.fruitsImage

  • Conforms To – public.image

  • Extensions – png

ま、ここは設定すればいいだけっぽいね

Assetsに画像を保存しないといけないっぽいので〜〜〜

ま、画像がない人は、今までの記事で作ったプロジェクトからでもテケトーな画像を

てな感じでセットすればいいんじゃね?👀

で、前回デフォルトで出来てたコードを編集してく

まずは、

ここの部分を
//変更前
extension UTType {
    static var exampleText: UTType {
        UTType(importedAs: "com.example.plain-text")
    }
}
//変更後
extension UTType {
    static var fruitsImage: UTType {
        UTType(importedAs: "com.example.fruitsImage")
    }
}

てな感じ

お次は、

この青くなっている部分を〜〜〜
//変更前
    static var readableContentTypes: [UTType] { [.exampleText] }
//変更後
    static var readableContentTypes: [UTType] { [.fruitsImage] }

てな感じに変更すると、

んで次は〜〜〜イメージファイルに対応するように

他の箇所も変更

//変更前
struct E46DocumentsGroupApp2Document: FileDocument {
    var text: String

    init(text: String = "Hello, world!") {
        self.text = text
    }

    static var readableContentTypes: [UTType] { [.exampleText] }

    init(configuration: ReadConfiguration) throws {
        guard let data = configuration.file.regularFileContents,
              let string = String(data: data, encoding: .utf8)
        else {
            throw CocoaError(.fileReadCorruptFile)
        }
        text = string
    }

    func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
        let data = text.data(using: .utf8)!
        return .init(regularFileWithContents: data)
    }
}
//変更後(変更箇所がわかるようにコメントアウト)
struct E46DocumentsGroupApp2Document: FileDocument {
    var imageManage: UIImage = UIImage()

    init() {
        if let imageManage = UIImage(named: "cascadefalls"){
            self.imageManage = imageManage
        }
    }
    
    static var readableContentTypes: [UTType] { [.fruitsImage] }

    init(configuration: ReadConfiguration) throws {
        guard let fruitsData = configuration.file.regularFileContents,
              let decodeImage: UIImage = UIImage(data: fruitsData)
        else {
            throw CocoaError(.fileReadCorruptFile)
        }
        imageManage = decodeImage
    }

    func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
        let data = imageManage.pngData()!
        return .init(regularFileWithContents: data)
    }
}

もちろん、

    init() {
        if let imageManage = UIImage(named: "cascadefalls"){
            self.imageManage = imageManage
        }
    }

なんて画像は今回Assetsに取り込んでないので〜〜〜

struct E46DocumentsGroupApp2Document: FileDocument {
    var imageManage: UIImage = UIImage()

    init() {
        if let imageManage = UIImage(named: "りんごちゃん"){
            self.imageManage = imageManage
         }
    }
    
    static var readableContentTypes: [UTType] { [.fruitsImage] }

    init(configuration: ReadConfiguration) throws {
        guard let fruitsData = configuration.file.regularFileContents,
              let decodeImage: UIImage = UIImage(data: fruitsData)
        else {
            throw CocoaError(.fileReadCorruptFile)
        }
        imageManage = decodeImage
    }

    func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
        let data = imageManage.pngData()!
        return .init(regularFileWithContents: data)
    }
}

てな感じに変更〜〜〜

で、コンテントビューを変更してくと〜〜〜

//変更前
import SwiftUI

struct ContentView: View {
    @Binding var document: E46DocumentsGroupApp2Document

    var body: some View {
        TextEditor(text: $document.text)
            .navigationTitle("ドキュメントを編集")
    }
}

#Preview {
    ContentView(document: .constant(E46DocumentsGroupApp2Document()))
}
//変更後
import SwiftUI

struct ContentView: View {
    @Binding var document: E46DocumentsGroupApp2Document
    
    var body: some View {
        VStack{
            Image(uiImage: document.imageManage)
                .resizable()
                .aspectRatio(contentMode: .fit)
                .padding()
            Button(action: {

            }, label: {
                Text("イメージをフィルター")
            })
        }
            .navigationTitle("画像を編集")
    }
}

#Preview {
    ContentView(document: .constant(E46DocumentsGroupApp2Document()))
}

でプレビューを見ると

てな感じで動いたー🕺

なんかシミュレーターでここまでで一旦動かしてるので〜〜〜

イメージを追加後に表示されてんね🕺

でイメージフィルター機能を追加〜〜〜

まずは、イメージフィルター機能に必要なフレームワークをインポート

import CoreImage
import CoreImage.CIFilterBuiltins

コードを追記して〜〜〜

import SwiftUI
import CoreImage
import CoreImage.CIFilterBuiltins

struct ContentView: View {
    @Binding var document: E46DocumentsGroupApp2Document
    @State private var ciFilterFruits = CIFilter.sepiaTone()

    let context = CIContext()
    
    var body: some View {
        VStack{
            Image(uiImage: document.imageManage)
                .resizable()
                .aspectRatio(contentMode: .fit)
                .padding()
            Button(action: {
                filterImage()
            }, label: {
                Text("イメージをフィルター")
            })
        }
            .navigationTitle("画像を編集")
    }
    
    func filterImage(){
        ciFilterFruits.intensity = Float(1.0)
        let ciImageFruits = CIImage(image: document.imageManage)
        ciFilterFruits.setValue(ciImageFruits, forKey: kCIInputImageKey)
        guard let outPutImageFruits = ciFilterFruits.outputImage else{
            return
        }
        if let cgImageFruits = context.createCGImage(outPutImageFruits, from: outPutImageFruits.extent){
            document.imageManage = UIImage(cgImage: cgImageFruits)
        }
    }
}

#Preview {
    ContentView(document: .constant(E46DocumentsGroupApp2Document()))
}

で実行

でけたー🕺

本にちゃんとした実行後の画像がもうちょっと充実させてほしいね😛

今回のコードまとめ

◾️E46DocumentsGroupApp2App.swift

import SwiftUI

@main
struct E46DocumentsGroupApp2App: App {
    var body: some Scene {
        DocumentGroup(newDocument: E46DocumentsGroupApp2Document()) { file in
            ContentView(document: file.$document)
        }
    }
}

◾️E46DocumentsGroupApp2Document.swift

import SwiftUI
import UniformTypeIdentifiers

extension UTType {
    static var fruitsImage: UTType {
        UTType(importedAs: "com.example.fruitsImage")
    }
}

struct E46DocumentsGroupApp2Document: FileDocument {
    var imageManage: UIImage = UIImage()

    init() {
        if let imageManage = UIImage(named: "りんごちゃん"){
            self.imageManage = imageManage
         }
    }
    
    static var readableContentTypes: [UTType] { [.fruitsImage] }

    init(configuration: ReadConfiguration) throws {
        guard let fruitsData = configuration.file.regularFileContents,
              let decodeImage: UIImage = UIImage(data: fruitsData)
        else {
            throw CocoaError(.fileReadCorruptFile)
        }
        imageManage = decodeImage
    }

    func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
        let data = imageManage.pngData()!
        return .init(regularFileWithContents: data)
    }
}

◾️ContentView.swift

import SwiftUI
import CoreImage
import CoreImage.CIFilterBuiltins

struct ContentView: View {
    @Binding var document: E46DocumentsGroupApp2Document
    @State private var ciFilterFruits = CIFilter.sepiaTone()

    let context = CIContext()
    
    var body: some View {
        VStack{
            Image(uiImage: document.imageManage)
                .resizable()
                .aspectRatio(contentMode: .fit)
                .padding()
            Button(action: {
                filterImage()
            }, label: {
                Text("イメージをフィルター")
            })
        }
            .navigationTitle("画像を編集")
    }
    
    func filterImage(){
        ciFilterFruits.intensity = Float(1.0)
        let ciImageFruits = CIImage(image: document.imageManage)
        ciFilterFruits.setValue(ciImageFruits, forKey: kCIInputImageKey)
        guard let outPutImageFruits = ciFilterFruits.outputImage else{
            return
        }
        if let cgImageFruits = context.createCGImage(outPutImageFruits, from: outPutImageFruits.extent){
            document.imageManage = UIImage(cgImage: cgImageFruits)
        }
    }
}

#Preview {
    ContentView(document: .constant(E46DocumentsGroupApp2Document()))
}

ここでポイント②:今回は久しぶりに詰まったねえ🧐

でコードと本を見比べてみて、

メンバーがない💢エラーが出たから、色々動かして変更してたんだけど

要は、

エクステンションの変数名を変更してなかっただけだった👀💦🤣

なので皆さんも気をつけてね〜〜〜

👉メンバーがないエラーは探すのに意外と苦労する

本文としては以上🕺

Apple公式

さてと、次回は

いよいよデータベースとの連携を扱う

第48章:Core DataとSwiftUIの紹介

をやってく〜〜〜

前回からが、オイラの本当の学び直しでゆっくり慎重にやってまつ
👉正直、ビューの構築までは、アプリの画面を単純に作ってるだけ
=やり方を一回やれば大体の人は理解できる

んだけど、前回と今回でやった

スマホ本来の機能との連携=Document Group

や次回以降でやる

データベース機能との連携

なんかでどんどんややこしくなるし、ま、だから去年の10月に始めた当初にゆーてたとおり、このマガジン記事のメインな目的は、あくまでも

オイラの学び直し

ってゆーてたんだよね。最近は知らないけど、

SwiftDataやDocumentGroupなんかで実践例が充実した本がない

からね。

記事公開後、

いつもどおり、

でやった操作を〜〜〜
なんだけど、ちょっと今回は前回分と今回分のソースコードとか設定を組み込んでいきたいので〜〜〜
前回予告していたとおり、まずは、

バックアップを取っているコイツ

を開いて〜〜〜

てな感じで前回用と今回用のSwiftUIファイルを追加

のコードを46と47用にはめ込み〜〜〜

てな感じでInfo.plistに設定を追加して
じっくり46用
じっくり47用

で組み込みは出来たんだけど、、、

シーンファイルでてな感じで追記して

実行してみると、、、

Sceneの一番最初が46であれば、

いつものポータルが開かずに、いきなりドキュメントグループからになるし
47をシーンの頭にすると
てな感じで
47の画面しか開かない😛

それは嫌だし、いつも通りにしときたいので〜〜〜

Sceneの最後に持ってきて〜〜〜
てな
てな感じ

で確認ができれば、

💃これまでのこのアプリの目的としては十分🕺

今回は2章まとめてで画像も多くなるので一気に〜〜〜〜


以上。てな感じで結合完了。

一歩前へ:結合してみた感想としては、、、

①インポートなんかのIdentifierは識別さえできるユニークなキーワードを入れておけばOKで、Bundle Identifierと同じじゃなくてもいいみたい👀
👉Bundle Identifierと同一じゃないといけないなら、+で増やせたところで、拡張性がないでしょ

②Sceneで各Group(例:WindowGroupやDocumenteGroup)はトレードオフな関係みたいなので、ひとつのアプリで複数のグループを使う時には別の配慮がいりそう

サンプルコード

さらに今回はまとめ用にきちんと整形した💦

◾️SwiftUICatalogforiOS17App.swift

import SwiftUI
import SwiftData

@main
struct SwiftUICatalogforiOS17App: App {
    //じっくり第26章で追加
    @Environment(\.scenePhase) private var scenePhase
    
    var sharedModelContainer: ModelContainer = {
        let schema = Schema([
            Item.self,
        ])
        let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false)

        do {
            return try ModelContainer(for: schema, configurations: [modelConfiguration])
        } catch {
            fatalError("Could not create ModelContainer: \(error)")
        }
    }()

    var body: some Scene {
        WindowGroup {
            ContentView()
            //じっくり第27章で追記
                .environment(E27TimesData())
        }
        .modelContainer(sharedModelContainer)
        //じっくり第26章で追加
        .onChange(of: scenePhase){
            switch scenePhase {
            case .background:
                print("バックグラウンドで実行")
            case .inactive:
                print("インアクティブになった")
            case .active:
                print("アクティブになった")
            default:
                print("識別不能なシーンで実行")
            }
        }
        //じっくり46で追加(順番によるが、他が開けなくなる=トレードオフなので最後)
        DocumentGroup(newDocument: E46DocumentsGroupAppDocument()) { file in
            Essentials46StandardView(document: file.$document)
        }
        //じっくり47で追加(順番によるが、他が開けなくなる=トレードオフなので最後)
        DocumentGroup(newDocument: E47DocumentsGroupAppDocument()) { file in
            Essentials47StandardView(document: file.$document)
        }
    }
}

◾️EssentialsMenu.swift

//フレームワーク
import SwiftUI
import WebKit

//ビュー管理構造体
struct ListiOSApp17DevelopmentEssentials: Identifiable {
    var id: Int
    var title: String
    var view: ViewEnumiOSApp17DevelopmentEssentials
}
//遷移先の画面を格納する列挙型
enum ViewEnumiOSApp17DevelopmentEssentials {
    case Ch1
    //じっくり13で追加
    case Ch2
    //じっくり14で追加
    case Ch3
    //じっくり15で追加
    case Ch4
    //じっくり16で追加
    case Ch5
    //じっくり17で追加
    case Ch6
    //じっくり18で追加
    case Ch7
    //じっくり19で追加
    case Ch8
    //じっくり20、21で追加
    case Ch9
    //じっくり22、23で追加
    case Ch10
    //じっくり24で追加
    case Ch11
    //じっくり25で追加
    case Ch12
    //じっくり26で追加
    case Ch13
    //じっくり27,28で追加
    case Ch14
    //じっくり29で追加
    case Ch15
    //じっくり31で追加
    case Ch16
    //じっくり32で追加
    case Ch17
    //じっくり33で追加
    case Ch18
    //じっくり34で追加
    case Ch19
    //じっくり35で追加
    case Ch20
    //じっくり36で追加
    case Ch21
    //じっくり37で追加
    case Ch22
    //じっくり40で追加
    case Ch23
    //じっくり41で追加
    case Ch24
    //じっくり43で追加
    case Ch25
    //じっくり44で追加
    case Ch26
    //じっくり45で追加
    case Ch27
    //じっくり46で追加
    case Ch28
    //じっくり47で追加
    case Ch29
    //じっくり48で追加
    case Ch30
    //じっくり49で追加
    case Ch31
    //じっくり50で追加
    case Ch32
    //じっくり51で追加
    case Ch33
    //じっくり52で追加
    case Ch34
    //じっくり53で追加
    case Ch35
    //じっくり54で追加
    case Ch36
    //じっくり55で追加
    case Ch37
    //じっくり56で追加
    case Ch38
    //じっくり57で追加
    case Ch39
    //じっくり58で追加
    case Ch40
    //じっくり59で追加
    case Ch41
    //じっくり60で追加
    case Ch42
    //じっくり61で追加
    case Ch43
    //じっくり62で追加
    case Ch44
    //じっくり63で追加
    case Ch45
    //じっくり64で追加
    case Ch46
    //じっくり65で追加
    case Ch47
}
//各項目に表示する文字列
let dataiOSApp17DevelopmentEssentials: [ListiOSApp17DevelopmentEssentials] = [
    ListiOSApp17DevelopmentEssentials(id: 1, title: essentialsChapter1Title, view: .Ch1),
    //じっくり13で追加
    ListiOSApp17DevelopmentEssentials(id: 2, title: essentialsChapter2Title, view: .Ch2),
    //じっくり13で追加
    ListiOSApp17DevelopmentEssentials(id: 3, title: essentialsChapter3Title, view: .Ch3),
    //じっくり15で追加
    ListiOSApp17DevelopmentEssentials(id: 4, title: essentialsChapter4Title, view: .Ch4),
    //じっくり16で追加
    ListiOSApp17DevelopmentEssentials(id: 5, title: essentialsChapter5Title, view: .Ch5),
    //じっくり17で追加
    ListiOSApp17DevelopmentEssentials(id: 6, title: essentialsChapter6Title, view: .Ch6),
    //じっくり18で追加
    ListiOSApp17DevelopmentEssentials(id: 7, title: essentialsChapter7Title, view: .Ch7),
    //じっくり19で追加
    ListiOSApp17DevelopmentEssentials(id: 8, title: essentialsChapter8Title, view: .Ch8),
    //じっくり20、21で追加
    ListiOSApp17DevelopmentEssentials(id: 9, title: essentialsChapter9Title, view: .Ch9),
    //じっくり22、23で追加
    ListiOSApp17DevelopmentEssentials(id: 10, title: essentialsChapter10Title, view: .Ch10),
    //じっくり24で追加
    ListiOSApp17DevelopmentEssentials(id: 11, title: essentialsChapter11Title, view: .Ch11),
    //じっくり25で追加
    ListiOSApp17DevelopmentEssentials(id: 12, title: essentialsChapter12Title, view: .Ch12),
    //じっくり26で追加
    ListiOSApp17DevelopmentEssentials(id: 13, title: essentialsChapter13Title, view: .Ch13),
    //じっくり27,28で追加
    ListiOSApp17DevelopmentEssentials(id: 14, title: essentialsChapter14Title, view: .Ch14),
    //じっくり29で追加
    ListiOSApp17DevelopmentEssentials(id: 15, title: essentialsChapter15Title, view: .Ch15),
    //じっくり31で追加
    ListiOSApp17DevelopmentEssentials(id: 16, title: essentialsChapter16Title, view: .Ch16),
    //じっくり32で追加
    ListiOSApp17DevelopmentEssentials(id: 17, title: essentialsChapter17Title, view: .Ch17),
    //じっくり33で追加
    ListiOSApp17DevelopmentEssentials(id: 18, title: essentialsChapter18Title, view: .Ch18),
    //じっくり34で追加
    ListiOSApp17DevelopmentEssentials(id: 19, title: essentialsChapter19Title, view: .Ch19),
    //じっくり35で追加
    ListiOSApp17DevelopmentEssentials(id: 20, title: essentialsChapter20Title, view: .Ch20),
    //じっくり36で追加
    ListiOSApp17DevelopmentEssentials(id: 21, title: essentialsChapter21Title, view: .Ch21),
    //じっくり37で追加
    ListiOSApp17DevelopmentEssentials(id: 22, title: essentialsChapter22Title, view: .Ch22),
    //じっくり40で追加
    ListiOSApp17DevelopmentEssentials(id: 23, title: essentialsChapter23Title, view: .Ch23),
    //じっくり41で追加
    ListiOSApp17DevelopmentEssentials(id: 24, title: essentialsChapter24Title, view: .Ch24),
    //じっくり43で追加
    ListiOSApp17DevelopmentEssentials(id: 25, title: essentialsChapter25Title, view: .Ch25),
    //じっくり44で追加
    ListiOSApp17DevelopmentEssentials(id: 26, title: essentialsChapter26Title, view: .Ch26),
    //じっくり45で追加
    ListiOSApp17DevelopmentEssentials(id: 27, title: essentialsChapter27Title, view: .Ch27),
    //じっくり46で追加
    ListiOSApp17DevelopmentEssentials(id: 28, title: essentialsChapter28Title, view: .Ch28),
    //じっくり47で追加
    ListiOSApp17DevelopmentEssentials(id: 29, title: essentialsChapter29Title, view: .Ch29),
    //じっくり48で追加
    ListiOSApp17DevelopmentEssentials(id: 30, title: essentialsChapter30Title, view: .Ch30),
    //じっくり49で追加
    ListiOSApp17DevelopmentEssentials(id: 31, title: essentialsChapter31Title, view: .Ch31),
    //じっくり50で追加
    ListiOSApp17DevelopmentEssentials(id: 32, title: essentialsChapter32Title, view: .Ch32),
    //じっくり51で追加
    ListiOSApp17DevelopmentEssentials(id: 33, title: essentialsChapter33Title, view: .Ch33),
    //じっくり52で追加
    ListiOSApp17DevelopmentEssentials(id: 34, title: essentialsChapter34Title, view: .Ch34),
    //じっくり53で追加
    ListiOSApp17DevelopmentEssentials(id: 35, title: essentialsChapter35Title, view: .Ch35),
    //じっくり54で追加
    ListiOSApp17DevelopmentEssentials(id: 36, title: essentialsChapter36Title, view: .Ch36),
    //じっくり55で追加
    ListiOSApp17DevelopmentEssentials(id: 37, title: essentialsChapter37Title, view: .Ch37),
    //じっくり56で追加
    ListiOSApp17DevelopmentEssentials(id: 38, title: essentialsChapter38Title, view: .Ch38),
    //じっくり57で追加
    ListiOSApp17DevelopmentEssentials(id: 39, title: essentialsChapter39Title, view: .Ch39),
    //じっくり58で追加
    ListiOSApp17DevelopmentEssentials(id: 40, title: essentialsChapter40Title, view: .Ch40),
    //じっくり59で追加
    ListiOSApp17DevelopmentEssentials(id: 41, title: essentialsChapter41Title, view: .Ch41),
    //じっくり60で追加
    ListiOSApp17DevelopmentEssentials(id: 42, title: essentialsChapter42Title, view: .Ch42),
    //じっくり61で追加
    ListiOSApp17DevelopmentEssentials(id: 43, title: essentialsChapter43Title, view: .Ch43),
    //じっくり62で追加
    ListiOSApp17DevelopmentEssentials(id: 44, title: essentialsChapter44Title, view: .Ch44),
    //じっくり63で追加
    ListiOSApp17DevelopmentEssentials(id: 45, title: essentialsChapter45Title, view: .Ch45),
    //じっくり64で追加
    ListiOSApp17DevelopmentEssentials(id: 46, title: essentialsChapter46Title, view: .Ch46),
    //じっくり65で追加
    ListiOSApp17DevelopmentEssentials(id: 47, title: essentialsChapter47Title, view: .Ch47),
]

struct iOSApp17DevelopmentEssentials: View {
    var body: some View {
        VStack {
            Divider()
            List (dataiOSApp17DevelopmentEssentials) { data in
                self.containedViewiOSApp17DevelopmentEssentials(dataiOSApp17DevelopmentEssentials: data)
            }
            .edgesIgnoringSafeArea([.bottom])
        }
        .navigationTitle("iOS開発の章目次")
        .navigationBarTitleDisplayMode(.inline)
    }
    //タップ後に遷移先へ遷移させる関数
    func containedViewiOSApp17DevelopmentEssentials(dataiOSApp17DevelopmentEssentials: ListiOSApp17DevelopmentEssentials) -> AnyView {
        switch dataiOSApp17DevelopmentEssentials.view {
        case .Ch1:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh1()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり13で追加
        case .Ch2:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh2()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり13で追加
        case .Ch3:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh3()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり15で追加
        case .Ch4:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh4()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり16で追加
        case .Ch5:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh5()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり17で追加
        case .Ch6:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh6()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり18で追加
        case .Ch7:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh7()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり19で追加
        case .Ch8:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh8()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり20、21で追加
        case .Ch9:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh9()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり22、23で追加
        case .Ch10:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh10()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり24で追加
        case .Ch11:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh11()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり25で追加
        case .Ch12:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh12()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり26で追加
        case .Ch13:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh13()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり27,28で追加
        case .Ch14:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh14()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり29で追加
        case .Ch15:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh15()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり31で追加
        case .Ch16:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh16()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり32で追加
        case .Ch17:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh17()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり33で追加
        case .Ch18:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh18()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり34で追加
        case .Ch19:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh19()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり35で追加
        case .Ch20:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh20()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり36で追加
        case .Ch21:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh21()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり37で追加
        case .Ch22:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh22()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり40で追加
        case .Ch23:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh23()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり41で追加
        case .Ch24:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh24()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり43で追加
        case .Ch25:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh25()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり44で追加
        case .Ch26:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh26()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり45で追加
        case .Ch27:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh27()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり46で追加
        case .Ch28:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh28()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり47で追加
        case .Ch29:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh29()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり48で追加
        case .Ch30:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh30()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり49で追加
        case .Ch31:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh31()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり50で追加
        case .Ch32:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh32()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり51で追加
        case .Ch33:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh33()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり52で追加
        case .Ch34:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh34()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり53で追加
        case .Ch35:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh35()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり54で追加
        case .Ch36:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh36()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり55で追加
        case .Ch37:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh37()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり56で追加
        case .Ch38:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh38()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり57で追加
        case .Ch39:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh39()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり58で追加
        case .Ch40:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh40()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり59で追加
        case .Ch41:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh41()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり60で追加
        case .Ch42:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh42()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり61で追加
        case .Ch43:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh43()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり62で追加
        case .Ch44:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh44()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり63で追加
        case .Ch45:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh45()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり64で追加
        case .Ch46:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh46()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり65で追加
        case .Ch47:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh47()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
        }
    }
}

#Preview {
    iOSApp17DevelopmentEssentials()
}

◾️Essentials46.swift

import SwiftUI
import UniformTypeIdentifiers
import WebKit

//タイトル
let essentialsChapter46NavigationTitle = "第46章"
let essentialsChapter46Title = "第46章 SwiftUI DocumentGroup シーンの概要"
let essentialsChapter46SubTitle = "第46章 SwiftUI DocumentGroup シーンの概要"

//コード
let codeEssentials46 = """
import SwiftUI
import SwiftData

@main
struct SwiftUICatalogforiOS17App: App {
    var body: some Scene {
        //じっくり46で追加(順番によるが、他が開けなくなる=トレードオフなので最後)
        DocumentGroup(newDocument: E46DocumentsGroupAppDocument()) { file in
            Essentials46StandardView(document: file.$document)
        }
    }
}
------------------------------------
import SwiftUI
import UniformTypeIdentifiers

extension UTType {
    static var exampleText: UTType {
        UTType(importedAs: "com.example.plain-text")
    }
}

struct E46DocumentsGroupAppDocument: FileDocument {
    var text: String

    init(text: String = "Hello, world!") {
        self.text = text
    }

    static var readableContentTypes: [UTType] { [.exampleText] }

    init(configuration: ReadConfiguration) throws {
        guard let data = configuration.file.regularFileContents,
              let string = String(data: data, encoding: .utf8)
        else {
            throw CocoaError(.fileReadCorruptFile)
        }
        text = string
    }

    func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
        let data = text.data(using: .utf8)!
        return .init(regularFileWithContents: data)
    }
}
------------------------------------
struct Essentials46ContentsView: View {
    var body: some View {
        Essentials46StandardView(document: .constant(E46DocumentsGroupAppDocument()))
    }
}

struct Essentials46StandardView: View {
    @Binding var document: E46DocumentsGroupAppDocument

    var body: some View {
        TextEditor(text: $document.text)
            .navigationTitle("ドキュメントを編集")
    }
}

#Preview {
    Essentials46ContentsView()
}
"""

//ポイント
let pointEssentials46 = """
今回も特になし
"""

//URL
let urlEssentials46 = "https://note.com/m_kakudo/n/n91321257dcf0"

//ビュー管理構造体
struct ListiOSApp17DevelopmentEssentialsCh46: Identifiable {
    var id: Int
    var title: String
    var view: ViewEnumiOSApp17DevelopmentEssentialsCh46
}
//遷移先の画面を格納する列挙型
enum ViewEnumiOSApp17DevelopmentEssentialsCh46{
    case Sec1
}
//各項目に表示するリスト項目
let dataiOSApp17DevelopmentEssentialsCh46: [ListiOSApp17DevelopmentEssentialsCh46] = [
    ListiOSApp17DevelopmentEssentialsCh46(id: 1, title: essentialsChapter46SubTitle, view: .Sec1),
]
struct iOSApp17DevelopmentEssentialsCh46: View {
    var body: some View {
        VStack {
            Divider()
            List (dataiOSApp17DevelopmentEssentialsCh46) { data in
                self.containedViewiOSApp17DevelopmentEssentialsCh46(dataiOSApp17DevelopmentEssentialsCh46: data)
            }
            .edgesIgnoringSafeArea([.bottom])
        }
        .navigationTitle(essentialsChapter46NavigationTitle)
        .navigationBarTitleDisplayMode(.inline)
    }
    //タップ後に遷移先へ遷移させる関数
    func containedViewiOSApp17DevelopmentEssentialsCh46(dataiOSApp17DevelopmentEssentialsCh46: ListiOSApp17DevelopmentEssentialsCh46) -> AnyView {
        switch dataiOSApp17DevelopmentEssentialsCh46.view {
        case .Sec1:
            return AnyView(NavigationLink (destination: Essentials46()) {
                Text(dataiOSApp17DevelopmentEssentialsCh46.title)
            })
        }
    }
}
#Preview {
    iOSApp17DevelopmentEssentialsCh46()
}

struct Essentials46: View {
    var body: some View {
        VStack{
            TabView {
                Essentials46ContentsView()
                    .tabItem {
                        Image(systemName: contentsImageTab)
                        Text(contentsTextTab)
                    }
                Essentials46Code()
                    .tabItem {
                        Image(systemName: codeImageTab)
                        Text(codeTextTab)
                    }
                Essentials46Points()
                    .tabItem {
                        Image(systemName: pointImageTab)
                        Text(pointTextTab)
                    }
                Essentials46WEB()
                    .tabItem {
                        Image(systemName: webImageTab)
                        Text(webTextTab)
                    }
            }
        }
    }
}
#Preview {
    Essentials46()
}

struct Essentials46Code: View {
    var body: some View {
        ScrollView{
            Text(codeEssentials46)
        }
    }
}
#Preview {
    Essentials46Code()
}
struct Essentials46Points: View {
    var body: some View {
        ScrollView{
            Text(pointEssentials46)
        }
    }
}
#Preview {
    Essentials46Points()
}
struct Essentials46WebView: UIViewRepresentable {
    let searchURL: URL
    func makeUIView(context: Context) -> WKWebView {
        let view = WKWebView()
        let request = URLRequest(url: searchURL)
        view.load(request)
        return view
    }
    func updateUIView(_ uiView: WKWebView, context: Context) {
        
    }
}
struct Essentials46WEB: View {
    private var url:URL = URL(string: urlEssentials46)!
    var body: some View {Essentials46WebView(searchURL: url)
    }
}
#Preview {
    Essentials46WEB()
}

extension UTType {
    static var exampleText: UTType {
        UTType(importedAs: "com.example.plain-text")
    }
}

struct E46DocumentsGroupAppDocument: FileDocument {
    var text: String

    init(text: String = "Hello, world!") {
        self.text = text
    }

    static var readableContentTypes: [UTType] { [.exampleText] }

    init(configuration: ReadConfiguration) throws {
        guard let data = configuration.file.regularFileContents,
              let string = String(data: data, encoding: .utf8)
        else {
            throw CocoaError(.fileReadCorruptFile)
        }
        text = string
    }

    func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
        let data = text.data(using: .utf8)!
        return .init(regularFileWithContents: data)
    }
}

struct Essentials46ContentsView: View {
    var body: some View {
        Essentials46StandardView(document: .constant(E46DocumentsGroupAppDocument()))
    }
}

struct Essentials46StandardView: View {
    @Binding var document: E46DocumentsGroupAppDocument

    var body: some View {
        TextEditor(text: $document.text)
            .navigationTitle("ドキュメントを編集")
    }
}

#Preview {
    Essentials46ContentsView()
}

◾️Essentials47.swift

import SwiftUI
import UniformTypeIdentifiers
import CoreImage
import CoreImage.CIFilterBuiltins
import WebKit

//タイトル
let essentialsChapter47NavigationTitle = "第47章"
let essentialsChapter47Title = "第47章 SwiftUI Documentチュートリアル"
let essentialsChapter47SubTitle = "第47章 SwiftUI Documentチュートリアル"

//コード
let codeEssentials47 = """
import SwiftUI
import SwiftData

@main
struct SwiftUICatalogforiOS17App: App {
    var body: some Scene {
        //じっくり47で追加(順番によるが、他が開けなくなる=トレードオフなので最後)
        DocumentGroup(newDocument: E47DocumentsGroupAppDocument()) { file in
            Essentials47StandardView(document: file.$document)
        }
    }
}
------------------------------------
import SwiftUI
import UniformTypeIdentifiers

extension UTType {
    static var fruitsImage: UTType {
        UTType(importedAs: "com.example.fruitsImage")
    }
}

struct E47DocumentsGroupAppDocument: FileDocument {
    var imageManage: UIImage = UIImage()

    init() {
        if let imageManage = UIImage(named: "りんごちゃん"){
            self.imageManage = imageManage
         }
    }
    
    static var readableContentTypes: [UTType] { [.fruitsImage] }

    init(configuration: ReadConfiguration) throws {
        guard let fruitsData = configuration.file.regularFileContents,
              let decodeImage: UIImage = UIImage(data: fruitsData)
        else {
            throw CocoaError(.fileReadCorruptFile)
        }
        imageManage = decodeImage
    }

    func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
        let data = imageManage.pngData()!
        return .init(regularFileWithContents: data)
    }
}
------------------------------------
import SwiftUI
import UniformTypeIdentifiers
import CoreImage
import CoreImage.CIFilterBuiltins

struct Essentials47ContentsView: View {
    var body: some View {
        Essentials47StandardView(document: .constant(E47DocumentsGroupAppDocument()))
    }
}

struct Essentials47StandardView: View {
    @Binding var document: E47DocumentsGroupAppDocument
    @State private var ciFilterFruits = CIFilter.sepiaTone()

    let context = CIContext()
    
    var body: some View {
        VStack{
            Image(uiImage: document.imageManage)
                .resizable()
                .aspectRatio(contentMode: .fit)
                .padding()
            Button(action: {
                filterImage()
            }, label: {
                Text("イメージをフィルター")
            })
        }
            .navigationTitle("画像を編集")
    }
    
    func filterImage(){
        ciFilterFruits.intensity = Float(1.0)
        let ciImageFruits = CIImage(image: document.imageManage)
        ciFilterFruits.setValue(ciImageFruits, forKey: kCIInputImageKey)
        guard let outPutImageFruits = ciFilterFruits.outputImage else{
            return
        }
        if let cgImageFruits = context.createCGImage(outPutImageFruits, from: outPutImageFruits.extent){
            document.imageManage = UIImage(cgImage: cgImageFruits)
        }
    }
}

#Preview {
    Essentials47ContentsView()
}

"""

//ポイント
let pointEssentials47 = """
◾️各項目の設定値
◆Description – Example Image
◆Identifier – com.example.fruitsImage
◆Conforms To – public.image
◆Extensions – png
ま、ここは設定すればいいだけっぽいね
"""

//URL
let urlEssentials47 = "https://note.com/m_kakudo/n/n91321257dcf0"

//ビュー管理構造体
struct ListiOSApp17DevelopmentEssentialsCh47: Identifiable {
    var id: Int
    var title: String
    var view: ViewEnumiOSApp17DevelopmentEssentialsCh47
}
//遷移先の画面を格納する列挙型
enum ViewEnumiOSApp17DevelopmentEssentialsCh47{
    case Sec1
}
//各項目に表示するリスト項目
let dataiOSApp17DevelopmentEssentialsCh47: [ListiOSApp17DevelopmentEssentialsCh47] = [
    ListiOSApp17DevelopmentEssentialsCh47(id: 1, title: essentialsChapter47SubTitle, view: .Sec1),
]
struct iOSApp17DevelopmentEssentialsCh47: View {
    var body: some View {
        VStack {
            Divider()
            List (dataiOSApp17DevelopmentEssentialsCh47) { data in
                self.containedViewiOSApp17DevelopmentEssentialsCh47(dataiOSApp17DevelopmentEssentialsCh47: data)
            }
            .edgesIgnoringSafeArea([.bottom])
        }
        .navigationTitle(essentialsChapter47NavigationTitle)
        .navigationBarTitleDisplayMode(.inline)
    }
    //タップ後に遷移先へ遷移させる関数
    func containedViewiOSApp17DevelopmentEssentialsCh47(dataiOSApp17DevelopmentEssentialsCh47: ListiOSApp17DevelopmentEssentialsCh47) -> AnyView {
        switch dataiOSApp17DevelopmentEssentialsCh47.view {
        case .Sec1:
            return AnyView(NavigationLink (destination: Essentials47()) {
                Text(dataiOSApp17DevelopmentEssentialsCh47.title)
            })
        }
    }
}
#Preview {
    iOSApp17DevelopmentEssentialsCh47()
}

struct Essentials47: View {
    var body: some View {
        VStack{
            TabView {
                Essentials47ContentsView()
                    .tabItem {
                        Image(systemName: contentsImageTab)
                        Text(contentsTextTab)
                    }
                Essentials47Code()
                    .tabItem {
                        Image(systemName: codeImageTab)
                        Text(codeTextTab)
                    }
                Essentials47Points()
                    .tabItem {
                        Image(systemName: pointImageTab)
                        Text(pointTextTab)
                    }
                Essentials47WEB()
                    .tabItem {
                        Image(systemName: webImageTab)
                        Text(webTextTab)
                    }
            }
        }
    }
}
#Preview {
    Essentials47()
}

struct Essentials47Code: View {
    var body: some View {
        ScrollView{
            Text(codeEssentials47)
        }
    }
}
#Preview {
    Essentials47Code()
}
struct Essentials47Points: View {
    var body: some View {
        ScrollView{
            Text(pointEssentials47)
        }
    }
}
#Preview {
    Essentials47Points()
}
struct Essentials47WebView: UIViewRepresentable {
    let searchURL: URL
    func makeUIView(context: Context) -> WKWebView {
        let view = WKWebView()
        let request = URLRequest(url: searchURL)
        view.load(request)
        return view
    }
    func updateUIView(_ uiView: WKWebView, context: Context) {
        
    }
}
struct Essentials47WEB: View {
    private var url:URL = URL(string: urlEssentials47)!
    var body: some View {Essentials47WebView(searchURL: url)
    }
}
#Preview {
    Essentials47WEB()
}

extension UTType {
    static var fruitsImage: UTType {
        UTType(importedAs: "com.example.fruitsImage")
    }
}

struct E47DocumentsGroupAppDocument: FileDocument {
    var imageManage: UIImage = UIImage()

    init() {
        if let imageManage = UIImage(named: "りんごちゃん"){
            self.imageManage = imageManage
         }
    }
    
    static var readableContentTypes: [UTType] { [.fruitsImage] }

    init(configuration: ReadConfiguration) throws {
        guard let fruitsData = configuration.file.regularFileContents,
              let decodeImage: UIImage = UIImage(data: fruitsData)
        else {
            throw CocoaError(.fileReadCorruptFile)
        }
        imageManage = decodeImage
    }

    func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
        let data = imageManage.pngData()!
        return .init(regularFileWithContents: data)
    }
}


struct Essentials47ContentsView: View {
    var body: some View {
        Essentials47StandardView(document: .constant(E47DocumentsGroupAppDocument()))
    }
}

struct Essentials47StandardView: View {
    @Binding var document: E47DocumentsGroupAppDocument
    @State private var ciFilterFruits = CIFilter.sepiaTone()

    let context = CIContext()
    
    var body: some View {
        VStack{
            Image(uiImage: document.imageManage)
                .resizable()
                .aspectRatio(contentMode: .fit)
                .padding()
            Button(action: {
                filterImage()
            }, label: {
                Text("イメージをフィルター")
            })
        }
            .navigationTitle("画像を編集")
    }
    
    func filterImage(){
        ciFilterFruits.intensity = Float(1.0)
        let ciImageFruits = CIImage(image: document.imageManage)
        ciFilterFruits.setValue(ciImageFruits, forKey: kCIInputImageKey)
        guard let outPutImageFruits = ciFilterFruits.outputImage else{
            return
        }
        if let cgImageFruits = context.createCGImage(outPutImageFruits, from: outPutImageFruits.extent){
            document.imageManage = UIImage(cgImage: cgImageFruits)
        }
    }
}

#Preview {
    Essentials47ContentsView()
}

以上。

さてと、今日明日は、

今日は、新聞読んで、ジム行って、
明日は、月1の大濠公園のゴミ拾いやっでね
アナログな人に戻ります〜〜〜🕺

💃皆さんも良き週末を🕺

いいなと思ったら応援しよう!