20200524のiOSに関する記事は9件です。

【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

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

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 code

Google先生に聞けば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

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

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 code

Google先生に聞けば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

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

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
    }
}
}

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

[Swift / Xcode / Realm]Realm を利用した ToDoアプリの実装(ToDo の追加・編集・更新・削除)

この記事の内容

モバイル向けデータベース管理システム Realm を利用した、ToDo アプリの開発
スクリーンショット 2020-05-24 13.05.26.png

参考にした記事

Realmを使ってTODOアプリを作ってみよう!/ Swift(Realmの使い方、初級編)

開発したアプリ

前掲の記事を参考にして、ToDo を追加しテーブルビューで表示することに加えて下記機能を実装した。
GitHub にもコードを UP しています。
GitHub - tanakadaichi1989

Remove All ボタン をクリックすると、ToDo を全て削除
Simulator Screen Shot - iPhone SE (2nd generation) - 2020-05-24 at 12.47.27.pngSimulator Screen Shot - iPhone SE (2nd generation) - 2020-05-24 at 12.47.33.png

・ToDo を表示しているテーブルビューのセルを左にスライドすると、その ToDo のみを削除
Simulator Screen Shot - iPhone SE (2nd generation) - 2020-05-24 at 12.47.11.png

・ToDo を表示しているテーブルビューのセルをクリックすると、ToDo を編集するためのアラートコントローラを表示
Simulator Screen Shot - iPhone SE (2nd generation) - 2020-05-24 at 12.47.19.png

・アラートコントローラの OKボタン をクリックすると、ToDo を更新
Simulator Screen Shot - iPhone SE (2nd generation) - 2020-05-24 at 12.47.23.pngSimulator Screen Shot - iPhone SE (2nd generation) - 2020-05-24 at 12.47.27.png

前提条件

① 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.swift
import Foundation
import RealmSwift

class TodoModel: Object{
    @objc dynamic var todo: String? = nil
}
ViewCotroller.swift
import 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 を改善したい

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

Realmを利用したiOSアプリの実装(データの追加・編集・更新・削除)

この記事の内容

モバイル向けデータベース管理システム Realm を利用した、iOSアプリの開発
スクリーンショット 2020-05-24 13.05.26.png

参考にした記事

Realmを使ってTODOアプリを作ってみよう!/ Swift(Realmの使い方、初級編)

開発したアプリ

前掲の記事を参考にして、ToDo を追加しテーブルビューで表示することに加えて下記機能を実装した。

Remove All ボタン をクリックすると、ToDo を全て削除
Simulator Screen Shot - iPhone SE (2nd generation) - 2020-05-24 at 12.47.27.pngSimulator Screen Shot - iPhone SE (2nd generation) - 2020-05-24 at 12.47.33.png

・ToDo を表示しているテーブルビューのセルを左にスライドすると、その ToDo のみを削除
Simulator Screen Shot - iPhone SE (2nd generation) - 2020-05-24 at 12.47.11.png

・ToDo を表示しているテーブルビューのセルをクリックすると、ToDo を編集するためのアラートコントローラを表示
Simulator Screen Shot - iPhone SE (2nd generation) - 2020-05-24 at 12.47.19.png

・アラートコントローラの OKボタン をクリックすると、ToDo を更新
Simulator Screen Shot - iPhone SE (2nd generation) - 2020-05-24 at 12.47.23.pngSimulator Screen Shot - iPhone SE (2nd generation) - 2020-05-24 at 12.47.27.png

前提条件

① 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.swift
import Foundation
import RealmSwift

class TodoModel: Object{
    @objc dynamic var todo: String? = nil
}
ViewCotroller.swift
import 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 を改善したい

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

【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)

}

参考

CLLocationManager

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

【SwiftUI】ビューをジェスチャーで回転させ、90°ずつスナップを効かせる方法

【SwiftUI】ビューをジェスチャーで回転させ、90°ずつスナップを効かせる方法

こんにちは、たいちです(@taichi_swift_)

たかし「お絵かきアプリとかでよくある、ビューを回転させて90°づつピタって止めるやつ、どうやって実装するんだろ...」

こちらのお悩みを解決します。

ビューを回転させる方法

まずは、基本のビューを回転させる方法です。
SwiftUIでは、
.rotationEffect(_ angle: Angle, anchor: UnitPoint = .center)
というmodifierを使用します。

実際に使うときには、
.rotationEffect(Angle(degrees: 90))
みたいに使うわけですね。

IMG_1985 2.jpg

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)

すると、二本指で自由に操れるビューができました。
IMG_1986.jpg

ビューを回転させ、スナップさせる方法

やっときました。本題です。

先ほど定義した、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)        
    }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【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:取り消し線の色

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