20210605のSwiftに関する記事は4件です。

[iOS] LicensePlistを使用してライセンスを設定アプリに表示してみた

はじめに アプリに使用しているライブラリ等のライセンスを明示する方法が分からなかったので、調べてみるとLicensePlistという「設定アプリにライセンスを自動で一覧表示してくれる」ツールが便利そうなので使用してみました。備忘録です。 LicensePlist というiOSアプリ利用ライブラリのライセンス一覧を生成するツールを作りました mono0926/LicensePlist 環境 [Xcode] 12.4 [iOS] 14.4 [MacOS] 10.15.7 導入手順 CococaPods, Homebrew, Mintを使用してインストールすることが推奨されていました。 本記事ではCocoaPodsを利用しました。 Podfile pod 'LicensePlist' # Installation path: `${PODS_ROOT}/LicensePlist/license-plist` まずプロジェクトファイルの下にSettings.bundleファイルを作成します。 次に、XcodeのTARGETSの[Build Phases]タブでRun Scriptを追加して以下を記載します。 自動で中身を更新してくれるためこの方法が推奨されていますが、もちろん手動でも実行出来ました。 if [ $CONFIGURATION = "Debug" ]; then ${PODS_ROOT}/LicensePlist/license-plist --output-path $PRODUCT_NAME/Settings.bundle fi あとはSetting.bundleの中身(画像ではRoot.plist)を画像のように編集してtitleやFilenameを指定し、アプリを実行します。 アプリを実行すると、設定アプリの中の自分のアプリにLicenseセルが表示されて中身を確認できます。 今回初めてSettings.bundleを触ったのですが、Preference Items内の項目を編集することで、設定アプリに表示する項目も変更することができるようです (上画像のitem1~item4)。 最後に 私を含め、アプリを作るときにライセンス表示について分からない初学者は少なくないのではないでしょうか。 ライブラリをいくつも使用する際は、手動だと時間もかかると思うので簡単に取り込むことができるLicensePlistを今後も活用していこうと思います。 参考文献 この記事は以下の情報を参考にしました。 - LicensePlist というiOSアプリ利用ライブラリのライセンス一覧を生成するツールを作りました - mono0926/LicensePlist
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Swift】UIButtonをsetTitleしたときのちらつきをなくす

はじめに 以下のように、UIButtonをsetTitleした時に一瞬ちらついてしまいます。これをスムーズに入れ替える方法を紹介します。 ちらつきあり ちらつきなし 実装 まず、ちらつきをなくす前のコードがこちらです。  enum ButtonType { case a case b var text: String { switch self { case .a: return "AAAAA" case .b: return "BBBBB" } } mutating func change() { switch self { case .a: self = .b case .b: self = .a } } } final class ViewController: UIViewController { @IBOutlet private weak var button: UIButton! private var buttonType: ButtonType = .b @IBAction private func buttonDidTapped(_ sender: Any) { button.setTitle(buttonType.text, for: .normal) buttonType.change() } } そして、ちらつきをなくしたコードがこちらです。 enum ButtonType { case a case b var text: String { switch self { case .a: return "AAAAA" case .b: return "BBBBB" } } mutating func change() { switch self { case .a: self = .b case .b: self = .a } } } final class ViewController: UIViewController { @IBOutlet private weak var button: UIButton! private var buttonType: ButtonType = .b @IBAction private func buttonDidTapped(_ sender: Any) { UIView.setAnimationsEnabled(false) button.setTitle(buttonType.text, for: .normal) buttonType.change() button.layoutIfNeeded() UIView.setAnimationsEnabled(true) } } ポイントは以下ですね。 ボタンのタイトルを変更する前にアニメーションを止めて、ちらつきを無くします。そして、ボタンに対してlayoutIfNeededをしてあげることで、ちらつきがなくなります。(これを書かないと、ちらついてしまう)そして、最後にアニメーションを再開します。 UIView.setAnimationsEnabled(false) button.setTitle(buttonType.text, for: .normal) buttonType.change() button.layoutIfNeeded() UIView.setAnimationsEnabled(true) おわりに おわりです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

TableView(xibファイル利用)で簡単なToDoアプリ風を作成

簡単なToDoアプリ風を作成してみた。 xibファイル利用して、Cellの再利用を実装。 完成形はこんな感じ↓ FirstViewController import UIKit final class FirstViewController: UIViewController { private let cellClassName = "TableViewCell" private let reuseId = "TableViewCell" private var texts:[Model] = [] private var heightCache: [CGFloat] = [] @IBOutlet weak var tableView: UITableView!{ didSet{ let cellNib = UINib(nibName: cellClassName, bundle: nil) tableView.register(cellNib, forCellReuseIdentifier: reuseId) tableView.delegate = self tableView.dataSource = self } } @IBAction func addButtun(_ sender: Any) { var textField = UITextField() let alert = UIAlertController(title: "新しいアイテムを追加", message: "", preferredStyle: .alert) let action = UIAlertAction(title: "リストに追加", style: .default) { (action) in // 「リストに追加」を押された時に実行される処理 let newItem: Model = Model(text: textField.text!) // アイテム追加処理 self.texts.append(newItem) self.tableView.reloadData() } alert.addTextField { (alertTextField) in alertTextField.placeholder = "新しいアイテム" textField = alertTextField } alert.addAction(action) present(alert, animated: true, completion: nil) } override func viewDidLoad() { super.viewDidLoad() } } extension FirstViewController:UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return texts.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { guard let cell = tableView.dequeueReusableCell(withIdentifier: reuseId, for: indexPath) as? TableViewCell else { return UITableViewCell() } let user = texts[indexPath.row] cell.textLabel?.text = user.text cell.configure(user: user) return cell } func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { texts.remove(at: indexPath.row) let indexPaths = [indexPath] tableView.deleteRows(at: indexPaths, with: .automatic) } } extension FirstViewController:UITableViewDelegate { } TableViewCell import UIKit class TableViewCell: UITableViewCell { @IBOutlet private weak var Label: UILabel! override func prepareForReuse() { super.prepareForReuse() Label?.text = nil } func configure(user: Model) { Label?.text = user.text } static func cellHeight(user: Model) -> CGFloat { /*読み込むデータに対して高さだけ決める*/ return 1000 } } Model import Foundation struct Model{ var text:String = "" }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Swift】フロントエンジニアがSwiftのリアクティブプログラミングを始めてみた話(RxJSと比較付)

はじめに 普段AngularでRxJSを利用しながらコーディングしているSwift初心者の筆者が Apple製の非同期フレームワークである、 Combine を利用してプログラミングしてみた記事になります。 どなたかの参考になれば幸いです。 Combineって何? iOS13 から利用できるようになったフレームワークです。 ※ iOS 13以降に使えるフレームワークなのでiOS 12以下は使えないことにご注意ください。 Combineの大事な3つの要素 Publishers: イベントの発行者 Subscribers: イベントの購読者です  Operators: 流れてくる値を加工することができます  新しい言語で新しい概念が出てくると中々理解しづらい部分があるため 慣れ親しんだRxJsとの対比表を書いて整理してみます。 CombineとRxJSとの対比表 Combine RxJS イベントの発行者 Publishers Observable イベントの購読者 Subscribers Observer 流れてくる値の加工 Operators Operator RxJsと対比してみて、なんとなく理解が進んだところで実際に簡単なサンプルコードを書いてみます。 Publisherを作る (Future) Futureとは非同期で値を返すことが可能なPublisherです。 値を 1つ 発行してfinish or failsのどちらかをすることができます。 値は Genericsになっていますので、 左辺に返したい値の型、右辺にエラーの型を定義する必要があります。 エラーの型は Error型 でも、自分で定義したErrorプロトコルに準拠した型でもいいです。 enum MyError: Error { case BadRequestError(_ value: Any, _ cause: String, _ stackTrace: String? = nil) case NotFoundError(_ value: Any, _ cause: String, _ stackTrace: String? = nil) case SystemError(_ value: Any, _ cause: String, _ stackTrace: String? = nil) } func testFuture(v: Bool) -> Future<String, MyError> { return Future { promise in if (v) { print("hello in test") promise(.success("hello")) } else { promise(.failure(AnError.NotFoundError("", "???", nil))) } } } testFuture(true) // 出力 hello in test 注意点としては、処理が実行されるタイミングが、初めてSubscribeされたタイミングではなく Futureインスタンスが生成されたタイミング になります。 ※ RxJsでいうところの .subscribeしたタイミングで実行されるわけではない そのため上記サンプルを見ていただいたら分かるとおり、 Future型の 関数を購読(Subscribers)していない ですが、関数内の print()が実行され "hello in test" が出力されていることがわかります。 Futureを遅延実行させる(Deferred) 先ほどのFutureの場合、Subscribeをしていないのに処理が実行されてしまい 思わぬ動作をしてしまう可能性もあります。 そのため購読したタイミングで処理を実行させる Deferred を活用する方法があります。 enum MyError: Error { case BadRequestError(_ value: Any, _ cause: String, _ stackTrace: String? = nil) case NotFoundError(_ value: Any, _ cause: String, _ stackTrace: String? = nil) case SystemError(_ value: Any, _ cause: String, _ stackTrace: String? = nil) } func testDeffered(v: Bool) -> Deferred<Future<String, AnError>> { return Deferred { Future { promise in if (v) { print("hello in test") promise(.success("hello")) } else { promise(.failure(AnError.NotFoundError("", "???", nil))) } } } } testDeffered(true) // 出力 なし 先ほどのFutureと異なり、購読していないため Publisher内の print() が実行されていない事がわかります。 実際に購読してみた場合の動きとコードは以下になります。 (購読については下で紹介) enum MyError: Error { case BadRequestError(_ value: Any, _ cause: String, _ stackTrace: String? = nil) case NotFoundError(_ value: Any, _ cause: String, _ stackTrace: String? = nil) case SystemError(_ value: Any, _ cause: String, _ stackTrace: String? = nil) } func testDeffered(v: Bool) -> Deferred<Future<String, AnError>> { return Deferred { Future { promise in if (v) { print("hello in test") promise(.success("hello")) } else { promise(.failure(AnError.NotFoundError("", "???", nil))) } } } } testDeffered(true).sink(receiveCompletion: { completion in switch completion { case .finished: print("finished") case .failure(let error): print("error \(error)") } }, receiveValue: { value in print(value) }) // 出力 hello in test hello finished Deferredの動きをRxJSで書いてみた場合は以下が近いような感じに近いと思います。 const helloObservable = of('hello'); helloObservable.subscribe(res => console.log(res)); // subscribeしたタイミングで実行される また、FutureやDeferred以外にも以下のようなPublisherがあるので、調べてみてもいいかもしれません。 Empty Fail Record Conforming Types 購読する(Subscribers) Publisherを購読するつ1つの方法として、 Deferredで紹介した sink が使えます。 func test() throws { Future<String, Error> { promise in promise(.success("hello")) }.sink(receiveCompletion: { completion in switch completion { case .finished: break case .failure(let error): print("error \(error)") } }, receiveValue: { value in print("value \(value)") }) } test() // 出力 hello in test hello finished RxJSでいうところの Observableを受け取ってsubscribe()を呼び出す処理にあたると思います。 流れてくる値の加工(Operators) Publisherで流れてきた値を 購読する前に値を加工して渡したい場合は map などのOperatorsを利用します。 ※ RxJSでいうmapやflatMapもCombineにはありますので似たような感覚で使えるかなと思います! func testOperators() throws { Future<Int, Error> { promise in promise(.success(1)) }.map { x in x * 2 }.sink(receiveCompletion: { completion in switch completion { case .finished: break case .failure(let error): print("error \(error)") } }, receiveValue: { value in print("value \(value)") }) } // 出力 value 2 RxJSと同じく、map以外にもさまざまなOperatorsが標準で用意されているため調べてみてもいいかもしれません。 おわりに Swiftの非同期周りの処理を書く際に、HydraやRxSwiftなどサードパーティーのライブラリを使うと 言語やOSのバージョンが上がった際にメンテナンスしんどそうだなぁと思っていたのですが 新しめのOSでしか利用できないものの、標準で用意されていて Swiftいいなと思いました。 Rx系のライブラリを触った事がある方なら簡単な処理ならすんなりと書けるところもグッドです。 ぜひ積極的に活用していければなと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む