- 投稿日:2020-05-24T23:43:55+09:00
【iOS】XCUIElementTypeQueryProviderの定義済みクエリとUI要素の対応
XCUITestでUITestを作成する場合、UI要素の検索でXCUIElementTypeQueryProviderの定義済みクエリを
使うこともあると思います。
定義済みクエリを使用する時、クエリと実際のUI要素との対応が一部曖昧な部分があったので、
よく使いそうなUI要素との対応を備忘録的に記録しておこうと思います。UILabel
func test_UILabelを取得() { let label = app.staticTexts["UILabel"] // accessibility identifierを指定 XCTAssertTrue(label.exists) // 指定したaccessibility identifierのUI要素が存在するか確認 }UIImageView
func test_UIImageViewを取得() { let imageView = app.images["UIImageView"] XCTAssertTrue(imageView.exists) }UITextField(secureTextEntryではない)
func test_UITextFieldを取得() { let textField = app.textFields["UITextField"] XCTAssertTrue(textField.exists) }UITextField(secureTextEntryである)
func test_secureTextEntryのUITextFieldを取得() { let secureTextField = app.secureTextFields["UITextField_Secure"] XCTAssertTrue(secureTextField.exists) }UIButton
func test_UIButtonを取得() { let button = app.buttons["UIButton"] XCTAssertTrue(button.exists) }UITextView
func test_UITextViewを取得() { let textView = app.textViews["UITextView"] XCTAssertTrue(textView.exists) }UISegmentedControl
func test_UISegmentedControlを取得() { let segmentedControl = app.segmentedControls["SegmentedControl"] XCTAssertTrue(segmentedControl.exists) }UISlider
func test_UISliderを取得() { let slider = app.sliders["UISlider"] XCTAssertTrue(slider.exists) }UISwitch
func test_UISwitchを取得() { let uiSwitch = app.switches["UISwitch"] XCTAssertTrue(uiSwitch.exists) }UIView
func test_UIViewを取得() { let view = app.otherElements["UIView"] XCTAssertTrue(view.exists) }終わりに
上記のUI以外のクエリは以下のリンクをご参照ください。
定義済みクエリを全て確認できます。
クエリはiOS, macOSの区別なく定義されているので一部iOSでは使えないものもあります。(touchBarなど)
https://developer.apple.com/documentation/xctest/xcuielementtypequeryprovider
- 投稿日:2020-05-24T23:01:11+09:00
R.Swiftでビルドエラー(Pods/R.swift/rswift: No such file or directory)が発生
事象
これです。よく見るやつですね。
Pods/R.swift/rswift: No such file or directory Command PhaseScriptExecution failed with a nonzero exit codeGoogle先生に聞けばCleanすれば直るとか諸々削除してpod installすれば直るとかBuildConfigの設定が間違っているとか、色々な解消法が出てくるかと思います。
今回、数分前までBuildできていたものが、いきなりBuildできなくなり
一般的な解消方法を全部やってもダメで、数時間潰しました。■一般的な方法 (引用元 https://qiita.com/masa-321/items/19f66557dcff65553f18)
・Clean(Command + Shift + K) ・DerivedDataファイル以下全ての中間ファイル(キャッシュ?)を削除 参考:[Xcode][小ネタ] DerivedDataの削除についての備忘録 ・Podfileを編集して、Podの再インストール。 ・最近アップデートしたライブラリがあれば、ダウングレードして試してみる。 ・Cocoa Podsのアップデート ・Carthageのアップデート ・キーチェーンのログインをロック&解除を1度繰り返す ・古いMacbookの証明書をコピー 参考:iOSアプリ開発で実機による開発を複数台(メイン機ではない2台目以降)のMacで行いたい場合 ・Build Phases>Run Script>Shellにて、タイピングミスがないか(余計なスペースが入っているケースがある)・・・① ・Xcodeの再起動 ・Macbook本体の再起動原因
アンチウイルスソフトが生成された
Pods/R.swift/rswift
を生成される度に消してました。
F**k
- 投稿日:2020-05-24T23:01:11+09:00
R.swiftでビルドエラー(Pods/R.swift/rswift: No such file or directory)が発生
事象
これです。よく見るやつですね。
Pods/R.swift/rswift: No such file or directory Command PhaseScriptExecution failed with a nonzero exit codeGoogle先生に聞けばCleanすれば直るとか諸々削除してpod installすれば直るとかBuildConfigの設定が間違っているとか、色々な解消法が出てくるかと思います。
今回、数分前までBuildできていたものが、いきなりBuildできなくなり
一般的な解消方法を全部やってもダメで、数時間潰しました。■一般的な方法 (引用元 https://qiita.com/masa-321/items/19f66557dcff65553f18)
・Clean(Command + Shift + K) ・DerivedDataファイル以下全ての中間ファイル(キャッシュ?)を削除 参考:[Xcode][小ネタ] DerivedDataの削除についての備忘録 ・Podfileを編集して、Podの再インストール。 ・最近アップデートしたライブラリがあれば、ダウングレードして試してみる。 ・Cocoa Podsのアップデート ・Carthageのアップデート ・キーチェーンのログインをロック&解除を1度繰り返す ・古いMacbookの証明書をコピー 参考:iOSアプリ開発で実機による開発を複数台(メイン機ではない2台目以降)のMacで行いたい場合 ・Build Phases>Run Script>Shellにて、タイピングミスがないか(余計なスペースが入っているケースがある)・・・① ・Xcodeの再起動 ・Macbook本体の再起動原因
アンチウイルスソフトが生成された
Pods/R.swift/rswift
を生成される度に消してました。
F**k
- 投稿日:2020-05-24T22:41:57+09:00
iOSのBageを標準とは違う位置に無理やりつける方法
ごくまれに、TabBarのバッチの位置をカスタムしたいとお願いされることがあります。
無理やりバッチをつけるのはちょっとめんどくさいですが、上手くできたので、メモっておきます。class CustomTabViewController: UITabBarController { private var badges: [UIView] = [] override func viewDidLoad() { super.viewDidLoad() addBage(position: 3, text: "11", frame: CGRect(x: 30, y: 5, width: 18, height: 18)) } extension MainTabViewController { // バッチをつける private func addBage(position: Int, text:String, frame:CGRect) { guard tabBar.subviews.count > position else { return } let tabBarButton = tabBar.subviews[position] for subView in tabBarButton.subviews { guard let icon = subView as? UIImageView else { continue } let badge = createBage(text: text, frame: frame) icon.addSubview(badge) badges.append(badge) } } // バッチを全て剥がす private func removeAllBage() { badges.forEach { bage in bage.removeFromSuperview() } } // バッジを生成 private func createBage(text: String, frame: CGRect) -> UIView { let bageView = UILabel(frame: frame) bageView.text = text bageView.textAlignment = .center bageView.font = .systemFont(ofSize: 13) bageView.adjustsFontSizeToFitWidth = true bageView.minimumScaleFactor = 0.5 bageView.textColor = .white bageView.layer.cornerRadius = 9 bageView.layer.masksToBounds = true bageView.backgroundColor = .red return bageView } } }
- 投稿日:2020-05-24T13:14:22+09:00
[Swift / Xcode / Realm]Realm を利用した ToDoアプリの実装(ToDo の追加・編集・更新・削除)
この記事の内容
モバイル向けデータベース管理システム Realm を利用した、ToDo アプリの開発
参考にした記事
Realmを使ってTODOアプリを作ってみよう!/ Swift(Realmの使い方、初級編)
開発したアプリ
前掲の記事を参考にして、ToDo を追加しテーブルビューで表示することに加えて下記機能を実装した。
GitHub にもコードを UP しています。
GitHub - tanakadaichi1989・Remove All ボタン をクリックすると、ToDo を全て削除
・ToDo を表示しているテーブルビューのセルを左にスライドすると、その ToDo のみを削除
・ToDo を表示しているテーブルビューのセルをクリックすると、ToDo を編集するためのアラートコントローラを表示
・アラートコントローラの OKボタン をクリックすると、ToDo を更新
前提条件
① CocoaPods Realm の導入、テキストボックスに入力した文字を保存する処理を実装する方法は前掲の記事を参照してください。
② 今回のアプリは、Realm の利用方法を確認するために開発しました。その為、 Apple が策定した Human Interface Guidelines は考慮していません。
開発環境
項目 PC MacBook Air 2017 IDE Xcode Ver. 11.4.1 言語 Swift 5.2.2 データベース Realm コード管理 GitHub Realm とは
モバイル向けデータベース管理システム
Main.storyboard
項目 属性 備考 ViewCotroller UIViewController ViewCotroller.swift と紐付け Navigation Bar UINavigationBar Title を "TestApp" と設定 ToDoTextField UITextField このテキストフィールドに入力した文字を ToDo として保存 Add Button UIButton ボタンの色:青 角は丸く Remove All Button UIButton ボタンの色:赤 角は丸く ToDoTableView UITableView 保存されている ToDo を表示 testCell UITableViewCell Identifier を "testCell" と設定 コード
ToDoModel.swiftimport Foundation import RealmSwift class TodoModel: Object{ @objc dynamic var todo: String? = nil }ViewCotroller.swiftimport UIKit import RealmSwift class ViewController: UIViewController,UITextFieldDelegate,UITableViewDelegate { @IBOutlet weak var todoTextFiled: UITextField! @IBOutlet weak var todoTableView: UITableView! @IBOutlet weak var addButton: UIButton! @IBOutlet weak var removeAllButton: UIButton! var itemList: Results<TodoModel>! // Add ボタンをクリックした際に実行する処理 @IBAction func tapAddButton(_ sender: Any) { let instancedTodoModel:TodoModel = TodoModel() instancedTodoModel.todo = self.todoTextFiled.text let realmInstance = try! Realm() try! realmInstance.write{ realmInstance.add(instancedTodoModel) } self.todoTableView.reloadData() } // Remove All ボタンをクリックした際に実行する処理 @IBAction func tapRemoveAllButton(_ sender: Any) { let realmInstance = try! Realm() try! realmInstance.write{ realmInstance.deleteAll() } self.todoTableView.reloadData() } override func viewDidLoad() { super.viewDidLoad() // UITableViewDataSource を self に設定 self.todoTableView.dataSource = self // UITableViewDelegate を self に設定 self.todoTableView.delegate = self // ボタンの角を丸くする設定 addButton.layer.cornerRadius = 5 removeAllButton.layer.cornerRadius = 5 let realmInstance = try! Realm() self.itemList = realmInstance.objects(TodoModel.self) self.todoTableView.reloadData() } } extension ViewController: UITableViewDataSource{ func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return self.itemList.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let testCell:UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "testCell")! let item: TodoModel = self.itemList[(indexPath as NSIndexPath).row] testCell.textLabel?.text = item.todo return testCell } // テーブルビューの編集を許可 func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { return true } // テーブルビューのセルとデータを削除 func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { if editingStyle == UITableViewCell.EditingStyle.delete { // データを削除 let realmInstance = try! Realm() try! realmInstance.write { realmInstance.delete(itemList[indexPath.row]) } // セルを削除 tableView.deleteRows(at: [indexPath as IndexPath], with: UITableView.RowAnimation.automatic) } } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { print("セル\(indexPath)が選択されました") showAlertController(indexPath) } // テーブルビューのセルをクリックしたら、アラートコントローラを表示する処理 func showAlertController(_ indexPath: IndexPath){ let alertController: UIAlertController = UIAlertController(title: "\(String(indexPath.row))番目の ToDo を編集", message: itemList[indexPath.row].todo, preferredStyle: .alert) // アラートコントローラにテキストフィールドを表示 テキストフィールドには入力された情報を表示させておく処理 alertController.addTextField(configurationHandler: {(textField: UITextField!) in textField.text = self.itemList[indexPath.row].todo}) // アラートコントローラに"OK"ボタンを表示 "OK"ボタンをクリックした際に、テキストフィールドに入力した文字で更新する処理を実装 alertController.addAction(UIAlertAction(title: "OK", style: .default, handler: { (action) -> Void in self.updateAlertControllerText(alertController,indexPath) })) // アラートコントローラに"Cancel"ボタンを表示 alertController.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil)) self.present(alertController, animated: true, completion: nil) } // "OK"ボタンをクリックした際に、テキストフィールドに入力した文字で更新 func updateAlertControllerText(_ alertcontroller:UIAlertController, _ indexPath: IndexPath) { // guard を利用して、nil チェック guard let textFields = alertcontroller.textFields else {return} guard let text = textFields[0].text else {return} // UIAlertController に入力された文字をコンソールに出力 print(text) // Realm に保存したデータを UIAlertController に入力されたデータで更新 let realmInstance = try! Realm() try! realmInstance.write{ itemList[indexPath.row].todo = text } self.todoTableView.reloadData() } }実装のポイント
・テーブルビューのセルを削除するだけでは、それに紐づく Realm に保存したデータは削除されない。そのため、セルとデータをそれぞれ削除する処理を実装する必要がある。
感想
・CocoaPods Realm の導入に少しつまづいた
・アラートコントロールを利用した編集・更新処理のコードが煩雑になったので、リファクタリングしたい
・データベースと連携した iOS アプリを開発できるようになり、開発の幅が広まったと思う
・Human Interface Guidelines に沿うように UI を改善したい
- 投稿日:2020-05-24T13:14:22+09:00
Realmを利用したiOSアプリの実装(データの追加・編集・更新・削除)
この記事の内容
モバイル向けデータベース管理システム Realm を利用した、iOSアプリの開発
参考にした記事
Realmを使ってTODOアプリを作ってみよう!/ Swift(Realmの使い方、初級編)
開発したアプリ
前掲の記事を参考にして、ToDo を追加しテーブルビューで表示することに加えて下記機能を実装した。
・Remove All ボタン をクリックすると、ToDo を全て削除
・ToDo を表示しているテーブルビューのセルを左にスライドすると、その ToDo のみを削除
・ToDo を表示しているテーブルビューのセルをクリックすると、ToDo を編集するためのアラートコントローラを表示
・アラートコントローラの OKボタン をクリックすると、ToDo を更新
前提条件
① CocoaPods Realm の導入、テキストボックスに入力した文字を保存する処理を実装する方法は前掲の記事を参照してください。
② 今回のアプリは、Realm の利用方法を確認するために開発しました。その為、 Apple が策定した Human Interface Guidelines は考慮していません。
開発環境
項目 PC MacBook Air 2017 IDE Xcode Ver. 11.4.1 言語 Swift 5.2.2 データベース Realm コード管理 GitHub Realm とは
モバイル向けデータベース管理システム
Main.storyboard
項目 属性 備考 ViewCotroller UIViewController ViewCotroller.swift と紐付け Navigation Bar UINavigationBar Title を "TestApp" と設定 ToDoTextField UITextField このテキストフィールドに入力した文字を ToDo として保存 Add Button UIButton ボタンの色:青 角は丸く Remove All Button UIButton ボタンの色:赤 角は丸く ToDoTableView UITableView 保存されている ToDo を表示 testCell UITableViewCell Identifier を "testCell" と設定 コード
ToDoModel.swiftimport Foundation import RealmSwift class TodoModel: Object{ @objc dynamic var todo: String? = nil }ViewCotroller.swiftimport UIKit import RealmSwift class ViewController: UIViewController,UITextFieldDelegate,UITableViewDelegate { @IBOutlet weak var todoTextFiled: UITextField! @IBOutlet weak var todoTableView: UITableView! @IBOutlet weak var addButton: UIButton! @IBOutlet weak var removeAllButton: UIButton! var itemList: Results<TodoModel>! // Add ボタンをクリックした際に実行する処理 @IBAction func tapAddButton(_ sender: Any) { let instancedTodoModel:TodoModel = TodoModel() instancedTodoModel.todo = self.todoTextFiled.text let realmInstance = try! Realm() try! realmInstance.write{ realmInstance.add(instancedTodoModel) } self.todoTableView.reloadData() } // Remove All ボタンをクリックした際に実行する処理 @IBAction func tapRemoveAllButton(_ sender: Any) { let realmInstance = try! Realm() try! realmInstance.write{ realmInstance.deleteAll() } self.todoTableView.reloadData() } override func viewDidLoad() { super.viewDidLoad() // UITableViewDataSource を self に設定 self.todoTableView.dataSource = self // UITableViewDelegate を self に設定 self.todoTableView.delegate = self // ボタンの角を丸くする設定 addButton.layer.cornerRadius = 5 removeAllButton.layer.cornerRadius = 5 let realmInstance = try! Realm() self.itemList = realmInstance.objects(TodoModel.self) self.todoTableView.reloadData() } } extension ViewController: UITableViewDataSource{ func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return self.itemList.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let testCell:UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "testCell")! let item: TodoModel = self.itemList[(indexPath as NSIndexPath).row] testCell.textLabel?.text = item.todo return testCell } // テーブルビューの編集を許可 func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { return true } // テーブルビューのセルとデータを削除 func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { if editingStyle == UITableViewCell.EditingStyle.delete { // データを削除 let realmInstance = try! Realm() try! realmInstance.write { realmInstance.delete(itemList[indexPath.row]) } // セルを削除 tableView.deleteRows(at: [indexPath as IndexPath], with: UITableView.RowAnimation.automatic) } } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { print("セル\(indexPath)が選択されました") showAlertController(indexPath) } // テーブルビューのセルをクリックしたら、アラートコントローラを表示する処理 func showAlertController(_ indexPath: IndexPath){ let alertController: UIAlertController = UIAlertController(title: "\(String(indexPath.row))番目の ToDo を編集", message: itemList[indexPath.row].todo, preferredStyle: .alert) // アラートコントローラにテキストフィールドを表示 テキストフィールドには入力された情報を表示させておく処理 alertController.addTextField(configurationHandler: {(textField: UITextField!) in textField.text = self.itemList[indexPath.row].todo}) // アラートコントローラに"OK"ボタンを表示 "OK"ボタンをクリックした際に、テキストフィールドに入力した文字で更新する処理を実装 alertController.addAction(UIAlertAction(title: "OK", style: .default, handler: { (action) -> Void in self.updateAlertControllerText(alertController,indexPath) })) // アラートコントローラに"Cancel"ボタンを表示 alertController.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil)) self.present(alertController, animated: true, completion: nil) } // "OK"ボタンをクリックした際に、テキストフィールドに入力した文字で更新 func updateAlertControllerText(_ alertcontroller:UIAlertController, _ indexPath: IndexPath) { // guard を利用して、nil チェック guard let textFields = alertcontroller.textFields else {return} guard let text = textFields[0].text else {return} // UIAlertController に入力された文字をコンソールに出力 print(text) // Realm に保存したデータを UIAlertController に入力されたデータで更新 let realmInstance = try! Realm() try! realmInstance.write{ itemList[indexPath.row].todo = text } self.todoTableView.reloadData() } }実装のポイント
・テーブルビューのセルを削除するだけでは、それに紐づく Realm に保存したデータは削除されない。そのため、セルとデータをそれぞれ削除する処理を実装する必要がある。
感想
・CocoaPods Realm の導入に少しつまづいた
・アラートコントロールを利用した編集・更新処理のコードが煩雑になったので、リファクタリングしたい
・データベースと連携した iOS アプリを開発できるようになり、開発の幅が広まったと思う
・Human Interface Guidelines に沿うように UI を改善したい
- 投稿日:2020-05-24T05:56:22+09:00
【Swift】位置情報を取得する
概要
swiftで位置情報を取得する方法について、まとめました。
startUpdatingLocation() で位置情報の取得を開始し、取得した場合 func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) が呼び出され位置情報を確認することだできます。
class ViewController: UIViewController, CLLocationManagerDelegate { var locationManager : CLLocationManager? override func viewDidLoad() { super.viewDidLoad() locationManager = CLLocationManager() locationManager!.delegate = self //位置情報を使用可能か if CLLocationManager.locationServicesEnabled() { //位置情報の取得開始 locationManager!.startUpdatingLocation() } } // 位置情報を取得した場合 func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { } }info.listに追記
位置情報を取得する場合、info.list に「Location Always and When In Use Usage Description」、「Location When In Use Usage Description」を追加し位置情報どのように使用するか記載する必要があります。
info.list// ユーザーの位置情報へのアクセスを常に要求している理由を記載します。 Privacy - Location Always and When In Use Usage Description // フォアグラウンドで実行されているときに、アプリがユーザーの位置情報へのアクセスを要求する理由を記載します。 Privacy - Location When In Use Usage Description位置情報を取得する際の設定
位置情報を取得する際に、精度や取得間隔の指定ができます。
locationManager = CLLocationManager() locationManager!.delegate = self // 常に使用する場合、バックグラウンドでも取得するようにする if CLLocationManager.authorizationStatus() == .authorizedAlways { // バックグラウンドでも取得する locationManager!.allowsBackgroundLocationUpdates = true } else { // バックグラウンドでは取得しない locationManager!.allowsBackgroundLocationUpdates = false } // 位置情報の取得精度を指定します locationManager!.desiredAccuracy = kCLLocationAccuracyBest // 更新に必要な最小移動距離 // Int値を指定することで、○○m間隔で取得するようになります locationManager!.distanceFilter = 10 // 移動手段を指定します // 徒歩、自動車等 locationManager!.activityType = .fitness // 位置情報取得開始 locationManager!.startUpdatingHeading()CLLocationManager.authorizationStatusの設定値
authorizationStatus 設定 .notDetermined 未設定 .restricted 機能が制限されている .denied 許可しない .authorizedWhenInUse このAppの使用中のみ許可 .authorizedAlways 常に許可 activityTypeの設置値
activityType 移動手段 .fitness 徒歩 .automotiveNavigation 自動車 .other その他 desiredAccuracyの設置値
desiredAccuracy 精度 kCLLocationAccuracyBestForNavigation デフォルト kCLLocationAccuracyBest 最高精度 kCLLocationAccuracyNearestTenMeters 10m以内 kCLLocationAccuracyHundredMeters 100m以内 kCLLocationAccuracyKilometer 1km以内 kCLLocationAccuracyThreeKilometers 3km以内 位置情報を取得した場合
位置情報を取得した場合以下の、関数を呼び出し緯度経度等の情報を取得できます。
// 位置情報取得した場合 func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { guard let newLocation = locations.last else { return } let location:CLLocationCoordinate2D = CLLocationCoordinate2DMake(newLocation.coordinate.latitude, newLocation.coordinate.longitude) let formatter: DateFormatter = DateFormatter() formatter.timeZone = TimeZone(identifier: "Asia/Tokyo") formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss'Z'" let date = formatter.string(from: newLocation.timestamp) print("緯度:", location.latitude, "経度:", location.longitude, "時間:", date) }参考
- 投稿日:2020-05-24T02:57:41+09:00
【SwiftUI】ビューをジェスチャーで回転させ、90°ずつスナップを効かせる方法
【SwiftUI】ビューをジェスチャーで回転させ、90°ずつスナップを効かせる方法
こんにちは、たいちです(@taichi_swift_)
たかし「お絵かきアプリとかでよくある、ビューを回転させて90°づつピタって止めるやつ、どうやって実装するんだろ...」
こちらのお悩みを解決します。
ビューを回転させる方法
まずは、基本のビューを回転させる方法です。
SwiftUIでは、
.rotationEffect(_ angle: Angle, anchor: UnitPoint = .center)
というmodifierを使用します。実際に使うときには、
.rotationEffect(Angle(degrees: 90))
みたいに使うわけですね。Text("RotationEffect") .rotationEffect(Angle(degrees: 90))ビューをジェスチャーで回転させる方法
続いて、ユーザーが指を使ってビューを回転させる方法です。
.gesture()
を使います。まずは、角度を格納する変数を2つ用意します。
@State var angle = Angle(degrees: 0.0) @State private var oldAngle = Angle(degrees: 0.0)そして、ジェスチャーを用意します
var rotation: some Gesture { RotationGesture() .onChanged { angle in self.angle = self.oldAngle + angle } .onEnded { angle in self.oldAngle = self.angle } }そんでこのジェスチャーをテキトーなビューにがっちゃんこします。
.rotationEffect(self.angle)
.gesture(rotation)
RoundedRectangle(cornerRadius: 20) .frame(width: 400, height: 300) .rotationEffect(self.angle) .gesture(rotation)ビューを回転させ、スナップさせる方法
やっときました。本題です。
先ほど定義した、rotationという名前のgestureにちょこっと細工を加えてあげます。
var rotation: some Gesture { RotationGesture() .onChanged { angle in self.angle = self.oldAngle + angle if abs(Int(self.angle.degrees) % 90) < 5 { self.angle = Angle(degrees: Double(Int(self.angle.degrees) - Int(self.angle.degrees) % 90)) } } .onEnded { angle in self.oldAngle = self.angle } }完成です。
struct ContentView: View { @State var angle = Angle(degrees: 0.0) @State private var oldAngle = Angle(degrees: 0.0) var rotation: some Gesture { RotationGesture() .onChanged { angle in self.angle = self.oldAngle + angle if abs(Int(self.angle.degrees) % 90) < 5 { self.angle = Angle(degrees: Double(Int(self.angle.degrees) - Int(self.angle.degrees) % 90)) } } .onEnded { angle in self.oldAngle = self.angle } } var body: some View { RoundedRectangle(cornerRadius: 20) .frame(width: 400, height: 300) .rotationEffect(self.angle) .gesture(rotation) } }
- 投稿日:2020-05-24T00:04:01+09:00
【SwiftUI】基本的なモディファイアの種類と使い方②
テキストの装飾に関するモディファイア
lineLimitモディファイア
文字列の表示行数の最大値を設定
例:文字列"hogehoge"を20回繰り返して3行に収めるText(String(repeating:"hogehoge", count:20)) .lineLimit(3)truncationModeモディファイア
文字列を省略する場所を指定
例:文字列の中心が省略Text(String(repeating:"hogehoge", count:20)) .lineLimit(1) .truncationMode(.middle)lineSpacingモディファイア
行間の幅を指定
cssのline-heightと同じ
例Text("hogehoge") .lineSpacing(50)fontモディファイア
フォントの種類を指定
種類:
largeTitle:大きなタイトル
title:タイトル
headline:見出し
subheadline:小見出し
body:本文
callout:吹き出し
footnote:注釈
caption:キャプション
例Text("hoge") .font(.body)boldモディファイア
文字を太字にする
italicモディファイア
文字を斜体にする
fontWeightモディファイア
文字の太さを設定する
(太い順にblack,heavy,bold,semibole,medium,regular,light,thin,ultraLight)baseLineOffsetモディファイア
ベースラインの太さを設定
正の数字ベースラインが上がるkerningモディファイア
2文字間の間隔を設定
trackingモディファイア
文字列全体の文字間隔を調整
kerningより優先されるunderlineモディファイア
文字列にアンダーラインを表示
active:下線を表示するかどうか
color:下線の色strikethroughモディファイア
文字列に取り消し線を表示
active:取り消し線を表示するかどうか
color:取り消し線の色