20200409のiOSに関する記事は6件です。

iPhoneのショートカットAppを使って、ヘルスケアに体温を楽々で記録する

みなさんこんばんは、スズキです。コロナ流行ってますね。怖いです。

求められる検温

さて、世の中的にはリモートが広まりつつありますが、友人の会社は作業開始時の日報に検温の記録が求められ始めたそうです。いやまぁ確かに学校と保育園は既にそうではあった。
そして、「検温記録で検索したらルナルナしか出ない、そうじゃないんだ」という声が飛び込んできました。

検温の記録自体は、iOS標準のヘルスケアアプリに記録することが出来ます。ですが、辿り着くまでのUIUXがちょっと分かりにくいんですよね。ご婦人系のカテゴリの中に入ってしまっていて…。(あんまり言うのも野暮なので各自確認してくれ)。
結論として、iPhone標準のヘルスケアアプリで体温の記録の蓄積は行なえます。
ちょっと調べてみたところ、ショートカットAppのAPIがあったので、気不味い画面を開くこと無くスムーズに健康を管理出来るよう、レシピを作ってみたので公開します。

尚、本件は以下のサイトを大いに参考にさせて頂きました。
ありがとうございます。

何となく風は爽やか / conyline
ヘルスケアに現在の体温を記録するショートカットを作成する。(iOS13 ショートカット)
「if文」と「繰り返し」を使ってショートカットレシピを作成する。(iOS13 ショートカット)

ショートカットAppとは

iOS12だか13で追加された機能です。
コードの書かないプログラミングというか、IFTTTの凄いやつというか、起動して値を入れたらBに変換されてCに収まっていく、ということを、アプリを跨いでやってくれる機能です。詳しくは以下。

iPhone や iPad でショートカット App を使う - Apple サポート

手順

1. アプリの準備

‎「ショートカット」をApp Storeで
まずこの「ショートカット」アプリを入れて下さい。
気付いてないだけで、放置されている可能性もある。

2. ショートカットレシピのダウンロード

ショートカット
今回作成したのはこちらになります。
iPhoneから接続するとダウンロードして、諸々聞いてくるので差し支えなければYESで進めて下さい。
諸々とは、ショートカットアプリがヘルスケアデータの書き込みと読み込みの許可を求めているよ、とか、インターネ
ットで公開されているレシピだから気をつけてね、とかそういう話です。

また、オフィシャルではない公開アプリは予め許可設定を入れておかないと弾かれます。
こちらだけ、どうか自己責任ということでご承知おき下さい。
IMG_83B313E0DF37-1.JPG

3. 実行する

ショートカットを実行します。
こんな感じで動作します。
20200409scapp.gif

数値を入れる、記録される、直近の記録も見たければ、ヘルスケアに飛ばすのでご確認下さい。

4. 実は他にもある起動方法

詳細は割愛しますが、大きく分けて3つ、起動する方法があります。

  1. オートメーション(=指定した時間や目覚ましと同時に起動する)
  2. ホームボタン化ショートカット内のアプリそのものをホーム画面に配置して、タップ一つで起動出来ます
  3. 画面左のウィジェットに配置。フリックしてボタンぽちーで完結

ちょっと弄れば、「検温を開始して下さい」(3分待機)「入力してください〜移行続く」みたいな動きにも出来るので、遊べて楽しいです

動作の内容について

ざっくり書きます。
IMG_3942.jpg
IMG_3943.jpg
IMG_3944.jpg
IMG_3945.jpg
IMG_3946.jpg

慌てて作ったのですごい見にくくてすいません。

その他

ざっくり以上です。
体温のパラメータを基礎体温とかに変えれば、そのまま流用出来たりすると思うので、自由度高いと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

swiftの基礎的な書き方

Xcodeの基礎的なことを書いていきます。

変数と定数

変数は数学で習う、χみたいなもので数字を何回でも入れ替えることができる。
変数では、先頭にvarをつける。
var 変数名: 型名
定数は一度決めたら変えられないもの。
例えば円の直径を計算するときのπ=3.14みたいな、変えたらおかしくなってしまうもの。
定数は先頭にletをつける。
let 変数名: 型名

Swiftのデータ型

Swiftのデータ型を書いていきます。

Int型

正と負のデータを扱うときに使う。

Double型

小数点を扱うときに使う。
小数点を扱うものにFloat型もあるがこれは扱える範囲が狭いため基本的にはDouble型を使う。

String型

文字列を扱うときに使う。

Bool型

論理演算でtrueかfalseを扱うときに使う。

条件分岐処理

条件分岐をするときは大体if文を使う。
こんな感じ

var num = 10

if (num > 5) {
    print("true")
} else {
    print("false")
}

結果は
trueとなる

ここにelse ifを使うことでどんどん条件を増やしていく。

文字列の結合

文字列の結合はこんな感じに書く

var test1 = tarou
var test2 = maru

print(test1 + test2)

結果
taroumaru

文字と数字はそのままだと結合できないので、文字に合わせる場合はString(test1)というような感じで結合する。

出力

swiftにはprint,dumpという出力方法がある。
基本的に使うのはprintで、細かななところまで出力できるのがdumpdeです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【SwiftUI】EmptyStateを作って理解するPreferenceKey

この記事は、SwiftUIで以下のような「子孫Viewの情報をコンテナViewで読み取るデザインのカスタムViewを作りたい」というiOSエンジニアへの知見共有を目的としており、調べた情報をなるべく素直に伝えていけるように書いていきます。

NavigationView {
    Text("Hello World")
        .navigationTitle("MyApp")
}

自分がEmptyStateをSwiftUIで作ってみたいと思って調べ始めたので、その流れに沿って進めていきます。

APIデザイン設計

冒頭にも挙げましたが、SwiftUIとのAPI一貫性を保つコンポーネントを作るために以下のNavigationViewようなAPIを目指しました。

参考にしたデザイン

NavigationView {
    Text("Hello World")
        .navigationTitle("MyApp")
}

ゴール

これまでのUIKit製のライブラリではDelegateを使ったEmptyStateの設定が多かったですが、SwiftUIでは@ViewBuilderを利用してカスタマイズ製が高く簡潔なEmptyStateのAPIを目指します。

EmptyStateView(empty: $empty) {
    Text("Hello World")
        .emptyStateItems {
            VStack(spacing: 15) {
                Image("EmptyImage")
                Text("EmptyTitle")
                Text("EmptyDescription")
            }
        }
}

調べて分かった事

上記のAPIを構築するために調べる必要があったのは以下の2つです。

  • .navigationTitle(text:)適応後のViewの具体型がどのようになっているのか?
  • コンテナViewがどのようにして内部Viewの属性を知るのか?

以降はこの2つについて解説していきます。

NavigationViewが構築する具体型

構築された具体型を知るために、以下のようなコードをPlaygroundで書いて検証しました。

struct SampleView : View {

    var body : some View {
        NavigationView {
            Text("Hello world")
                .navigationBarItems(leading: Text("Leading"))
        }
    }

}
let sampleView = SampleView()
print(type(of: sampleView.body))

このViewの具体型は

NavigationView<EnvironmentReaderView<ModifiedContent<Text, _PreferenceWritingModifier<NavigationBarItemsKey>>>>

になります。ModifiedContent以外は見慣れない型だと思うので、解説していきます。

EnvironmentReaderView

Appleの非公開APIのViewです。これについては全く情報がありません。

_PreferenceWritingModifier

こちらのAppleの非公開APIのModifierですが、iOS13以降のSwiftUIのswiftinterfaceを見る事で存在を確認できます。

@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
@frozen public struct _PreferenceWritingModifier<Key> : SwiftUI.ViewModifier where Key : SwiftUI.PreferenceKey {
  public var value: Key.Value
  @inlinable public init(key _: Key.Type = Key.self, value: Key.Value) {
        self.value = value
    }
  public static func _makeView(modifier: SwiftUI._GraphValue<SwiftUI._PreferenceWritingModifier<Key>>, inputs: SwiftUI._ViewInputs, body: @escaping (SwiftUI._Graph, SwiftUI._ViewInputs) -> SwiftUI._ViewOutputs) -> SwiftUI._ViewOutputs
  public typealias Body = Swift.Never
}

そして、func preference()というModifierの具体型として返却されているのが分かります。(some Viewで隠蔽されているので、通常知ることはできませんが)

@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
extension View {
  @inlinable public func preference<K>(key _: K.Type = K.self, value: K.Value) -> some SwiftUI.View where K : SwiftUI.PreferenceKey {
        return modifier(_PreferenceWritingModifier<K>(value: value))
    }

}

_PreferenceWritingModifierは、実際には上記のModifierを呼ぶ事で返されているので自作する必要がなさそうです。

NavigationBarItemsKey

Appleの非公開APIです。ただ、外側の_PreferenceWritingModifierが、Key:SwiftUI.PreferenceKeyを利用しているので、これがPreferenceKeyに適合した具体型であることは分かります。

PreferenceKeyはコンテナView側から内部のViewが保持する情報を読み出すための機構です。公式ドキュメントは記述が少ないので、チュートリアルから一歩踏み出したSwiftUIのCustom Viewの作り方ーその2(PreferenceKey編)という記事が分かりやすかったです。

コンテナViewで内部Viewの保持する情報を読み出す

PreferenceKeyを利用する事で内部Viewの情報が読める事がわかりました。

なので方針としてはこのようになります。

  • 内側のViewで与えられる@ViewBuilderのViewを保持しておくために、PreferenceKeyに適合したEmptyStateContentsKeyを自作する。
  • extension Viewで以下のような関数を用意し、emptyStateItemsを注入する。
func emptyStateItems<Items>(@ViewBuilder items: () -> (Items)) -> some View where Items : View { }
  • EmptyStateViewにおいて、bodyで以下のようにうまくpreferenceの値を読み出してemptyContentとして返したい。
struct EmptyStateView : View {
    @Binding var empty: Bool
    var body: some View {
        if empty {
            return emptyContent
        } else {
            return filledContent
        }
    }
}

調べて分からなかった事

方針は立てましたが、EnvironmentReaderViewについてはまるっきり分かりません。名前からしてなんらかのEnvironmentValueの読み出しに関連しているような事だけは分かります。しかし、今回の実装にはこのViewは必要ありません。

実装

それでは調べた事を踏まえて実装したEmptyStateViewについて解説していきます。

PreferenceKeyの自作

struct EmptyStatePreferenceKey : PreferenceKey {
    static var defaultValue: Value = .init(view: EmptyView().eraseToAnyView())

    static func reduce(value: inout Value, nextValue: () -> Value) {
        value = nextValue()
    }

    struct Value : Equatable {
        var view: AnyView
        var uuid = UUID()

        static func == (lhs: EmptyStatePreferenceKey.Value, rhs: EmptyStatePreferenceKey.Value) -> Bool {
            lhs.uuid == rhs.uuid
        }
    }

}

Valueの自作

PreferenceKeyassociatedvalue ValueとしてEquatableに適合した型を要求します。
EmptyStatePreferenceKeyでは、クライアント側で渡されたViewを保持しておきコンテンツが空の時に表示する必要があるため、Valueではview:AnyViewをプロパティとして保持させています。等価性を実装しないといけないのですが、AnyViewには上手く利用できそうな情報はないのでここではUUIDを利用します。

加えてstatic var defaultValue: Valueを返す必要があるので、ここではEmptyViewを返しておきます。

reduce(value:, nextValue:)の実装

こちらは必須実装のメソッドになります。

.preference(key: PreferenceKey, value: PreferenceKey.Value)が呼ばれて値を渡されると、このreduceが呼ばれます。この関数では新しく渡されたpreferenceの値をどのように保持するかを決めます。基本的な実装方針として、inoutで渡された構造体の参照に対してnext()の値を加える、もしくは代入する形になります。

例えばNavigationViewだと以下のようにnavigationTitleを利用する事で複数のPreferenceを設定する事ができますが、最後に設定した値だけが利用されます。上記のEmptyStatePreferenceKeyの実装でも、常に最後に設定された値だけが使われるようにvalue = nextValue()としています。

NavigationView {
    Text("")
        .navigationTitle("A")
        .navigationTitle("B")
        .navigationTitle("C")
}

これで子孫Viewで保持している値を親に流す仕組みは作れました。

EmptyStateViewの自作

特筆して複雑な事はしていませんが、注意すべき箇所は2つです。

  1. onPreferenceChangeを利用して@State private var emptyStateItems: AnyViewを更新し、コンテンツが空の場合に表示するようにしています。
  2. extension Viewpublic func emptyStateItems<Items>という関数を用意して、任意の箇所でpreferenceを注入できるようにしています。
/// A view for presenting empty state items when inner content is empty.
public struct EmptyStateView<Content> : View where Content : View {

    private var content: Content

    @State
    private var emptyStateItems: AnyView

    @Binding
    private var empty: Bool

    public init(empty: Binding<Bool>, @ViewBuilder content: () -> Content) {
        self.content = content()

        // Initialized emptyContent from content, Implementation can be simple for several reasons.
        self._emptyStateItems = .init(initialValue: self.content.eraseToAnyView())
        self._empty = empty
    }

    public var body: some View {

        // I don't know the reason but it goes well with using VStack. It doesn't go well with Group.
        VStack<AnyView> {
            if empty {
                return emptyStateItems
                    .onPreferenceChange(EmptyStatePreferenceKey.self) { (preference) in
                        self.emptyStateItems = preference.view
                }
                .eraseToAnyView()
            } else {
                return content
                    .onPreferenceChange(EmptyStatePreferenceKey.self) { (preference) in
                        self.emptyStateItems = preference.view
                }
                .eraseToAnyView()
            }
        }
    }

}

extension View {

    /// Configures the empty state items for the view.
    ///
    /// This modifier only takes effect when this view is inside of and visible within a `EmptyStateView`.
    ///
    /// - Parameter items: A view that appears when the content is empty.
    public func emptyStateItems<Items>(@ViewBuilder items: () -> Items) -> some View where Items : View {
        self.preference(key: EmptyStatePreferenceKey.self, value: .init(view: items().eraseToAnyView()))
    }

}

このようにして、子孫Viewの保持する情報を親Viewで読み取って利用する事が出来ました。
ソースコードの完成版はこちらで公開しています。
https://github.com/yosshi4486/EmptyState/tree/master/Sources/EmptyState

おわりに

ここまでで

  • NavigationViewNavigationView<EnvironmentReaderView<ModifiedContent<Text, _PreferenceWritingModifier<NavigationBarItemsKey>>>>という具体型を構築している
  • PreferenceKeyのシステムを利用して内部Viewの保持する値にアクセスする事で実現可能。
  • EnvironmentReaderViewについては一切わからない。
  • EmptyStateの実現のために、PreferenceKeyの具体型の自作とEmptyStateView`の実装

という解説を行いました。

SwiftUIは深入りしようと思うと

  • まだ情報が十分ではない
  • 仕組みが難しい
  • iOSのバグで奇妙な挙動をする事がある

など大変なため、少しでも情報を公開して知見がネットに貯まれば良いと思い記事化しています。

ここまでお読みいただきありがとうございました。

改定履歴

2020/04/13 PreferenceKeyを利用した実装が実現できたため、関連する部分を差し替えてタイトルを変更しました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【SwiftUI】NavigationViewのようなカスタムViewを自作しようとして分かった事と分からなかった事

この記事は、SwiftUIで以下のような「内部のViewの情報をコンテナViewで読み取るデザインのカスタムViewを作りたい」というiOSエンジニアへの知見共有を目的としています。

NavigationView {
    Text("Hello World")
        .navigationTitle("MyApp")
}

iOSでもAndroidでもよく見かけるEmptyState(iOSでは昔DZNEmptyDataSetがよく使われてた)をPureSwiftUIで実装しようと思ったときに、分かった事とわからなかった事があったので紹介していきます。

この記事は現状では代替案を最後に解説していますが、正しい解決法が分かり次第タイトルを含む記事の内容を更新します。

APIデザイン設計

冒頭にも挙げましたが、SwiftUIとのAPI一貫性を保つコンポーネントを作るために以下のNavigationViewようなAPIを目指しました。

参考にしたデザイン

NavigationView {
    Text("Hello World")
        .navigationTitle("MyApp")
}

ゴール

これまでのUIKit製のライブラリではDelegateを使ったEmptyStateの設定が多かったですが、SwiftUIでは@ViewBuilderを利用してカスタマイズ製が高く簡潔なEmptyStateのAPIを目指しました。

EmptyStateView(empty: $empty) {
    Text("Hello World")
        .emptyStateItems {
            VStack(spacing: 15) {
                Image("EmptyImage")
                Text("EmptyTitle")
                Text("EmptyDescription")
            }
        }
}

分かった事

上記のAPIを構築するために知らなければいけなかったことは下の2つです。

  • .navigationTitle(text:)適応後のViewの具体型がどのようになっているのか?
  • コンテナViewがどのようにして内部Viewの属性を知るのか?

以降はこの2つについて解説していきます。

NavigationViewが構築する具体型

構築された具体型を知るために、以下のようなコードをPlaygroundで書いて検証しました。

struct SampleView : View {

    var body : some View {
        NavigationView {
            Text("Hello world")
                .navigationBarItems(leading: Text("Leading"))
        }
    }

}
let sampleView = SampleView()
print(type(of: sampleView.body))

このViewの具体型は

NavigationView<EnvironmentReaderView<ModifiedContent<Text, _PreferenceWritingModifier<NavigationBarItemsKey>>>>

になります。ModifiedContent以外は見慣れない型だと思うので、解説していきます。

EnvironmentReaderView

Appleの非公開APIのViewです。これについては全く情報がありません。

_PreferenceWritingModifier

こちらのAppleの非公開APIのModifierですが、iOS13以降のSwiftUIのswiftinterfaceを見る事で存在を確認できます。

@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
@frozen public struct _PreferenceWritingModifier<Key> : SwiftUI.ViewModifier where Key : SwiftUI.PreferenceKey {
  public var value: Key.Value
  @inlinable public init(key _: Key.Type = Key.self, value: Key.Value) {
        self.value = value
    }
  public static func _makeView(modifier: SwiftUI._GraphValue<SwiftUI._PreferenceWritingModifier<Key>>, inputs: SwiftUI._ViewInputs, body: @escaping (SwiftUI._Graph, SwiftUI._ViewInputs) -> SwiftUI._ViewOutputs) -> SwiftUI._ViewOutputs
  public typealias Body = Swift.Never
}

そして、func preference()というModifierの具体型として返却されているのが分かります。(some Viewで隠蔽されているので、通常知ることはできませんが)

@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
extension View {
  @inlinable public func preference<K>(key _: K.Type = K.self, value: K.Value) -> some SwiftUI.View where K : SwiftUI.PreferenceKey {
        return modifier(_PreferenceWritingModifier<K>(value: value))
    }

}

_PreferenceWritingModifierは、実際には上記のModifierを呼ぶ事で返されているので自作する必要がなさそうです。

NavigationBarItemsKey

Appleの非公開APIです。ただ、外側の_PreferenceWritingModifierが、Key:SwiftUI.PreferenceKeyを利用しているので、これがPreferenceKeyに適合した具体型であることは分かります。

PreferenceKeyはコンテナView側から内部のViewが保持する情報を読み出すための機構です。公式ドキュメントは記述が少ないので、チュートリアルから一歩踏み出したSwiftUIのCustom Viewの作り方ーその2(PreferenceKey編)という記事が分かりやすかったです。

コンテナViewで内部Viewの保持する情報を読み出す

PreferenceKeyを利用する事で内部Viewの情報が読める事がわかりました。

なので方針としてはこのようになります。

  • 内側のViewで与えられる@ViewBuilderのViewを保持しておくために、PreferenceKeyに適合したEmptyStateContentsKeyを自作する。
  • extension Viewで以下のような関数でEnvironmentReaderViewに包まれたModifiedContentを返すようにする
func emptyStateItems<Items>(@ViewBuilder items: () -> (Items)) -> some View where Items : View { }
  • EmptyStateViewにおいて、bodyで以下のようにうまくpreferenceの値を読み出してemptyContentとして返したい。
struct EmptyStateView : View {
    @Binding var empty: Bool
    var body: some View {
        if empty {
            return emptyContent
        } else {
            return filledContent
        }
    }
}

分からなかった事

方針は立てましたが、EnvironmentReaderViewについてはまるっきり分かりません。名前からしてなんらかのEnvironmentValueの読み出しに関連しているような事だけは分かります。しかし、今回の実装にはこのViewは必要ありません。

実装

それでは実装を解説していきます。

PreferenceKeyの自作

struct EmptyStatePreferenceKey : PreferenceKey {
    static var defaultValue: Value = .init(view: EmptyView().eraseToAnyView())

    static func reduce(value: inout Value, nextValue: () -> Value) {
        value = nextValue()
    }

    struct Value : Equatable {
        var view: AnyView
        var uuid = UUID()

        static func == (lhs: EmptyStatePreferenceKey.Value, rhs: EmptyStatePreferenceKey.Value) -> Bool {
            lhs.uuid == rhs.uuid
        }
    }

}

Valueの自作

PreferenceKeyassociatedvalue ValueとしてEquatableに適合した型を要求します。
EmptyStatePreferenceKeyでは、クライアント側で渡されたViewを保持しておきコンテンツが空の時に表示する必要があるため、Valueではview:AnyViewをプロパティとして保持させています。等価性を実装しないといけないのですが、AnyViewには上手く利用できそうな情報はないのでここではUUIDを利用します。

加えてstatic var defaultValue: Valueを返す必要があるので、ここではEmptyViewを返しておきます。

reduce(value:, nextValue:)の実装

こちらは必須実装のメソッドになります。

.preference(key: PreferenceKey, value: PreferenceKey.Value)が呼ばれて値を渡されると、このreduceが呼ばれます。inoutで渡された構造体の参照に対して、nextの値を加えていくような形が基本になります。

例えばNavigationViewだと以下のように複数のPreferenceを設定する事ができますが、最後に設定した値が利用されます。上記のEmptyStatePreferenceKeyの実装でも、常に最後に設定された値だけが使われるようにvalue = nextValue()としています。

NavigationView {
    Text("")
        .navigationTitle("A")
        .navigationTitle("B")
        .navigationTitle("C")
}

これで子孫Viewで保持する値を親に流す仕組みは作れました。

EmptyStateViewの自作

特筆して複雑な事はしていませんが、注意すべき箇所は2つです。

  1. onPreferenceChangeを利用して@State private var emptyStateItems: AnyViewを更新しています。
  2. extension Viewpublic func emptyStateItems<Items>という関数を用意して、任意の箇所でpreferenceを注入できるようにしています。
/// A view for presenting empty state items when inner content is empty.
public struct EmptyStateView<Content> : View where Content : View {

    private var content: Content

    @State
    private var emptyStateItems: AnyView

    @Binding
    private var empty: Bool

    public init(empty: Binding<Bool>, @ViewBuilder content: () -> Content) {
        self.content = content()

        // Initialized emptyContent from content, Implementation can be simple for several reasons.
        self._emptyStateItems = .init(initialValue: self.content.eraseToAnyView())
        self._empty = empty
    }

    public var body: some View {

        // I don't know the reason but it goes well with using VStack. It doesn't go well with Group.
        VStack<AnyView> {
            if empty {
                return emptyStateItems
                    .onPreferenceChange(EmptyStatePreferenceKey.self) { (preference) in
                        self.emptyStateItems = preference.view
                }
                .eraseToAnyView()
            } else {
                return content
                    .onPreferenceChange(EmptyStatePreferenceKey.self) { (preference) in
                        self.emptyStateItems = preference.view
                }
                .eraseToAnyView()
            }
        }
    }

}

extension View {

    /// Configures the empty state items for the view.
    ///
    /// This modifier only takes effect when this view is inside of and visible within a `EmptyStateView`.
    ///
    /// - Parameter items: A view that appears when the content is empty.
    public func emptyStateItems<Items>(@ViewBuilder items: () -> Items) -> some View where Items : View {
        self.preference(key: EmptyStatePreferenceKey.self, value: .init(view: items().eraseToAnyView()))
    }

}

このようにして、子孫Viewの保持する情報を親Viewで読み取って利用する事が出来ました。
ソースコードの完成版はこちらで公開しています。
https://github.com/yosshi4486/EmptyState/tree/master/Sources/EmptyState

おわりに

ここまででNavigationViewのようなカスタムViewの自作において

  • NavigationViewNavigationView<EnvironmentReaderView<ModifiedContent<Text, _PreferenceWritingModifier<NavigationBarItemsKey>>>>という具体型を構築している
  • PreferenceKeyのシステムを利用して内部Viewの保持する値にアクセスする事で実現可能。
  • EnvironmentReaderViewについては一切わからない。
  • EmptyStateViewの自作

という解説を行いました。

もし何か知見をお持ちの方いらっしゃいましたら、お助け頂けると助かります。

改定履歴

2020/04/13 PreferenceKeyを利用した実装が実現できたため、関連する部分を差し替えました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

FlutterがiPhoneの実機ではビルドできない人へ

iOS13.3ではビルドできないよ!!!13.4にアップデートしましょう!!

僕の伝えたいことはこれだけです。

謎のエラーにflutterでアプリ開発し始めた瞬間思いっきり出鼻を挫かれましたが、久々に触ってみて、iOSをアップデートしたら直りました。

しっかりstackoverflowか何かでアップデートに伴うバグはチェックしましょう、、、(自戒)

その他注意点

Xcodeの設定を忘れずにしましょう。


  • Signing & CapabilityでTeamを設定すること

  • BundleIDの設定


など。

・BundleIDの設定についてのメモ
アプリのBundleIDとそれが含めるbundleのBundleIDが一致しているよ(?)みたいなエラーが出た時はそれらのBundleIDを異なるものにしたら直りました。(雑すぎてすみません)

リクエストがあればできるかぎり詳しく書いていこうと思います!!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

"001 UILabelで文字を表示" iOS app開発 習得日記 (5)

UIkitを覚えるために
こちらの写経がいいときいたので始めた
(http://docs.fabo.io/swift/uikit/001_uilabel.html)

001 UILabelで文字を表示

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        let bWidth: CGFloat = 200
        let bHeight: CGFloat = 50

        let posX: CGFloat = self.view.bounds.width/2 - bWidth/2
        let posY: CGFloat = self.view.bounds.height/2 - bHeight/2

        let label: UILabel = UILabel(frame: CGRect(x: posX, y: posY, width: bWidth, height: bHeight))

        label.backgroundColor = UIColor.orange

        label.layer.masksToBounds = true

        label.layer.cornerRadius = 20.0

        label.shadowColor = UIColor.gray

        label.textAlignment = NSTextAlignment.center

        self.view.backgroundColor = UIColor.cyan

        self.view.addSubview(label)



    }


}

selfは、ViewControllerのインスタンスを表しているのでよいのか?
また、

label.layer.masksToBounds

とドットを続けて書いているところがわからない。
labelインスタンスの layerプロパティのmasksToboundsプロパティということ?
プロパティのプロパティ?
どういうこと?

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む