[Swift] 12 自製ヘルスケアapp / UIKit

コードだけでGUIを作成するため、SwiftUIでの開発を中断しUIKitに乗り換えました。

JavaやC++ではフレームワークにとらわれずコードだけで好きなようにGUIアプリを作成していたのが、SwiftUIですっかり調子を狂わされてしまいました。

ようやく自分の土俵で取り組めそうですが、Javaのように公式ドキュメントがしっかりしているわけではないので前途多難は続くでしょう。

import UIKit

extension UIColor {
    static let blackRGB = UIColor(red: 0/255, green: 0/255, blue: 0/255, alpha: 1)
}

class ViewController: UIViewController {
    var label = UILabel()
    var button_HR = UIButton()

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = UIColor.white
        
        label.text = "Health Manager"
        label.textAlignment = .center
        label.textColor = UIColor.blackRGB
        label.font = UIFont.systemFont(ofSize: 40)
        view.addSubview(label)
        
        button_HR.setTitle("心拍数", for: .normal)
        button_HR.setTitleColor(UIColor.red, for: .normal)
        view.addSubview(button_HR)
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        
        let width = Float(UIScreen.main.bounds.size.width)
        
        label.sizeToFit()
        let widthGap = (width - Float(label.frame.width)) / 2
        label.frame = CGRect.init(x: CGFloat(widthGap),
            y: 200,
            width: label.frame.width,
            height: label.frame.height)
        
        button_HR.sizeToFit()
        let widthGap_btn = (width - Float(button_HR.frame.width)) / 2
        button_HR.frame = CGRect.init(x: CGFloat(widthGap_btn),
            y: 300,
            width: button_HR.frame.width,
            height: button_HR.frame.height)

    }
}

[Swift] 11 自製ヘルスケアapp / 列挙体による画面遷移

NavigationLinkを使わずに列挙体で画面遷移するようにコーディングしていますが、遷移した際に元の画面が消えずに残ってしまいます。

ところで、XCodeのインデントに縦線が入ると見やすいのですが拡張機能でもできないようです。やはりVSCodeの方が圧倒的に使いやすいです。エディタですから当然ですが。

遷移前
不完全遷移
struct ContentView: View {
    enum ShowView {
        case Home
        case HR_Graph
        case HR_List
        case HRV_Graph
        case HRV_List
    }

    @State var displayMode = ShowView.Home
    @State private var selectedDate_from = Date()
    @State private var selectedDate_to = Date()
    @State private var selection = 1
         
    var body: some View {
        if displayMode == ShowView.HR_Graph {
            ContentView_HR_Graph()
        } else if displayMode == ShowView.HR_List {
            ContentView_HR_List()
        } else if displayMode == ShowView.HRV_Graph {
            ContentView_HRV_Graph()
        } else if displayMode == ShowView.HRV_List {
            ContentView_HRV_List()
        }

[Swift] 10 自製ヘルスケアapp / DatePickerからの日付取得

DatePickerから日付を取得しつつ画面遷移するところでうまくいかなくなったので、段階を踏んで進めていくことにしました。

まずは日付の取得です。画面に取得日を仮表示させました。

次は画面遷移になります。

このコードはGitでtest branchに分岐させており、完成したらmainと合体させる予定です。Gitの練習も兼ねています。

C++ – Java – Pythonという進化の系譜とは異なる言語なので違和感が強いままですが、先を急がずだらだら進めていきます。

import SwiftUI
import CoreData

struct ContentView: View {
    @State private var selectedDate_from = Date()
    @State private var selectedDate_to = Date()
    @State private var selection = 1
    @State private var shouldShowSecondView_HR: Bool = false
    @State private var shouldShowSecondView_HRV: Bool = false
    
    var body: some View {
        NavigationView {
            VStack {
                Text("Health Manager Test")
                    .foregroundColor(Color(red:70/255,green:14/255,blue:68/255))
                    .font(.system(size: 32))
                    .padding()
                
                DatePicker("From", selection: $selectedDate_from, displayedComponents: .date)
                    .frame(width: 180, height: 50)
                    .scaleEffect(x: 1.5, y: 1.5)
                    .font(.system(size: 20))

                
                DatePicker("To", selection: $selectedDate_to, displayedComponents: .date)
                    .frame(width: 180, height: 50)
                    .scaleEffect(x: 1.5, y: 1.5)
                    .font(.system(size: 20))
                    .padding()
                
                Menu {
                    Picker(selection: $selection, label: Text("")) {
                        Text("グラフ").tag(1)
                        Text("リスト").tag(2)
                    }
                } label: {
                    Text("データタイプ")
                        .font(.system(size: 20))
                }
                .padding()
                
                Text("Date Picker From \(selectedDate_from)")
                    .font(.system(size: 20))
                
                Text("Date Picker To \(selectedDate_to)")
                    .font(.system(size: 20))
                
                Text("データタイプ \(selection)")
                    .font(.system(size: 20))
 
                Button {
                    shouldShowSecondView_HR = true
                } label: {
                    Text("心拍数")
                        .foregroundColor(Color.white)
                }
                    .frame(width: 200, height: 100)
                    .background(Color(red:61/255,green:110/255,blue:218/255))
                    .font(.system(size: 24))
                    .cornerRadius(24)
                    .padding()

                Button {
                    shouldShowSecondView_HRV = true
                } label: {
                    Text("心拍変動")
                        .foregroundColor(Color.white)
                }
                    .frame(width: 200, height: 100)
                    .background(Color(red:61/255,green:110/255,blue:218/255))
                    .font(.system(size: 24))
                    .cornerRadius(24)
                
                Spacer()
                
            }
        }
    }
}

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

[C++] 50 FLTK : findコマンド生成アプリ/ 濁音かなの判定

[M1 Mac, Monterey 12.3, FLTK 1.3.8]

macOSではfindコマンドの検索語に濁音・半濁音のかなを使えないため、これらを判定する関数を実装しました。イテレータを使えるケースですが、今回は不使用です。

正規表現が使えないようなので地道に配列を作成しました。検出するとダイアログが表示されます。

std::vector<string> dakuon = {"が","ぎ","ぐ","げ","ご","ざ","じ","ず","ぜ","ぞ","だ","ぢ","づ","で","ど","ば","び","ぶ","べ","ぼ" \
                                "ガ","ギ","グ","ゲ","ゴ","ザ","ジ","ズ","ゼ","ゾ","ダ","ヂ","ヅ","デ","ド","バ","ビ","ブ","ベ","ボ" \
                                "ぱ","ぴ","ぷ","ぺ","ぽ","パ","ピ","プ","ペ","ポ"};

bool dakuon_detection(const char* name_char) {
    string name_str = string(name_char);

    for (size_t i = 0; i < dakuon.size(); ++i) {
        if (name_str.find(dakuon[i]) != std::string::npos) {
            const char* msg = "検索不可\n濁音・半濁音の平仮名・片仮名が\n含まれています";
            dlg = new modalDialog(400, 200, "Attention", msg);
            dlg->hotspot(window);
            int x = dlg->x_root();
            int y = dlg->y_root();
            dlg->resize(x+20,y+120,250,150);
            dlg->set_modal();
            dlg->show();

            cout << msg << endl;

            return true;
        }
    }
    return false;
}

bool judge = dakuon_detection(name_char);

if (judge){
    <濁音かな検出時の処理>
}else{
    <濁音かな非検出時の処理>
}

[Swift] 09 自製ヘルスケアapp / プルダウンによる条件分岐

プルダウンメニューでデータタイプをグラフ、リストから選択し、ボタンを押すと測定項目のデータが表示されるようにしました。普通のif文です。

次は心拍変動のデータ取得、指定期間の反映、グラフ表示などです。

import SwiftUI
import CoreData

struct ContentView: View {
    @State private var selectiedDate_from = Date()
    @State private var selectiedDate_to = Date()
    @State private var selection = 1
    @State private var shouldShowSecondView_HR: Bool = false
    @State private var shouldShowSecondView_HRV: Bool = false
    
    var body: some View {
        NavigationView {
            VStack {
                Text("Health Manager")
                    .foregroundColor(Color(red:70/255,green:14/255,blue:68/255))
                    .font(.system(size: 40))
                    .padding()
                
                DatePicker("From", selection: $selectiedDate_from, displayedComponents: .date)
                    .frame(width: 180, height: 50)
                    .scaleEffect(x: 1.5, y: 1.5)
                    .font(.system(size: 20))

                
                DatePicker("To", selection: $selectiedDate_to, displayedComponents: .date)
                    .frame(width: 180, height: 50)
                    .scaleEffect(x: 1.5, y: 1.5)
                    .font(.system(size: 20))
                    .padding()
                
                Menu {
                    Picker(selection: $selection, label: Text("")) {
                        Text("グラフ").tag(1)
                        Text("リスト").tag(2)
                    }
                } label: {
                    Text("データタイプ")
                        .font(.system(size: 20))
                }
                .padding()
                
                if selection == 1 {
                    NavigationLink(destination: ContentView_HR_Graph(), isActive: $shouldShowSecondView_HR) {}
                    NavigationLink(destination: ContentView_HRV_Graph(), isActive: $shouldShowSecondView_HRV) {}
                } else {
                    NavigationLink(destination: ContentView_HR_List(), isActive: $shouldShowSecondView_HR) {}
                    NavigationLink(destination: ContentView_HRV_List(), isActive: $shouldShowSecondView_HRV) {}
                }
                

                Button {
                    shouldShowSecondView_HR = true
                } label: {
                    Text("心拍数")
                        .foregroundColor(Color.white)
                }
                    .frame(width: 200, height: 100)
                    .background(Color(red:61/255,green:110/255,blue:218/255))
                    .font(.system(size: 24))
                    .cornerRadius(24)
                    .padding()

                Button {
                    shouldShowSecondView_HRV = true
                } label: {
                    Text("心拍変動")
                        .foregroundColor(Color.white)
                }
                    .frame(width: 200, height: 100)
                    .background(Color(red:61/255,green:110/255,blue:218/255))
                    .font(.system(size: 24))
                    .cornerRadius(24)
                
                Spacer()
                
            }
        }
    }
}

[Swift] 08 自製ヘルスケアapp / VSCodeの併用

VSCodeにSwift拡張機能を入れてコーディング、XCodeではBuild & RunとGit管理他に役割分担すると負担がかなり軽減しました。IDE嫌いの私でも快適にiOSアプリ開発できそうです。XCodeは想像をはるかに超えて難物でした。

Appleネイティブ言語に関するネット記事を漁っていくうちにObjective-CやUIkitに興味が出てきましたが、ここは我慢してSwiftで基礎力を身につけることに専念します。

XCodeやSwiftに対する不満をぶちまけたら改善策が色々浮かんできました。プログラマにとって不平不満は推進力になります。

[Swift] 07 自製ヘルスケアapp / Simulatorと実機の乖離

[Mac mini M1, MacOS Monterey 12.3.1, XCode 13.3.1, iPhone11]

前回の続きです。

iPhone11実機ではDatePickerの日付Styleがshort(1/1/22)ではなくmedium(Jan 1, 2022)で表示されてしまいます。Simulatorではshortになります。

SwiftUIではどうやってもshortにできませんでした。UIkitならできるのでしょうか?

一事が万事。SwiftUIは細かいところでいろいろ不備がありそうで創作意欲が萎えています。早めにUIkitにシフトする方が快適に開発できそうな気がしてきました。

あるいはSwiftUIはそういうものと割り切り、仕様に合わせて適当にコーディングするか迷います。今のところ愛着はゼロですからドライに使いこなしましょうか。

[Swift] 06 自製ヘルスケアapp / Picker

[Mac mini M1, MacOS Monterey 12.3.1, XCode 13.3.1]

データタイプを選択させるためプルダウンメニューを配置しました。

フォントサイズの設定に手間取りました。Swiftの文法で引っかかったのは初めてです。Pickerの最初のラベルが反映されないので空文字にしましたが、なぜそうなるのか謎です。

あとiPhone13では正常に表示されますが、iPhone11ではFromとToがまともに表示されません。月が英語表記になっています。英語表記になって幅が広がったためラベルがはみ出したのでしょう。何らかの対策が必要になります。表記を統一できない場合は、自分用のアプリなのでiPhone13を切り捨てることになります。

import SwiftUI
import CoreData

struct ContentView: View {
    @State private var selectiedDate_from = Date()
    @State private var selectiedDate_to = Date()
    @State private var selection = 1
    @State private var shouldShowSecondView_HR: Bool = false
    @State private var shouldShowSecondView_HRV: Bool = false

    var body: some View {
        NavigationView {
            VStack {
                Text("Health Manager")
                    .foregroundColor(Color(red:70/255,green:14/255,blue:68/255))
                    .font(.system(size: 40))
                    .padding()
                
                DatePicker("From", selection: $selectiedDate_from, displayedComponents: .date)
                    .frame(width: 130, height: 50)
                    .scaleEffect(x: 1.5, y: 1.5)
                    .font(.system(size: 20))
                
                DatePicker("To", selection: $selectiedDate_to, displayedComponents: .date)
                    .frame(width: 130, height: 50)
                    .scaleEffect(x: 1.5, y: 1.5)
                    .font(.system(size: 20))
                    .padding()
                
                Menu {
                    Picker(selection: $selection, label: Text("")) {
                        Text("グラフ").tag(1)
                        Text("リスト").tag(2)
                    }
                } label: {
                    Text("データ選択")
                        .font(.system(size: 20))
                }
                .padding()
                
                NavigationLink(destination: ContentView_HR(), isActive: $shouldShowSecondView_HR) {
                    EmptyView()
                }

                Button {
                    shouldShowSecondView_HR = true
                } label: {
                    Text("心拍数")
                        .foregroundColor(Color.white)
                }
                    .frame(width: 200, height: 100)
                    .background(Color(red:61/255,green:110/255,blue:218/255))
                    .font(.system(size: 24))
                    .cornerRadius(24)
                    .padding()
                
                NavigationLink(destination: ContentView_HRV(), isActive: $shouldShowSecondView_HRV) {
                    EmptyView()
                }

                Button {
                    shouldShowSecondView_HRV = true
                } label: {
                    Text("心拍変動")
                        .foregroundColor(Color.white)
                }
                    .frame(width: 200, height: 100)
                    .background(Color(red:61/255,green:110/255,blue:218/255))
                    .font(.system(size: 24))
                    .cornerRadius(24)
                
                Spacer()
                
            }
        }
    }
}

[Swift] 05 自製ヘルスケアapp / DatePicker

[Mac mini M1, MacOS Monterey 12.3.1, XCode 13.3.1]

日付範囲を指定できるようにして、ガワを整えました。さすがAppleのネイティブ言語だけあって直感的にすらすら書けますね。日本語記事が多いので助かります。

後は他のContentViewとContentViewModelを書いていきます。

データはリストではなくグラフで表示したいです。さらにiCloudにデータを保存できたらSwiftデビュー作は一応完成といったところでしょうか。

XCodeのBuild&Runが結構遅いのでもっとマシンパワーが欲しいです。M1 Proか今秋発売が予想されるM2あたりですかね。

import SwiftUI
import CoreData

struct ContentView: View {
    @State private var selectiedDate_from = Date()
    @State private var selectiedDate_to = Date()
    @State private var shouldShowSecondView_HR: Bool = false
    @State private var shouldShowSecondView_HRV: Bool = false

    var body: some View {
        NavigationView {
            VStack {
                Text("Health Manager")
                    .foregroundColor(Color(red:70/255,green:14/255,blue:68/255))
                    .font(.system(size: 40))
                    .padding()
                
                DatePicker("From", selection: $selectiedDate_from, displayedComponents: .date)
                    .frame(width: 130, height: 50)
                    .scaleEffect(x: 1.5, y: 1.5)
                    .font(.system(size: 20))
                    .padding()
                
                DatePicker("To", selection: $selectiedDate_to, displayedComponents: .date)
                    .frame(width: 130, height: 50)
                    .scaleEffect(x: 1.5, y: 1.5)
                    .font(.system(size: 20))
                    .padding()
                
                NavigationLink(destination: ContentView_HR(), isActive: $shouldShowSecondView_HR) {
                    EmptyView()
                }

                Button {
                    shouldShowSecondView_HR = true
                } label: {
                    Text("心拍数")
                        .foregroundColor(Color.white)
                }
                    .frame(width: 200, height: 100)
                    .background(Color(red:61/255,green:110/255,blue:218/255))
                    .font(.system(size: 24))
                    .cornerRadius(24)
                    .padding()
                
                NavigationLink(destination: ContentView_HRV(), isActive: $shouldShowSecondView_HRV) {
                    EmptyView()
                }

                Button {
                    shouldShowSecondView_HRV = true
                } label: {
                    Text("心拍変動")
                        .foregroundColor(Color.white)
                }
                    .frame(width: 200, height: 100)
                    .background(Color(red:61/255,green:110/255,blue:218/255))
                    .font(.system(size: 24))
                    .cornerRadius(24)
                
                Spacer() // 上詰め
                
            }
        }
    }
}

[Swift] 04 自製ヘルスケアapp ボタン設定 / 色・フォントサイズ他

[Mac mini M1, MacOS Monterey 12.3.1, XCode 13.3.1]

ボタンを角丸にして色を付けました。こういうことができるようになるとコーディングが楽しくなってきます。

色をRGBで指定する際、0から1.0で指定するのが面倒です。分数は使えます。自製アプリ ColorSampleJPにRGB(0 – 1.0)を追加しなければ。

import SwiftUI
import CoreData

struct ContentView: View {
    @State private var shouldShowSecondView_HR: Bool = false
    @State private var shouldShowSecondView_HRV: Bool = false

    var body: some View {
        NavigationView {
            VStack {
                NavigationLink(destination: ContentView_HR(), isActive: $shouldShowSecondView_HR) {
                    EmptyView()
                }

                Button {
                    shouldShowSecondView_HR = true
                } label: {
                    Text("心拍数")
                        .foregroundColor(Color.white)
                }
                .frame(width: 200, height: 100)
                .background(Color(red:61/255,green:110/255,blue:218/255))
                .font(.system(size: 24))
                .cornerRadius(24)
                .padding()
                
                NavigationLink(destination: ContentView_HRV(), isActive: $shouldShowSecondView_HRV) {
                    EmptyView()
                }

                Button {
                    shouldShowSecondView_HRV = true
                } label: {
                    Text("心拍変動")
                    .foregroundColor(Color.white)
                }
                .frame(width: 200, height: 100)
                .background(Color(red:61/255,green:110/255,blue:218/255))
                .font(.system(size: 24))
                .cornerRadius(24)
                
            }
        }
    }
}