カテゴリー: IT

  • Vision Pro M5チップ搭載機発売

     近々出るという噂は耳にしていたのですが、こんなに早いとは思いませんでした。先日初代モデルを購入したところです。ちょっと悔しい気はありますが、初代機を持っているというささやかな優越感で満足しています。発売当初から欲しくて、一刻も早く購入したかったのです。使い始めてまだ1月ほどですが、十分楽しめているし、まだまだ、使いこなせていないし、よしとします。そうやって、自分の気持ちを畳み込まないと落ち込みます。

  • Zeiss Optical Inserts到着

     予定より2日早く到着しました。コンタクトレンズを真剣に考えたくらい待ち侘びていたので、到着の知らせをパートナーがLINEで知らせてくれた時は、仕事中でなければ、歓声を挙げていたかもしれません。

     帰宅して早速セットし、登録して、視線と手の設定をしました。設定の時点で、今までの視界が嘘のようにクリアになっているのには驚きました。当然の結果なのですが、感激しました。今まで、レンズなしで見ていた世界は、夢の中のように漠然としていたのですが、レンズを入れた途端、目が覚めたように、本当にクリアで、ポップアップで出てくるメッセージもしっかり認識できます。文字入力も苦労せずできます。やはり必要なものは揃えないとダメだと思いました。

     早速、VIsion ProでMacを仮想ディスプレイで表示して、この投稿を書いてます。ワイド画面は思ったより広い感じです。デフォルトの解像度5120×1440のまま、さらに拡大できます。今まで27インチのMacにミラーリングしていたのですが、その3倍の画面の広さの感じです。解像度は10240×2880まで上げられるようですが、私はデフォルトの解像度が見やすいので、そちらを拡大して使うつもりです。

  • AirPods Pro 3予約しました

     Vision Proにはスピーカーがついているのですが、出ている音と耳に入ってくる音の量に結構違いがあるようです。
    ソファーに座り、そんなに音を大きくしているつもりはなかったのですが、Vision Proで動画を見ていると、横に座っていたパートナーが、
    「すごい音漏れ」
    といいました。
     気になってスピーカーと耳を手で包み込んでみると、そりゃそうだよなと思うくらい耳に音が流れ込んできました。
    空間演出がしっかりできているのに、これはもったいない。
     ネットを調べてみると、AirPods Pro 3がVision Proと相性が良さそうです。ノイズキャンセリングも性能を上げているようです。
    AirPods Pro 3は結構お高いです。でもこれを使えば没入感は格段に上がると思います。体感できるであろう没入感と金額を天秤にかけて、結局購入に踏み切りました。

  • Vision Pro初期設定クリア

     私の視力は裸眼だと両目で0.1ないのでZeiss Optical insertsが必要です。しかしまだ手元にありません。それでも画面は何となく認識できそうなので、視線と手の設定に挑戦です。何とか目を凝らして指示に従ってクリアしました。アプリアイコンが出てきました。感動です。

  • Vision Pro クイック設定

     Zeiss Optical Insertsが来るまで何もできないかと思いきや、iPhoneのAppleIDとWi-Fi設定を飛ばしたりできるとか。

     やってみました。そもそも電源が入るところから見えてなかったので、そこで諦めていたのですが、ちょっと辛抱していたら、アップルのマークが出てきました。それからiPhoneを近づけたら、iPhoneにVision Proのマークが出てきました。そこからは指示に従っていたら、あっという間に終わってしまいました。無事アカウントとWi-Fiの設定は完了しました。

  • Vision Pro購入

     Vision Proがついに手に入ります。

     世間では、次世代が出るとか噂があるのですが、私のタイミングで購入を決めました。

     ネットでの注文は毎回ドキドキします。確定のボタンを押したら落ち着くのですが、そのあとは、

    「本当にこれでOKなの?」

    と不安になります。Apple Appを確認すると、登録されているし、ちょっと安心です。

     受け取り方法は、宅配もあったのですが、Apple Storeで受け取る場合フィッティングもしてもらえるみたいだし、最近できたApple Storeが会社帰りに寄れるので、そこでの受け取りを選択しました。

     私は近視なので、Zeiss Optical Insertsも同時購入しました。こちらは自宅への宅配のみです。

     本体は当日には受け取れるのですが、Zeiss Optical Insertsは1週間ほどかかるそうです。

     とりあえず、バッテリーを充電して、電源オンしてみましたが、Zeiss Optical Inserts無しでは、書かれている文字がさっぱり読めませんでした。Zeiss Optical Insertsよ、早くきておくれ。それまで、Vision Proは眺めるだけの存在です。

  • プログラミング言語SwiftのStructuresとProtocolについて

     Structures(以下構造体)は定数、変数、関数の定義に使用する構文を使用して、プロパティとメソッドを定義し、機能を追加します。Classes(以下クラス)でもいいのではと思ってしまうのですが、特にクラス特有の機能が必要なければ構造体を使うことが推奨されています。

     クラスは他のクラスを継承することでプロパティとメソッドの定義を継承・追加・変更できます。さらにインスタンスという具体的なのもとして、変数に割り当てるとき、参照渡しとして、割り当てます。素敵な機能なのですが、何世代も継承されると、変更がどこに影響するかがわからなくなる可能性があったりするようです。そこが、致命的な問題のようです。そこで構造体が登場です。この子は他の構造体のプロパティとメソッドの定義を継承できません。また、インスタンスを変数に割り当てるとき値渡しとして、割り当てます。シンプルを極めた感じです。これでデバッグをしやすくした感じです。

     ところが、継承ができないと、同じ機能を持つものとして扱うことが難しくなり、汎用性が妨げられます。そこで登場するのがProtocol(以下プロトコル)です。プロトコルは特定のタスクや機能に適したメソッド、プロパティ、その他の要件の設計図を定義します。 その後、プロトコルをクラス、構造、または列挙で採用して、これらの要件の実際の実装を提供できます。 プロトコルの要件を満たす型は、そのプロトコルに準拠していると言われます。型を同じプロトコルに準拠させることで、汎用性がひろがります。

     値渡しをすることで、複雑さを避け、プロトコルに準拠させることで汎用性をました構造体はSwiftでプログラミングすることの利点だと思います。

  • VisionProデモ体験

       VisionProデモ体験

     近視の老眼の場合どうなるんだろうと気に

    なってました。それを知りたくて、Apple

    StoreでVisionProのデモ体験をしてきました。

     最初に近視のメガネを専用の機械で解析し

    てくれました。そして、それに合うZEISS

    Optical Insertsなるレンズを入れてくれてまし

    た。

     仮想ディスプレイは近くにしても遠くにし

    てもはっきり見えました。どうやら老眼の事

    は気にしなくても良いようです。

     音響も素晴らしく、最初に誕生日のお祝い

    の映像で蝋燭を吹き消す場面があるのですが、

    蝋燭を吹き消す息が、顔を通り、耳元を通り

    すぎていく感じがリアルでした。

     また、バスケットボールの3D映像では、

    ボールがこちらに飛んでくる場面で、反射的

    に体が動いてしまいました。

     三十分程度のデモでしたが、とてもワクワ

    クする経験でした。

  • 初めてのアプリ登録

    Apple Developer Program に登録して、1年以上紆余曲折しながら、初めてApp Store Connectにアプリの審査申請をしてみました。Apple Developer Program に登録したての頃は軽い気持ちで取り組んでました。ところが、いろいろ情報を得ていくうちに今までの知識ではとても間に合わないことに気づき、休日を利用し基本的なところから、まず学習していきました。そのかいあってか簡単なアプリを作ることができたので、ここは試しにアプリの登録をしようとこれまた軽い気持ちで、審査申請に挑戦しました。まずアプリをどうやって審査申請のまな板にのせるかがわからず、ネットを調べたら、XCodeのメニューproduct の archiveでできるとのこと、さっそくやってみました。そして、登録申請のボタンがあるのでとりあえず、クリックすると、メッセージの山です。登録上必要事項が記入されていないと、メッセージが出て、審査申請が完了しません。何度もメッセージに従って必要事項を記入して、根負けしそうになりながらも、何度も審査申請をクリックすると、やっと、審査中になりました。そして、2日目に配信準備完了になりました。同時に何通か来ていたメールに、配信への手順の記載があり、それに従い必要項目を設定し配信できました。AppStoreでの表示はその翌日に可能となりました。

    申請にはプライバシポリシーの保存先URLと、サポートページのURLを記載する必要がありました。このサイトの一部を使用することで対応できました。このサイトはそんなことは全く想定しておらず勢いで作成していたのですが、この時ばかりはこのサイトを運営していてよかった思いました。

  • SwiftData @Modelを使ってList項目の追加・移動・削除を実装してみました

    SwiftDataの@Modelを使って、Listの項目を移動させる処理を作ってみました。@Modelはクラスに適用することで、アプリを終了させてもプロパティの値を保持させることができます。ただしアプリを削除したらプロパティの値は消滅します。

    まず、@Modelを適用させるクラスです。

    import Foundation
    import SwiftData
    
    @Model
    final class Item: Identifiable {
        var name: String
        //チェックボックス表示切り替え用
        var check: Bool
        //移動処理時にソートをかける対象
        var sortNumber: Int
        //移動処理をかけるときにIdentifiableプロトコルを準拠させるのに必要
        var id = UUID()
            
        init(name: String, sortNumber: Int) {
            self.name = name
            self.check = false
            self.sortNumber = sortNumber
        }
        init(item: Item) {
            self.name = item.name
            self.check = item.check
            self.sortNumber = item.sortNumber
        }
    }

    Itemの配列をリストとして表示し項目の追加、移動、削除の処理を可能にしたViewがこちらです。

    import SwiftUI
    import SwiftData
    
    struct ItemListView: View {
        //@Model class Item を modelContextとしてしようできるようにします
        //これでmodelContextの insert delete が利用できます
        @Environment(\.modelContext) var modelContext
        //Listのエディットモードを切り替える変数 editModeを設定
        @Environment(\.editMode) var editMode
        //ItemのsortNumberプロパティを並び替えの対象としてItemの配列をnumbersとして設定
        @Query(sort: \Item.sortNumber) private var numbers: [Item]
        @State var shoppingItem: String = ""
        @State var modeName: String = "追加削除モード開始"
    
        var body: some View {
            VStack {
                if editMode?.wrappedValue.isEditing == true {
                    HStack{
                        //買い物リストのアプリにしているので↓
                        TextField("ここに買い物するものを入力してください", text: $shoppingItem)
                            .font(.system(size: 30))
                            .background(Color.yellow)
                        Button{
                            let i = numbers.count
                            modelContext.insert(Item(name: shoppingItem, sortNumber: i))
                            shoppingItem = ""
                        } label: {
                            Text("追加")
                                .font(.system(size: 24))
                        }
                    }
                    .padding()
                }
                //[Item]のnumbersをListで表示
                List {
                    ForEach(numbers) { number in
                        HStack {
                            //チェックボックスの表示を切り替えると表示文字の大きさと色を変更
                            if number.check == false {
                                Text(number.name)
                                    .font(.system(size: 24, weight: .bold, design: .rounded))
                                    .foregroundColor(.black)
                            } else {
                                Text(number.name)
                                    .font(.system(size: 18))
                                    .foregroundColor(.gray)
                            }
                            Spacer()
                            //チェックボックスの表示を切り替え
                            if number.check == false {
                                Image(systemName: "square")
                                    .scaledToFill()
                                    .onTapGesture {
                                        number.check = true
                                    }
                            } else {
                                Image(systemName: "checkmark.square")
                                    .scaledToFill()
                                    .onTapGesture {
                                        number.check = false
                                    }
                            }
                        }
                    }
                    //移動時の処理
                    .onMove{fromOffSet,newOffset in
                        var fromIndex: Int = 0
                        var toIndex: Int = 0
                        var tempItem: [Item] = numbers
    
                        //fromOffSetの配列オブジェクトから並び替え用の数値を取得
                        //これが移動対象の並び替え用の数値
                        for item in fromOffSet {
                            fromIndex = item
                        }
                        
                        if newOffset > fromIndex{
                            toIndex = newOffset - 1
                        }else{
                            toIndex = newOffset
                        }
                        //項目の移動
                        tempItem.swapAt(fromIndex, toIndex)
                        //sortNumberを整える
                        for i in 0..<tempItem.count {
                            tempItem[i].sortNumber = i
                        }
    
                        //いったん全削除
                        for i in 0..<numbers.count {
                            modelContext.delete(numbers[i])
                        }
                        //整った配列を追加
                        for i in 0..<tempItem.count {
                            modelContext.insert(Item(item: tempItem[i]))
                        }
                    }
                    //削除時の処理
                    .onDelete{ indexSet in
                        //
                        for item in indexSet {
                            let obj = numbers[item]
                            modelContext.delete(obj)
                            //削除処理後並び替え用の数値を変化させます
                            //これにより移動処理後に項目の並び替えが行えます
                            //項目追加時に配列の個数を元に並び替え用数値を設定しているのでここで全体の数値を整理しています
                            if obj.sortNumber < numbers.count - 1 {
                                for i in obj.sortNumber..<numbers.count {
                                    numbers[i].sortNumber -= 1
                                }
                            }
                        }
                    }
                    .listRowBackground(Color.yellow)
                }
                .scrollContentBackground(.hidden)
                .background(.clear)
    
                Button {
    
                    if editMode?.wrappedValue.isEditing == true {
                        editMode?.wrappedValue = .inactive
                        modeName = "追加削除モード開始"
                    } else {
                        editMode?.wrappedValue = .active
                        modeName = "追加削除モード終わり"
                    }
                    //ソート用ナンバーを再設定
                    if numbers.isEmpty == false  {
                        for i in 0...numbers.count - 1 {
                            numbers[i].numberSet(sortNumber: i)
                        }
                    }
    
                }label: {
                    Text(modeName)
                        .padding()
                        .font(.system(size: 24))
                        .foregroundColor(.black)
                        .background(Color.green)
                        .cornerRadius(10)
                }
            }
            .padding()
        }
    }