[swift] エフェクト編集画面の作成

EffectView.swift

struct EffectView: View {
    
    @Binding var isShowSheet: Bool
    
    let captureImage: UIImage
    
    @State var showImage: UIImage?
    @State var isShowActivity = false
    
    var body: some View {
        
        VStack {
            Spacer()
            
            if let unwrapShowImage = showImage {
                Image(uiImage: unwrapShowImage)
                    .resizable()
                    .aspectRatio(contentMode: .fit)
            }
            
            Spacer()
            
            Button(action: {}){
                Text("エフェクト")
                    .frame(maxWidth: .infinity)
                    .frame(height: 50)
                    .multilineTextAlignment(.center)
                    .background(Color.blue)
                    .foregroundColor(Color.white)
            }
            .padding()
            
            Button(action: {}){
                Text("閉じる")
                    .frame(maxWidth: .infinity)
                    .frame(height: 50)
                    .multilineTextAlignment(.center)
                    .background(Color.blue)
                    .foregroundColor(Color.white)
            }
            .padding()
        }
        .onAppear {
            showImage = captureImage
        }
    }
}

struct EffectView_Previews: PreviewProvider {
    static var previews: some View {
        EffectView(
            isShowSheet: Binding.constant(true),
            captureImage: UIImage(named: "preview_use")!)
    }
}

CoreImageに多数の編集機能がある

なるほど、カメラ機能の実装はiPhone端末がないとダメだな

[自然言語処理] Amazon Lexを使ってみる

Create botで OrderFlowersを選択する

Runtime roleはbasic Amazon Lex permissionsにします。
Idle session timeoutは5minutesにします。
そして Select languageをJapaneseにしてcreate

定義されたSlotsを回収しようとする
FlowerType
PickupDate
PickupTime

Confirmation promptにチェックがあると、すべてのスロット値が取得できた時点で確認

footerのbuildを押下します。

会話になってないね。精度はイマイチなのかな。
あまりamazon lexで開発しているって聞かないですね。

[自然言語処理] chatterbotで日本語のcorpusを使う

$ git clone https://github.com/gunthercox/chatterbot-corpus.git
$ cp -r chatterbot-corpus/chatterbot_corpus/data/japanese /home/vagrant/.local/lib/python3.8/site-packages/chatterbot_corpus/data

from chatterbot import ChatBot 
from chatterbot.trainers import ListTrainer
from chatterbot.trainers import ChatterBotCorpusTrainer

bot = ChatBot("チャットボット試作品")

trainer = ChatterBotCorpusTrainer(bot)
trainer.train("chatterbot.corpus.japanese")

while True:
	try:
		bot_input = bot.get_response(input())
		print(bot_input)
	except(KeyboardInterrupt, EOFError, SystemExit):
		break

出身はどちらですか
私はすべてのソフトウェアプログラムの出所です。遠く離れた銀河。
こんにちは
こちらこそ こんにちは
宜しくお願いします
a
ところで

今日は何の日ですか?
不明な変数名。
疲れたー
わかります。
プログラミングは面白い

何だって?
ピーターパークカー。
何?
それはあなたが一度だけ生きていることを意味します。あなたはそれをどこで聞いたのですか?
いいえ
顕著ではない。
その通り。
どの言語を使いたいですか?
日本語
0と1以外のすべての数字のように、多くのもの。
何のこと?
関心度
関心度
それはすべて中央銀行の行動に依存します。
経済やん
はい、そうです。
そうなのか
ロボットは嘘をついてはいけません
よくわからん

まあ
株式市場

うーむ、全く会話になってないな…

[自然言語処理] チャットボット基礎

ChatterBot:
The program selects the closest matching response by searching for the closest matching known statement that matches the input, it then returns the most likely response to that statement based on how frequently each response is issued by the people the bot communicates with.

chat.txt

こんにちは
こちらこそ こんにちは
今日もよろしくお願いします

chat2.txt

こんばんは
こちらこそ こんばんは
もう眠いですね
おやすみなさい

main.py

from chatterbot import ChatBot 
from chatterbot.trainers import ListTrainer

bot = ChatBot("チャットボット試作品")

training_ = open('chat.text','r').readlines()
training2_ = open('chat2.text','r').readlines()

trainer = ListTrainer(bot)

trainer.train(training_)
trainer.train(training2_)

while True:
	try:
		bot_input = bot.get_response(input())
		print(bot_input)
	except(KeyboardInterrupt, EOFError, SystemExit):
		break

$ pip3 install chatterbot
$ pip3 install chatterbot-corpus
$ python3 main.py
[nltk_data] Downloading package stopwords to
[nltk_data] /home/vagrant/nltk_data…
[nltk_data] Package stopwords is already up-to-date!
[nltk_data] Downloading package averaged_perceptron_tagger to
[nltk_data] /home/vagrant/nltk_data…
[nltk_data] Package averaged_perceptron_tagger is already up-to-
[nltk_data] date!
List Trainer: [####################] 100%
List Trainer: [####################] 100%
こんにちは
こちらこそ こんにちは
もう眠いですね
おやすみなさい
もう眠いですね
おやすみなさい
こちらこそ こんばんは
もう眠いですね
aaa
もう眠いですね
そうですか
こちらこそ こんばんは
おはよう
もう眠いですね
こんにちは
こちらこそ こんにちは

なんだこれ…

[swift] シェア画面を追加

ActivityView.swift
L Anyは任意の値を表すデータ型、どんな型でもOK

import SwiftUI

struct ActivityView: UIViewControllerRepresentable {
    
    let shareItems: [Any]
    
    func makeUIViewController(context: Context) ->
    UIActivityViewController {
        
    let controller = UIActivityViewController(
        activityItems: shareItems,
        applicationActivities: nil)
        
        return controller
    }
    
    func uupdateUIViewController(
        _ uiViewController: UIActivityViewController,
        context: UIViewControllerRepresentableContext<ActivityView>){
            
        }
}

ContentView.swift
 L 写真がないこともある変数をオプショナル変数という アンラップ処理をする

            Button(action: {
                if let _ = captureImage{
                    isShowActivity = true
                }
            }){
                Text("SNSに投稿する")
                    .frame(maxWidth: .infinity)
                    .frame(height: 50)
                    .multilineTextAlignment(.center)
                    .background(Color.blue)
                    .foregroundColor(Color.white)
            }
            .padding()
            .sheet(isPresented: $isShowActivity){
                ActivityView(shareItems: [captureImage!])
            }
        }

### フォトライブラリから写真を取り込めるようにする
PHPickerViewControllerはPhotoKitで提供されている機能
coordinatorを使用する

struct PHPickerView: UIViewControllerRepresentable {
    
    @Binding var isShowSheet: Bool
    @Binding var captureImage: UIImage?
    
    class Coordinator: NSObject,
                       PHPickerViewControllerDelegate {
        var parent: PHPickerView
        
        init(parent: PHPickerView){
            self.parent = parent
        }
        
        func picker(
            _ picker: PHPickerViewController,
            didFinishPicking results: [PHPickerResult]){
                
                if let result = results.first {
                    result.itemProvider.loadObject(ofClass: UIImage.self){
                        (image, error) in
                        
                        if let unwrapImage = image as? UIImage {
                            self.parent.captureImage = unwrapImage
                        } else {
                            print("使用できる写真がないです")
                        }
                    }
                } else {
                    print("選択された写真はないです")
                }
                parent.isShowSheet = false
            }
    }
}
    func makeCoordinator() -> Coordinator {
        Coordinator(parent: self)
    }
    
    func makeUIViewController(
        context:UIViewControllerRepresentableContext<PHPickerView>)
    -> PHPickerViewController {
        var configuration = PHPickerConfiguration()
        configuration.filter = .images
        configuration.selectionLimit = 1
        let picker = PHPickerViewController(configuration: configuration)
        picker.delegate = context.coordinator
        return picker
    }
    
    func updateUIViewController(
        _ uiViewController: PHPickerViewController,
        context: UIViewControllerRepresentableContext<PHPickerView>){
            
        }

### カメラとフォトライブラリーの選択画面

            .actionSheet(isPresented: $isShowAction){
                ActionSheet(title: Text("確認"),
                            message: Text("選択してください"),
                            buttons: [
                                .default(Text("カメラ"), action: {
                                    isPhotolibrary = false
                                    
                                    if UIImagePickerController.isSourceTypeAvailable(.camera){
                                        print("カメラは利用できます")
                                        isShowSheet = true
                                    } else {
                                        print("カメラは利用できません")
                                    }
                                }),
                                .default(Text("フォトライブラリー"), action: {
                                    isPhotolibrary = true
                                    isShowSheet = true
                                }),
                                .cancel()
                            ])
            }

なるほど、カメラを使えると中々面白い

[swift] カメラアプリを作ろう1

– カメラが起動し撮影できる
– SNSなどでシェアできる

カメラの起動はUIImagePickerControllerクラス、Coordinatorを使う
delegateメソッドを使って撮影後の写真を画面に表示できる

ContentView.swift, ImagePickerView.swift, ActivityView.swiftを作成する
UIKitはiOS開発に中核となるコントロール群

ImagePickerView.swiftを作成
L UIKitは自動的にimportされている
  L 写真と撮影画面を閉じるフラグ設定
L UIImageは画像を管理するクラス、 Coordinator機能を利用する

import SwiftUI

struct ImagePickerView: UIViewControllerRepresentable {
    @Binding var isShowSheet: Bool
    
    @Binding var captureImage: UIImage?
}

coordinator class追加

class Coordinator: NSObject,
                UINavigationControllerDelegate,
                       UIImagePickerControllerDelegate {
        
        let parent: ImagePickerView
        
        init(_ parent: ImagePickerView){
            self.parent = parent
        }
        
        func imagePickerController(
            _ picker: UIImagePickerController,
            didFinishPickingMediaWithInfo info:
            [UIImagePickerController.InfoKey : Any]){
                
            if let originalImage =
                info[UIImagePickerController.InfoKey.originalImage]
                as? UIImage {
                parent.captureImage = originalImage
                }
                parent.isShowSheet = false
            }
        
        func imagePickerControllerDidCancel(
            _ picker: UIImagePickerController) {
                parent.isShowSheet = false
            }
    }

UIImagePickerController.InfoKey.originalImageでカメラで撮影した写真が取得できる

### Coordinator classとUIViewControllerRepresentable

    func makeUIViewController (
        context: UIViewControllerRepresentableContext<ImagePickerView>) ->
    UIImagePickerController {
        
        let myImagePickerController = UIImagePickerController()
        myImagePickerController.sourceType = .camera
        myImagePickerController.delegate = context.coordinator
        return myImagePickerController
    }
    
    func updateUIViewController(
        _ uiViewController: UIImagePickerController,
        context: UIViewControllerRepresentableContext<ImagePickerView>){
            
        }

カメラを使用するときにはカメラの動きを指示するオプションを設定する
プロパティは sourceType, mediaType, cameraDevice, cameraFlashModeなどがある

delegateとは、あるクラスで行いたい処理の一部を他のクラスに任せたり、任せた処理を指定したクラスに通知する仕組み
protocolから処理を依頼されるクラスがある

### カメラの起動処理
カメラのプロパティを設定する行を追加
Privacy – Camera Usage Description : 写真を撮影するためにカメラを利用します。

プロパティリストはアプリの稼働に必要な設定情報を管理

### カメラ起動処理

struct ContentView: View {
    var body: some View {
        VStack {
            Spacer()
            Button(action: {
                if UIImagePickerController.isSourceTypeAvailable(.camera){
                    print("カメラは使用できます")
                } else {
                    print("カメラは使用できません")
                }
            }){
                Text("カメラを起動する")
                    .frame(maxWidth: .infinity)
                    .frame(height: 50)
                    .multilineTextAlignment(.center)
                    .background(Color.blue)
                    .foregroundColor(Color.white)
            }
        }
    }
}

### カメラを起動して撮影

@State var captureImage: UIImage? = nil
@State var isShowSheet = false

// 省略

            Spacer()
            if let unwrapCaptureImage = captureImage {
                
                Image(uiImage: unwrapCaptureImage)
                    .resizable()
                    .aspectRatio(contentMode: .fit)
            }
// 省略
            Button(action: {
                if UIImagePickerController.isSourceTypeAvailable(.camera){
                    print("カメラは使用できます")
                    isShowSheet = true
                } else {
                    print("カメラは使用できません")
                }
            }){
                Text("カメラを起動する")
                    .frame(maxWidth: .infinity)
                    .frame(height: 50)
                    .multilineTextAlignment(.center)
                    .background(Color.blue)
                    .foregroundColor(Color.white)
            }
// 省略
            .sheet(isPresented: $isShowSheet){
                ImagePickerView(
                    isShowSheet: $isShowSheet,
                    captureImage: $captureImage)
            }

カメラは覚えることが多いが、アプリで一番面白そうな分野ではある

[swift] タイマー処理と設定した秒数

@AppStorageはデータを永続化するUserDefaultsから値を読み込みする
UserDefaultはアプリで利用する値を保存する機能
L ここではtimer_valueというkeyにtimeValueの初期時10を導入している

    @State var timerHandler : Timer?
    @State var count = 0
    @AppStorage("timer_value") var timerValue = 10

1秒ごとに呼び出してcountを+1とし、残り0でタイマーを止める

    func countDownTimer() {
        count += 1
        
        if timerValue - count <= 0 {
            timerHandler?.invalidate()
        }
    }

タイマー開始

    func startTimer() {
        if let unwrapedTimerHandler = timerHandler {
            if unwrapedTimerHandler.isValid == true {
                return
            }
        }
        
        if timerValue - count <= 0 {
            count = 0
        }
        
        timerHandler = Timer.scheduledTimer(withTimeInterval: 1, repeats: true){
            _ in
            
            countDownTimer()
        }
    }

### タイマーの保存
SettingView.swift

@AppStorage("timer_value") var timerValue = 10

シミュレータで確認する

### アラートの表示

            .alert(isPresented: $showAlert){
                Alert(title: Text("終了"),
                      message: Text("タイマー終了時間です"),
                      dismissButton: .default(Text("OK")))
            }

すげえええええええええええ

[swift] タイマーアプリを作ろう

スタート、ストップボタンとカウントダウンの秒数を表示させるテキストを配置
スタートでカウントダウンを開始再開、ストップで一時停止
秒数設定で終了時間を設定できる
画面遷移はNavigationViewを利用、カウントダウン開始秒数をpickerを配置して設定 Backでタイマー画面に戻る

ContentView.swift
L NavigationViewは先頭画面であることを宣言
  L .toolbarはボタンを配置
  L navigationBarTrailingで右側に配置 navigationBarLeadingは右側、.bottomBarは下部

struct ContentView: View {
    var body: some View {
        NavigationView {
            VStack {
                Text("タイマー画面")
            }
            .toolbar {
                ToolbarItem(placement: .navigationBarTrailing){
                    NavigationLink(destination: SettingView()){
                        Text("秒数設定")
                    }
                }
            }
        }
        .navigationViewStyle(StackNavigationViewStyle())
    }
}

### タイマーの色を定義
Asset.xcassetsでcolorsetを追加、「Any Appearance」を選択
218, 78, 122にRGBを設定
Darkモードで208, 68, 112で設定

同様にストップの色も定義する

ZStack {
                Image("backgroundTimer")
                    .resizable()
                    .ignoresSafeArea()
                    .aspectRatio(contentMode: .fill)
                VStack(spacing: 30.0) {
                    Text("残り10秒")
                        .font(.largeTitle)
                    HStack {
                        Button(action: {}){
                            Text("スタート")
                                .font(.title)
                                .foregroundColor(Color.white)
                                .frame(width: 140, height: 140)
                                .background(Color("startColor"))
                                .clipShape(Circle())
                        }
                        Button(action: {}){
                            Text("ストップ")
                                .font(.title)
                                .foregroundColor(Color.white)
                                .frame(width: 140, height: 140)
                                .background(Color("stopColor"))
                                .clipShape(Circle())
                        }
                    }
                }
            }

### Pickerを配置
SettingView.swift
L pickerStyle(.wheel)でPickerホイール配置

struct SettingView: View {
    
    @State var timerValue = 10
    
    var body: some View {
        
        ZStack {
            Color("backgroundSetting")
                .ignoresSafeArea()
            
            VStack {
                
                Picker(selection: $timerValue){
                    Text("10")
                        .tag(10)
                    Text("20")
                        .tag(20)
                    Text("30")
                        .tag(30)
                    Text("40")
                        .tag(40)
                    Text("50")
                        .tag(50)
                    Text("60")
                        .tag(60)
                } label: {
                    Text("選択")
                }
                .pickerStyle(.wheel)
            }
        }
    }
}

うむ、覚えることが広いな

[swift] マップ検索アプリでMapKitとクロージャを学習2

ContentView.swift

struct ContentView: View {
    
    @State var inputText: String = ""
    @State var dispSearchKey: String = ""
    
    var body: some View {
        VStack {
            
            TextField("キーワード", text: $inputText, prompt: Text("キーワードを入力してください"))
                .onSubmit {
                    dispSearchKey = inputText
                }
                .padding()
            MapView(searchKey: dispSearchKey)
        }
    }
}

ContentView.swift

struct ContentView: View {
    
    @State var inputText: String = ""
    @State var dispSearchKey: String = ""
    @State var dispMapType: MKMapType = .standard
    
    var body: some View {
        VStack {
            
            TextField("キーワード", text: $inputText, prompt: Text("キーワードを入力してください"))
                .onSubmit {
                    dispSearchKey = inputText
                }
                .padding()
            
            ZStack(alignment: .bottomTrailing){
                
                MapView(searchKey: dispSearchKey, mapType: dispMapType)
                
                Button(action: {
                    if dispMapType == .standard {
                        dispMapType = .satellite
                    } else if dispMapType == .satellite {
                        dispMapType = .hybrid
                    } else if dispMapType == .hybrid {
                        dispMapType = .hybridFlyover
                    } else if dispMapType == .hybridFlyover {
                        dispMapType = .mutedStandard
                    } else {
                        dispMapType = .standard
                    }
                }) {
                    Image(systemName: "map")
                        .resizable()
                        .frame(width: 35.0, height: 35.0, alignment: .leading)
                }
                .padding(.trailing, 20.0)
                .padding(.bottom, 30.0)
            }
            
        }
    }
}

うーむ、これは凄いわ

[swift] マップ検索アプリでMapKitとクロージャを学習1

検索窓(TextField)、マップを表示するView(MapKit)を設置する
キーワードから緯度経度を検索 -> 緯度経度からピンの画面パーツ生成 -> ピンの画面パーツを地図画面に貼り付け
MapKitにはUIViewRepresentableを使用する
TextFieldでの文字入力と入力完了後(onCommit)の使い方を学ぶ
ContentView.swiftとMapView.swiftを作成する

SwiftUIのMapView.swiftを作成
アプリでMapを表示するにはMapKitをインポートする
MapKitは地図、衛星画像、ピン配置、住所から座標検索ができる

import SwiftUI
import MapKit

struct MapView: UIViewRepresentable {
    func makeUIView(context: Context) -> MKMapView {
        MKMapView()
    }
    
    func updateUIView(_ uiView: MKMapView, context: Context){
    }
}

struct MapView_Previews: PreviewProvider {
    static var previews: some View {
        MapView()
    }
}

UIViewRepresentableを記載すると、makeUIView(viewの作成)とupdateUIView(viewの変更、再描画)が必要になる
UIViewRepresentableは、MKMapViewを使うためのラッパー

### 検索キーワードの設定
MapView.swift

struct MapView: UIViewRepresentable {
    
    let searchKey: String
    
    func makeUIView(context: Context) -> MKMapView {
        MKMapView()
    }
    
    func updateUIView(_ uiView: MKMapView, context: Context){
        
        print(searchKey)
    }
}

struct MapView_Previews: PreviewProvider {
    static var previews: some View {
        MapView(searchKey: "東京タワー")
    }
}

ContentView.swift

struct ContentView: View {
    var body: some View {
        VStack {
            MapView(searchKey: "東京タワー")
        }
    }
}

-> シュミレーターを立ち上げると”東京タワー”と表示される

シミュレータでプログラムが実行される順番
 MyMapApp.swift -> ContentView.swift -> MapView.swift

### 検索キーワードから緯度経度を検索

    func updateUIView(_ uiView: MKMapView, context: Context){
        
        print(searchKey)
        
        let geocoder = CLGeocoder()
        geocoder.geocodeAddressString(
        searchKey ,
        completionHandler: { (placemarks, error) in
            
        if let unwrapPlacemarks = placemarks,
           let firstPlacemark = unwrapPlacemarks.first ,
           let location = firstPlacemark.location {
            
            let targetCoordinate = location.coordinate
            print(targetCoordinate)
            }
        })
    }

追加

            let pin = MKPointAnnotation()
            pin.coordinate = targetCoordinate
            pin.title = searchKey
            uiView.region = MKCoordinateRegion(
                center: targetCoordinate,
                latitudinalMeters: 500.0,
                longitudinalMeters: 500.0)

MKPointAnnotationはピンを置くための機能

地図の扱いは特殊ですね