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

M1搭載MacでReact Nativeの環境構築してみた

はじめに M1 Macの環境構築で苦労したので、うまくいった方法をまとめたいと思います。 これから始める方にも分かりやすくしようと思うので、分かるところは飛ばしながら読んでください。 細かい説明は省いているので、随時気になるところは調べてください。 参考にさせていただいた記事 Homebrewのインストール 2021/2/5にAppleSilicon(M1)対応のHomebrew3.0.0がリリースされました。 今回はこちらをインストールしていきたいと思います。 以下サイトからインストールできます https://brew.sh/ Install Homebrewと書かれているところの下のコードをコピーしてターミナルに貼り付けます。 パスワードを入力するとインストールが始まります。 Press RETURN to continue or any other key to abort と表示されたら[Enter]を押します。 インストールが完了したらHomebrewのパスを追加する必要があるので、以下のコードを実行します。 ユーザー名と書かれているところは自身のユーザー名に変更して下さい。 echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> /Users/ユーザ名/.zprofile 以下のコマンドを実行し、正常に出力されれば成功です。 brew help Nodeenvのインストール brew install nodenv touch .zshrc インストール出来たらパスを通します eval "$(nodenv init -)" Nodeのインストール ターミナルをRosetta2に切り替えてインストールしてください。 ファインダーのアプリケーションからターミナルを探し、右クリックで「情報を見る」を選択。 Rosettaを使用して開くにチェックを入れて閉じます。 一度ターミナルを終了し再度起動します。 以下コマンドを実行すると現在の状態が確認できます。 ・arm64: M1 ・x86_64: Rosetta2 uname -a # Darwin Kernel Version 20.3.0... x86_64 以下コマンドでNodeをインストールしていきます nodenv install 15.9.0 インストール出来たらバージョンを確認しましょう nodenv -v yarnのインストール brew install yarn インストール出来たらバージョンを確認しましょう yarn -v Watchmanのインストール brew install watchman JDKのインストール brew install --cask adoptopenjdk/openjdk/adoptopenjdk8 Cocoapodsのインストール brew install cocoapods React Native CLIのinstall npm install -g react-native-cli npmはnodeと共にinstallされるので、すでに使用できる状態になっていると思います。 CLI(Command Line Interfaceの略) Xcodeのインストール App Storeからインストールして起動 (結構時間がかかります) Preferences > Locations > Command Line Tools を設定します Android Studioのインストール 以下サイトからインストール出来ます https://developer.android.com/studio?hl=ja&gclid=Cj0KCQjwytOEBhD5ARIsANnRjVj-zR8PvuV40cHP5tnPfqo4sCn9VZigpAvyiMCFWqHrmlxFAZyg0_YaAhhCEALw_wcB&gclsrc=aw.ds 初回起動時は「Custom」を選択してください ・ Android SDK ・ Android SDK Platform ・ Android Virtual Device の3つにチェックを入れ「Next」を押します。 SDK Manager ① ・Configure > SDK Manager を選択 ・"SDK Platforms" > "Show Package Details"にチェックを入れる ・以下にチェックを入れインストールする Android SDK Platform 29 Intel x86 Atom_64 System Image ② "SDK Tools" > "Show Package Details"にチェックを入れる 29.0.2 にチェックを入れインストールする AVD Manager 以下サイトの android-emulator-m1-preview.dmg をダウンロードする https://github.com/741g/android-emulator-m1-preview/releases/tag/0.1 「アプリケーション」フォルダで右クリックして「開く」 これでAndroidエミュレータが起動できるようになりますので、起動した状態でAndroid Studioを再起動すればエミュレータが認識されます。 プロジェクトの作成 以下公式サイトを参考にプロジェクトを作成していきます。 https://reactnative.dev/docs/environment-setup npmを使用してExpoCLIコマンドラインユーティリティをインストールします npm install -g expo-cli 次に「AwesomeProject」という新しいReactNativeプロジェクトを作成します。 expo init AwesomeProject cd AwesomeProject npm start これで開発サーバーが起動します。 動作確認 先ほど作った「AwesomeProject」の中に「App.js」というファイルがあるのでエディターで開いてみましょう。 export default function App() と書かれている中のTextの文字の中身を自由に変更し、ファイルを保存。 開発サーバーの中央左側にある Run on Android device/emulator Run on iOS simulator をクリックしてください。 デバイスが起動し、「AwesomeProject」が実行されて変更した文字列が表記されれば成功です。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SwiftUIに内包されているViewControl系Structまとめ

はじめに SwiftUIフレームワークに内包されている構造体の内、頻繁に利用する構造体をまとめようと思います。 Views Controls ☆本記事ではここが対象 Layout Paints Other Modifiers Controls Effects Layout Text Image List Styles Accessibility Events Gestures Shape Modifiers Other など 今後残りの部分についてもまとめていければと思っています。 また、端末で動作確認できるようにもしているので、もし興味があればローカルで端末にビルドいただければと思います。 ソースコード Views Controls Button @State var number = 0 var body: some View { VStack { Text("\(number)") .padding() Button(action: { number = number + 1 }) { Text("DefaultButtonStyle") }.buttonStyle(DefaultButtonStyle()) Button(action: { number = number + 1 }) { Text("PlainButtonStyle") }.buttonStyle(PlainButtonStyle()) Button(action: { number = number + 1 }) { Text("BorderlessButtonStyle") }.buttonStyle(BorderlessButtonStyle()) } } ColorPicker @State private var selectedColor = Color.red var body: some View { ColorPicker("ColorPicker", selection: $selectedColor) .frame(width: 150) } DatePicker @State private var date = Date() var body: some View { VStack { DatePicker(selection: $date, displayedComponents: [.date]) { Text("displayedComponents .date") }.padding() DatePicker(selection: $date, displayedComponents: [.hourAndMinute]) { Text("displayedComponents .hourAndMinute") }.padding() } } @State private var date = Date() var body: some View { ScrollView(.vertical){ VStack { DatePicker(selection: $date) { Text("CompactDatePickerStyle") }.datePickerStyle(CompactDatePickerStyle()) DatePicker(selection: $date) { Text("GraphicalDatePickerStyle") }.datePickerStyle(GraphicalDatePickerStyle()) DatePicker(selection: $date) { Text("WheelDatePickerStyle") }.datePickerStyle(WheelDatePickerStyle()) DatePicker(selection: $date) { Text("DefaultDatePickerStyle") }.datePickerStyle(DefaultDatePickerStyle()) } } } DisclosureGroup struct ToggleStates { var oneIsOn: Bool = false var twoIsOn: Bool = true } @State private var toggleStates = ToggleStates() @State private var topExpanded: Bool = true var body: some View { VStack { DisclosureGroup("Items", isExpanded: $topExpanded) { Toggle("Toggle 1", isOn: $toggleStates.oneIsOn) Toggle("Toggle 2", isOn: $toggleStates.twoIsOn) DisclosureGroup("Sub-items") { Text("Sub-item 1") } }.padding() } } EditButton @Environment(\.editMode) var editMode @State private var fruits = [ "Apple", "Banana", "Papaya", "Mango" ] var body: some View { VStack { NavigationView{ List { ForEach( fruits, id: \.self ) { fruit in Text(fruit) } .onDelete { self.fruits.remove(atOffsets: $0) } .onMove { self.fruits.move(fromOffsets: $0, toOffset: $1) } } .navigationTitle("Fruits") .toolbar { EditButton() } } Text(String(describing: editMode?.wrappedValue)) } } Form @State var toggleState = false var body: some View { VStack { Form { Section(header: Text("Text")) { Text("item 1") Text("item 2") } Section(header: Text("Toggle")) { Toggle(isOn: $toggleState) { Text("item 3") } } Section(header: Text("Button")) { Button(action: {}) { Text("item 4") } } } } } GroupBox var body: some View { GroupBox( label: Label("Heart Rate", systemImage: "heart.fill").foregroundColor(.red) ) { Text("Your hear rate is 90 BPM.") }.padding() Label var body: some View { VStack { Label("Label", systemImage: "42.circle") .padding() Label("Label: TitleOnlyLabelStyle", systemImage: "bolt.fill") .padding() .labelStyle(TitleOnlyLabelStyle()) Label("Label: IconOnlyLabelStyle", systemImage: "bolt.fill") .padding() .labelStyle(IconOnlyLabelStyle()) Label("Label: TitleAndIconLabelStyle", systemImage: "bolt.fill") .padding() .labelStyle(TitleAndIconLabelStyle()) Label { Text("bbbb") .font(.body) .foregroundColor(.primary) Text("cccc") .font(.subheadline) .foregroundColor(.secondary) } icon: { Circle() .fill(Color.red) .frame(width: 44, height: 44, alignment: .center) .overlay(Text("aaa")) } } Link var body: some View { Link(destination: URL(string: "https://www.google.com/?hl=ja")!) { Text("googleを開く") }.padding() .font(.title) .foregroundColor(.green) } Navigation var body: some View { VStack { NavigationView { List { NavigationLink(destination: ButtonView()) { Text("Button Page") } NavigationLink(destination: ColorPickerView()) { Text("ColorPicker Page") } } }.navigationViewStyle(StackNavigationViewStyle()) NavigationView { List { NavigationLink(destination: ButtonView()) { Text("Button Page") } NavigationLink(destination: ColorPickerView()) { Text("ColorPicker Page") } } }.navigationViewStyle(DoubleColumnNavigationViewStyle()) }.navigationBarTitle(Text("Navigation")) OutlineGroup struct FileItem: Hashable, Identifiable, CustomStringConvertible { var id: Self { self } var name: String var children: [FileItem]? = nil var description: String { switch children { case nil: return "? \(name)" case .some(let children): return children.isEmpty ? "? \(name)" : "? \(name)" } } } let data = FileItem(name: "users", children: [FileItem(name: "user1234", children: [FileItem(name: "Photos", children: [FileItem(name: "photo001.jpg"), FileItem(name: "photo002.jpg")]), FileItem(name: "Movies", children: [FileItem(name: "movie001.mp4")]), FileItem(name: "Documents", children: []) ]), FileItem(name: "newuser", children: [FileItem(name: "Documents", children: []) ]) ]) var body: some View { VStack { OutlineGroup(data, id: \.id, children: \.children) { item in Text("\(item.description)") }.padding() } } Picker enum Flavor: String, CaseIterable, Identifiable { case chocolate case vanilla case strawberry var id: String { self.rawValue } } enum Topping: String, CaseIterable, Identifiable { case nuts case cookies case blueberries var id: String { self.rawValue } } extension Flavor { var suggestedTopping: Topping { switch self { case .chocolate: return .nuts case .vanilla: return .cookies case .strawberry: return .blueberries } } } struct PickerView: View { @State private var selectedFlavor = Flavor.chocolate @State private var suggestedTopping: Topping = .cookies var body: some View { VStack { Picker(selection: $selectedFlavor, label: Text("Flavor")) { ForEach(Flavor.allCases) { flavor in Text(flavor.rawValue.capitalized) } } Text("Selected flavor: \(selectedFlavor.rawValue)") Divider() Picker(selection: $selectedFlavor, label: Text("Suggest a topping for:")) { ForEach(Flavor.allCases) { flavor in Text(flavor.rawValue.capitalized).tag(flavor.suggestedTopping) } } Text("suggestedTopping: \(suggestedTopping.rawValue)") } } } @State private var selectedFlavor1 = Flavor.chocolate @State private var selectedFlavor2 = Flavor.chocolate @State private var selectedFlavor3 = Flavor.chocolate @State private var selectedFlavor4 = Flavor.chocolate var body: some View { ScrollView(.vertical) { VStack { Picker(selection: $selectedFlavor1, label: Text("Flavor: MenuPickerStyle")) { Text("Chocolate").tag(Flavor.chocolate) Text("Vanilla").tag(Flavor.vanilla) Text("Strawberry").tag(Flavor.strawberry) }.pickerStyle(MenuPickerStyle()) Text("Selected flavor: \(selectedFlavor1.rawValue)") Picker(selection: $selectedFlavor2, label: Text("Flavor: WheelPickerStyle")) { Text("Chocolate").tag(Flavor.chocolate) Text("Vanilla").tag(Flavor.vanilla) Text("Strawberry").tag(Flavor.strawberry) }.pickerStyle(WheelPickerStyle()) Text("WheelPickerStyle Selected flavor: \(selectedFlavor2.rawValue)") Picker(selection: $selectedFlavor3, label: Text("Flavor: WheelPickerStyle")) { Text("Chocolate").tag(Flavor.chocolate) Text("Vanilla").tag(Flavor.vanilla) Text("Strawberry").tag(Flavor.strawberry) }.pickerStyle(InlinePickerStyle()) Text("InlinePickerStyle Selected flavor: \(selectedFlavor3.rawValue)") Spacer(minLength: 50) Picker(selection: $selectedFlavor4, label: Text("Flavor: SegmentedPickerStyle")) { Text("Chocolate").tag(Flavor.chocolate) Text("Vanilla").tag(Flavor.vanilla) Text("Strawberry").tag(Flavor.strawberry) }.pickerStyle(SegmentedPickerStyle()) Text("SegmentedPickerStyle Selected flavor: \(selectedFlavor4.rawValue)") } } ProgressView @State private var progress = 0.0 private let total = 1.0 var body: some View { VStack { ProgressView("DefaultProgressViewStyle", value: progress, total: total) .padding() Button("More", action: { if (progress + 0.05) < total { progress += 0.05 } else { progress = total } }) ProgressView("LinearProgressViewStyle", value: 0.25, total: total) .progressViewStyle(LinearProgressViewStyle()) .padding() ProgressView("CircularProgressViewStyle", value: 0.75, total: total) .progressViewStyle(CircularProgressViewStyle()) .padding() } } ScrollView VStack { ScrollView(.horizontal) { Text(".horizontal test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test") }.padding() ScrollView(.horizontal, showsIndicators: false) { Text(".horizontal showIndicators: false test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test") }.padding() ScrollView { ScrollViewReader { value in Button("Jump to 30") { withAnimation { value.scrollTo(30, anchor: .top) } } ForEach(1..<51) { index in Image(systemName: "\(index).square") .font(.largeTitle) .frame(height: 70) .id(index) } } } } Section @State private var selectedValue = "C++" var body: some View { VStack { List { Section(header: Text("header 果物"), footer: Text("footer 果物")) { Text("桃") Text("りんご") Text("みかん") } Section(header: Text("header 野菜"), footer: Text("footer 野菜")) { Text("きゅうり") Text("トマト") Text("なす") } }.frame(height:450) Form { Section(header: Text("header プログラミング言語"), footer: Text("footer プログラミング言語")) { Picker("プログラミング言語", selection: $selectedValue) { Text("C++").tag("C++") Text("Kotlin").tag("Kotlin") Text("Swift").tag("Swift") } } } } } SecureField / TextField @State private var username: String = "" @State private var password: String = "" var body: some View { VStack { TextField( "User name (email address)", text: $username) .autocapitalization(.none) .disableAutocorrection(true) .border(Color(UIColor.separator)) .padding() SecureField( "Password", text: $password ) { print("ログイン") } .border(Color(UIColor.separator)) .padding() } } Scroll @State private var speed = 50.0 @State private var isEditing = false var body: some View { VStack { Slider( value: $speed, in: 0...100, step: 5, onEditingChanged: { editing in isEditing = editing }, minimumValueLabel: Text("0"), maximumValueLabel: Text("100") ) { Text("Speed") } Text("\(speed)") .foregroundColor(isEditing ? .red : .blue) }.padding() } Stepper @State private var value1 = 0 @State private var value2 = 0 let colors: [Color] = [.orange, .red, .gray, .blue, .green, .purple, .pink] func incrementStep() { value1 += 1 if value1 >= colors.count { value1 = 0 } } func decrementStep() { value1 -= 1 if value1 < 0 { value1 = colors.count - 1 } } var body: some View { VStack { Stepper(onIncrement: incrementStep, onDecrement: decrementStep) { Text("Value: \(value1) Color: \(colors[value1].description)") } .padding(10) .background(colors[value1]) Stepper(value: $value2, in: 1...50, step: 5) { Text("Current: \(value2) in 1...50 stepping by 5") }.padding(10) } } TabView @State private var selection = 1 var body: some View { VStack { Text("selection: \(selection)") TabView(selection: $selection) { Text("The First Tab") .tabItem { Image(systemName: "1.square.fill") Text("First") } .tag(1) Text("Another Tab") .tabItem { Image(systemName: "2.square.fill") Text("Second") } .tag(2) Text("The Last Tab") .tabItem { Image(systemName: "3.square.fill") Text("Third") } .tag(3) } .font(.headline) } } Text var body: some View { VStack { Text("Hamlet") .font(.title) .padding() Text("by William Shakespeare") .font(.system(size: 12, weight: .light, design: .serif)) .italic() .padding() Text("Brevity is the soul of wit.") .frame(width: 100) .lineLimit(1) .padding() } } TextEditor @State private var fullText: String = "This is some editable text..." var body: some View { TextEditor(text: $fullText) .foregroundColor(Color.gray) .font(.custom("HelveticaNeue", size: 18)) .lineSpacing(5) } Toggle @State private var vibrateOnRing = true var body: some View { Toggle("Vibrate on Ring", isOn: $vibrateOnRing) .toggleStyle(SwitchToggleStyle()) .padding() } 最後に 最後まで閲覧いただきありがとうございました。 今後時間を見つけて残りのStructについてもまとめていく予定です。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SwiftUIに内包されているStructまとめ - ViewControl編

はじめに SwiftUIフレームワークに内包されている構造体の内、頻繁に利用する構造体をまとめようと思います。 Views Controls ☆本記事ではここが対象 Layout Paints Other Modifiers Controls Effects Layout Text Image List Styles Accessibility Events Gestures Shape Modifiers Other など 今後残りの部分についてもまとめていければと思っています。 また、端末で動作確認できるようにもしているので、もし興味があればローカルで端末にビルドいただければと思います。 ソースコード Views Controls Button @State var number = 0 var body: some View { VStack { Text("\(number)") .padding() Button(action: { number = number + 1 }) { Text("DefaultButtonStyle") }.buttonStyle(DefaultButtonStyle()) Button(action: { number = number + 1 }) { Text("PlainButtonStyle") }.buttonStyle(PlainButtonStyle()) Button(action: { number = number + 1 }) { Text("BorderlessButtonStyle") }.buttonStyle(BorderlessButtonStyle()) } } ColorPicker @State private var selectedColor = Color.red var body: some View { ColorPicker("ColorPicker", selection: $selectedColor) .frame(width: 150) } DatePicker @State private var date = Date() var body: some View { VStack { DatePicker(selection: $date, displayedComponents: [.date]) { Text("displayedComponents .date") }.padding() DatePicker(selection: $date, displayedComponents: [.hourAndMinute]) { Text("displayedComponents .hourAndMinute") }.padding() } } @State private var date = Date() var body: some View { ScrollView(.vertical){ VStack { DatePicker(selection: $date) { Text("CompactDatePickerStyle") }.datePickerStyle(CompactDatePickerStyle()) DatePicker(selection: $date) { Text("GraphicalDatePickerStyle") }.datePickerStyle(GraphicalDatePickerStyle()) DatePicker(selection: $date) { Text("WheelDatePickerStyle") }.datePickerStyle(WheelDatePickerStyle()) DatePicker(selection: $date) { Text("DefaultDatePickerStyle") }.datePickerStyle(DefaultDatePickerStyle()) } } } DisclosureGroup struct ToggleStates { var oneIsOn: Bool = false var twoIsOn: Bool = true } @State private var toggleStates = ToggleStates() @State private var topExpanded: Bool = true var body: some View { VStack { DisclosureGroup("Items", isExpanded: $topExpanded) { Toggle("Toggle 1", isOn: $toggleStates.oneIsOn) Toggle("Toggle 2", isOn: $toggleStates.twoIsOn) DisclosureGroup("Sub-items") { Text("Sub-item 1") } }.padding() } } EditButton @Environment(\.editMode) var editMode @State private var fruits = [ "Apple", "Banana", "Papaya", "Mango" ] var body: some View { VStack { NavigationView{ List { ForEach( fruits, id: \.self ) { fruit in Text(fruit) } .onDelete { self.fruits.remove(atOffsets: $0) } .onMove { self.fruits.move(fromOffsets: $0, toOffset: $1) } } .navigationTitle("Fruits") .toolbar { EditButton() } } Text(String(describing: editMode?.wrappedValue)) } } Form @State var toggleState = false var body: some View { VStack { Form { Section(header: Text("Text")) { Text("item 1") Text("item 2") } Section(header: Text("Toggle")) { Toggle(isOn: $toggleState) { Text("item 3") } } Section(header: Text("Button")) { Button(action: {}) { Text("item 4") } } } } } GroupBox var body: some View { GroupBox( label: Label("Heart Rate", systemImage: "heart.fill").foregroundColor(.red) ) { Text("Your hear rate is 90 BPM.") }.padding() Label var body: some View { VStack { Label("Label", systemImage: "42.circle") .padding() Label("Label: TitleOnlyLabelStyle", systemImage: "bolt.fill") .padding() .labelStyle(TitleOnlyLabelStyle()) Label("Label: IconOnlyLabelStyle", systemImage: "bolt.fill") .padding() .labelStyle(IconOnlyLabelStyle()) Label("Label: TitleAndIconLabelStyle", systemImage: "bolt.fill") .padding() .labelStyle(TitleAndIconLabelStyle()) Label { Text("bbbb") .font(.body) .foregroundColor(.primary) Text("cccc") .font(.subheadline) .foregroundColor(.secondary) } icon: { Circle() .fill(Color.red) .frame(width: 44, height: 44, alignment: .center) .overlay(Text("aaa")) } } Link var body: some View { Link(destination: URL(string: "https://www.google.com/?hl=ja")!) { Text("googleを開く") }.padding() .font(.title) .foregroundColor(.green) } Navigation var body: some View { VStack { NavigationView { List { NavigationLink(destination: ButtonView()) { Text("Button Page") } NavigationLink(destination: ColorPickerView()) { Text("ColorPicker Page") } } }.navigationViewStyle(StackNavigationViewStyle()) NavigationView { List { NavigationLink(destination: ButtonView()) { Text("Button Page") } NavigationLink(destination: ColorPickerView()) { Text("ColorPicker Page") } } }.navigationViewStyle(DoubleColumnNavigationViewStyle()) }.navigationBarTitle(Text("Navigation")) OutlineGroup struct FileItem: Hashable, Identifiable, CustomStringConvertible { var id: Self { self } var name: String var children: [FileItem]? = nil var description: String { switch children { case nil: return "? \(name)" case .some(let children): return children.isEmpty ? "? \(name)" : "? \(name)" } } } let data = FileItem(name: "users", children: [FileItem(name: "user1234", children: [FileItem(name: "Photos", children: [FileItem(name: "photo001.jpg"), FileItem(name: "photo002.jpg")]), FileItem(name: "Movies", children: [FileItem(name: "movie001.mp4")]), FileItem(name: "Documents", children: []) ]), FileItem(name: "newuser", children: [FileItem(name: "Documents", children: []) ]) ]) var body: some View { VStack { OutlineGroup(data, id: \.id, children: \.children) { item in Text("\(item.description)") }.padding() } } Picker enum Flavor: String, CaseIterable, Identifiable { case chocolate case vanilla case strawberry var id: String { self.rawValue } } enum Topping: String, CaseIterable, Identifiable { case nuts case cookies case blueberries var id: String { self.rawValue } } extension Flavor { var suggestedTopping: Topping { switch self { case .chocolate: return .nuts case .vanilla: return .cookies case .strawberry: return .blueberries } } } struct PickerView: View { @State private var selectedFlavor = Flavor.chocolate @State private var suggestedTopping: Topping = .cookies var body: some View { VStack { Picker(selection: $selectedFlavor, label: Text("Flavor")) { ForEach(Flavor.allCases) { flavor in Text(flavor.rawValue.capitalized) } } Text("Selected flavor: \(selectedFlavor.rawValue)") Divider() Picker(selection: $selectedFlavor, label: Text("Suggest a topping for:")) { ForEach(Flavor.allCases) { flavor in Text(flavor.rawValue.capitalized).tag(flavor.suggestedTopping) } } Text("suggestedTopping: \(suggestedTopping.rawValue)") } } } @State private var selectedFlavor1 = Flavor.chocolate @State private var selectedFlavor2 = Flavor.chocolate @State private var selectedFlavor3 = Flavor.chocolate @State private var selectedFlavor4 = Flavor.chocolate var body: some View { ScrollView(.vertical) { VStack { Picker(selection: $selectedFlavor1, label: Text("Flavor: MenuPickerStyle")) { Text("Chocolate").tag(Flavor.chocolate) Text("Vanilla").tag(Flavor.vanilla) Text("Strawberry").tag(Flavor.strawberry) }.pickerStyle(MenuPickerStyle()) Text("Selected flavor: \(selectedFlavor1.rawValue)") Picker(selection: $selectedFlavor2, label: Text("Flavor: WheelPickerStyle")) { Text("Chocolate").tag(Flavor.chocolate) Text("Vanilla").tag(Flavor.vanilla) Text("Strawberry").tag(Flavor.strawberry) }.pickerStyle(WheelPickerStyle()) Text("WheelPickerStyle Selected flavor: \(selectedFlavor2.rawValue)") Picker(selection: $selectedFlavor3, label: Text("Flavor: WheelPickerStyle")) { Text("Chocolate").tag(Flavor.chocolate) Text("Vanilla").tag(Flavor.vanilla) Text("Strawberry").tag(Flavor.strawberry) }.pickerStyle(InlinePickerStyle()) Text("InlinePickerStyle Selected flavor: \(selectedFlavor3.rawValue)") Spacer(minLength: 50) Picker(selection: $selectedFlavor4, label: Text("Flavor: SegmentedPickerStyle")) { Text("Chocolate").tag(Flavor.chocolate) Text("Vanilla").tag(Flavor.vanilla) Text("Strawberry").tag(Flavor.strawberry) }.pickerStyle(SegmentedPickerStyle()) Text("SegmentedPickerStyle Selected flavor: \(selectedFlavor4.rawValue)") } } ProgressView @State private var progress = 0.0 private let total = 1.0 var body: some View { VStack { ProgressView("DefaultProgressViewStyle", value: progress, total: total) .padding() Button("More", action: { if (progress + 0.05) < total { progress += 0.05 } else { progress = total } }) ProgressView("LinearProgressViewStyle", value: 0.25, total: total) .progressViewStyle(LinearProgressViewStyle()) .padding() ProgressView("CircularProgressViewStyle", value: 0.75, total: total) .progressViewStyle(CircularProgressViewStyle()) .padding() } } ScrollView VStack { ScrollView(.horizontal) { Text(".horizontal test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test") }.padding() ScrollView(.horizontal, showsIndicators: false) { Text(".horizontal showIndicators: false test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test") }.padding() ScrollView { ScrollViewReader { value in Button("Jump to 30") { withAnimation { value.scrollTo(30, anchor: .top) } } ForEach(1..<51) { index in Image(systemName: "\(index).square") .font(.largeTitle) .frame(height: 70) .id(index) } } } } Section @State private var selectedValue = "C++" var body: some View { VStack { List { Section(header: Text("header 果物"), footer: Text("footer 果物")) { Text("桃") Text("りんご") Text("みかん") } Section(header: Text("header 野菜"), footer: Text("footer 野菜")) { Text("きゅうり") Text("トマト") Text("なす") } }.frame(height:450) Form { Section(header: Text("header プログラミング言語"), footer: Text("footer プログラミング言語")) { Picker("プログラミング言語", selection: $selectedValue) { Text("C++").tag("C++") Text("Kotlin").tag("Kotlin") Text("Swift").tag("Swift") } } } } } SecureField / TextField @State private var username: String = "" @State private var password: String = "" var body: some View { VStack { TextField( "User name (email address)", text: $username) .autocapitalization(.none) .disableAutocorrection(true) .border(Color(UIColor.separator)) .padding() SecureField( "Password", text: $password ) { print("ログイン") } .border(Color(UIColor.separator)) .padding() } } Scroll @State private var speed = 50.0 @State private var isEditing = false var body: some View { VStack { Slider( value: $speed, in: 0...100, step: 5, onEditingChanged: { editing in isEditing = editing }, minimumValueLabel: Text("0"), maximumValueLabel: Text("100") ) { Text("Speed") } Text("\(speed)") .foregroundColor(isEditing ? .red : .blue) }.padding() } Stepper @State private var value1 = 0 @State private var value2 = 0 let colors: [Color] = [.orange, .red, .gray, .blue, .green, .purple, .pink] func incrementStep() { value1 += 1 if value1 >= colors.count { value1 = 0 } } func decrementStep() { value1 -= 1 if value1 < 0 { value1 = colors.count - 1 } } var body: some View { VStack { Stepper(onIncrement: incrementStep, onDecrement: decrementStep) { Text("Value: \(value1) Color: \(colors[value1].description)") } .padding(10) .background(colors[value1]) Stepper(value: $value2, in: 1...50, step: 5) { Text("Current: \(value2) in 1...50 stepping by 5") }.padding(10) } } TabView @State private var selection = 1 var body: some View { VStack { Text("selection: \(selection)") TabView(selection: $selection) { Text("The First Tab") .tabItem { Image(systemName: "1.square.fill") Text("First") } .tag(1) Text("Another Tab") .tabItem { Image(systemName: "2.square.fill") Text("Second") } .tag(2) Text("The Last Tab") .tabItem { Image(systemName: "3.square.fill") Text("Third") } .tag(3) } .font(.headline) } } Text var body: some View { VStack { Text("Hamlet") .font(.title) .padding() Text("by William Shakespeare") .font(.system(size: 12, weight: .light, design: .serif)) .italic() .padding() Text("Brevity is the soul of wit.") .frame(width: 100) .lineLimit(1) .padding() } } TextEditor @State private var fullText: String = "This is some editable text..." var body: some View { TextEditor(text: $fullText) .foregroundColor(Color.gray) .font(.custom("HelveticaNeue", size: 18)) .lineSpacing(5) } Toggle @State private var vibrateOnRing = true var body: some View { Toggle("Vibrate on Ring", isOn: $vibrateOnRing) .toggleStyle(SwitchToggleStyle()) .padding() } 最後に 最後まで閲覧いただきありがとうございました。 今後時間を見つけて残りのStructについてもまとめていく予定です。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Flutter】ATT(Application Tracking Transparency)対応

リジェクト内容 May 3, 2021 at 3:38 AM From Apple 5. 1.2 Legal: Privacy - Data Use and Sharing Guideline 5.1.2 - Legal - Privacy - Data Use and Sharing We noticed you do not use App Tracking Transparency to request the user's permission before tracking their activity across apps and websites. The app privacy information you provided in App Store Connect indicates you collect data in order to track the user, including Performance Data, Crash Data, Advertising Data, Product Interaction, and Other Diagnostic Data. Starting with iOS 14.5, apps on the App Store need to receive the user’s permission through the AppTrackingTransparency framework before collecting data used to track them. This requirement protects the privacy of App Store users. Next Steps Here are two ways to resolve this issue: You can remove the tracking functionality from your app and update your app privacy information in App Store Connect. If you decide to continue tracking users, you must implement App Tracking Transparency and request permission before collecting data used to track the user or device. Resources See Frequently Asked Questions about the new requirements for apps that track users. Learn more about designing appropriate permission requests. Bug Fix Submissions If this is a bug fix submission and you'd like to have it approved at this time, reply to this message in Resolution Center to let us know. You do not need to resubmit your app for us to proceed. Alternatively, if you'd like to resolve these issues now, please feel free to resubmit. Let us know if you have any questions about the issues we found in our review. You may appeal your app rejection if you believe this decision was made incorrectly. We also invite you to provide feedback on our review guidelines. ATT(Application Tracking Transparency)とは iOS 14.5、iPadOS 14.5、tvOS 14.5以降では、ユーザーをトラッキングしたり、ユーザーのデバイスの広告識別子にアクセスしたりする際には、AppTrackingTransparencyのフレームワークを通じてユーザーの許可を得る必要があります。トラッキングとは、自分のAppで収集したユーザーやデバイスに関するデータを、ターゲット広告や広告効果測定を目的として、他社のApp、Webサイト、またはオフラインのプロパティから収集されたユーザーやデバイスに関するデータに紐付ける行為を指します。また、ユーザーやデバイスに関するデータをデータブローカーに共有することもトラッキングに該当します。 対応方法 app_tracking_transparencyを利用します。 導入 pubspec.yaml app_tracking_transparency: info.plistにConfigの追加 info.plist <key>SKAdNetworkItems</key> <array> <dict> <key>SKAdNetworkIdentifier</key> <string>cstr6suwn9.skadnetwork</string> </dict> </array> info.plist <key>NSUserTrackingUsageDescription</key> <string>パーソナライズされた広告の表示のために使用いたします。</string> ATTリクエスト用の関数作成 import 'package:app_tracking_transparency/app_tracking_transparency.dart'; //・・・省略 Future<void> confirmATTStatus() async { final status = await AppTrackingTransparency.requestTrackingAuthorization(); print('ATT Status = $status'); } 実装結果 以下のように正しくダイアログが表示されました。 参考文献
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

iPhone/iOSの画面下部のバーを消す

override var prefersHomeIndicatorAutoHidden: Bool { return true } ? フリーランスエンジニアです。 お仕事のご相談こちらまで rockyshikoku@gmail.com Core MLを使ったアプリを作っています。 機械学習関連の情報を発信しています。 Twitter Medium
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Swift】IBOutlet接続したプロパティのdidset内に直接遷移を実装すると画面が表示されない理由

結論 プロパティオブザーバーを利用して画面遷移を行う際は、イベントコントロールで正しく行えるようにする必要がある。直接遷移をさせずに、addTargetメソッドなどを用いる。 理由 IBOutletが接続されているViewに遷移されると、イニシャライズされて変数にIB部品のインスタンスが入ります。その際にプロパティオブザーバーとしてのdidsetが実行されるが、このdidset内にNavigationControllerによる別の画面への遷移を直接実装してしまうと、画面が遷移されてきたにも関わらず、画面を遷移するという状態になるため、意図している形には正しく遷移されない、もしくはエラーが生じる。 このエラーはaddTargetメソッドでイベントコントロールを図ることで解決できる。実行のタイミングを.touchUpInside、つまり枠内をタッチするイベントのような形にすることで、didset内に記述されていてもインスタンス生成時に実行されなくなる。 そして、実際の遷移はaddTargetメソッドでセレクタをし、objc属性のfunctionとしてNavigationControllerによる遷移を実装するということになる。 例 didset内に遷移を直接実装した場合 final class ViewController: UIViewController { @IBOutlet private weak var reStartButton: UIButton! { didSet { RootViewContorller.root.reStart() } } } //RootViewController.swift final class RootViewContorller { static let root: RootViewContorller = .init() private init(){} func reStart(){ UserDefaults.standard.isLogined = [true, false].randomElement()! showInitialView(window: window) } } この場合は次のようなDebugが表示される 2021-05-08 10:33:25.508263+0900 RootViewControllerTest[13610:776213] Unbalanced calls to begin/end appearance transitions for < RootViewControllerTest.SecondViewContorller: 0x7fd8bb524200 >. メッセージにあるように、遷移画面を起動/終了時の呼び出しが不安定ということ。遷移しても、即座に別の画面に遷移するような状態にあり、結果的に画面が表示されないためこのようなメッセージが表示されている。 didset内でaddTargetを利用した場合 final class ViewController: UIViewController { @IBOutlet private weak var reStartButton: UIButton! { didSet { reStartButton.addTarget(self, action: #selector(tapReStartButton(_:)), for: .touchUpInside) } } @objc private func tapReStartButton(_ sender: UIResponder) { RootViewContorller.root.reStart() } } //RootViewController.swift final class RootViewContorller { static let root: RootViewContorller = .init() private init(){} func reStart(){ UserDefaults.standard.isLogined = [true, false].randomElement()! showInitialView(window: window) } } この記述だと、プロパティオブザーバーとしてdidset内はインスタンス生成に実行されるが、addTargetメソッドは.touchUpInsideというIBパーツの枠内をタッチした場合に実行されるような形になっているため、遷移は実行さレナい。addTargetでは遷移の中身はactionパラメータのセレクターとして指定され、objc属性以下に書かれる。 addTargetメソッドは別のQiitaにまとめています。 まとめ プロパティオブザーバーを利用して画面遷移を行う際は、イベントコントロールで正しく行えるようにする必要がある。直接遷移をさせずに、addTargetメソッドなどを用いる。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む