20210727のSwiftに関する記事は8件です。

Segmentで表示する画像データを選択する(UISegmentedControl)

完成形はこんな感じです 機能説明 画面上部のUISegmentedControlをタップすると、対応した画像をAPIで取得して表示します。 コードと簡単解説 Podfile Podfile pod 'SwiftyJSON' pod 'Alamofire' pod 'SDWebImage' Model JsonDataSets import Foundation struct JsonDataSets{ var imageURLData = String() } 今回は、10個の画像のURLを取得したいので,for needDataCount in 0...10 - 1{}内でresponseに入ってきたデータをjsonResultDatasArrayに入れていきます。 AlamofireModel import Foundation import Alamofire import SwiftyJSON class AlamofireModel{ var jsonResultDatasArray = [JsonDataSets]() } extension AlamofireModel{ func searchGetImageURL(searchKeyword:String){ AF.request("https://pixabay.com/api/?key=~~~~~~~~~APIKey~~~~~~~~~&q=\(searchKeyword)", method: .get, parameters: nil, encoding: JSONEncoding.default).responseJSON { (response) in switch response.result{ case.success: self.jsonResultDatasArray = [] //以前のデータを削除 let jsonResult:JSON = JSON(response.data as Any) for needDataCount in 0...10 - 1{ if jsonResult["hits"][needDataCount]["webformatURL"].string != nil{ let jsonResult = JsonDataSets(imageURLData: jsonResult["hits"][needDataCount]["webformatURL"].string!) self.jsonResultDatasArray.append(jsonResult) }else{ break //取得できる"webformatURL"が無くなった時に処理を終わらせる } } case .failure: let error = NSError() print(error.debugDescription) break } } } } View segmentContentsArrayはSegmentのTitleと画像のURLを取得する時に使います。 for count in 0...segmentContentsArray.count - 1{}内で、insertSegment(withTitle: String, at: Int, animated: Bool)を使用して、表示したい個数分のSegmentを作成します。 SegmentFile import Foundation import UIKit class SegmentFile{ let alamofireModel = AlamofireModel() let uiSegmentetControl = UISegmentedControl() //インスタンス作成 let segmentContentsArray = ["Cat","Dog","Bear","Sea","Mountain"] } extension SegmentFile{ func createSegmentControl(targetView:UIView){ for count in 0...segmentContentsArray.count - 1{ uiSegmentetControl.insertSegment(withTitle: segmentContentsArray[count], at: count, animated: true) } uiSegmentetControl.frame = CGRect(x: targetView.bounds.minX, y: targetView.bounds.minY, width: targetView.frame.size.width, height: targetView.frame.size.height) uiSegmentetControl.backgroundColor = UIColor.white uiSegmentetControl.selectedSegmentTintColor = UIColor.systemGreen targetView.addSubview(uiSegmentetControl) } } Controller Segmentを選択時に画像のURLを取得したいので、addTarget(target: Any?, action: Selector, for: UIControl.Event)を使用します。今回は、actionに#selector(displayImage)を入れています。なので、viewWillAppearを出た場所で@objc func displayImage(sender:UISegmentedControl){}を作成します。 forEachの({})内にある$0は、senderArrayの値が1つずつ入ってきます。 $0 == sender.selectedSegmentIndexで選択されたSegmentを確実に調べます。選択されたselectedSegmentIndexと同じIndex番号のsegmentContentsArrayの値を使い画像のURLを取得してきます。 ViewController import UIKit import SDWebImage class ViewController: UIViewController { @IBOutlet weak var collectionView: UICollectionView! @IBOutlet weak var segmentedControlView: UIView! let segmentFile = SegmentFile() let alamofireModel = AlamofireModel() let collectionViewFlowLayout = UICollectionViewFlowLayout() override func viewDidLoad() { super.viewDidLoad() collectionView.delegate = self collectionView.dataSource = self } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) segmentFile.createSegmentControl(targetView: segmentedControlView) collectionViewFlowLayout.sectionInset = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5) collectionView.collectionViewLayout = collectionViewFlowLayout segmentFile.uiSegmentetControl.addTarget(self, action: #selector(displayImage), for: .valueChanged) } @objc func displayImage(sender:UISegmentedControl){ let senderArray = [0,1,2,3,4] senderArray.forEach({ if $0 == sender.selectedSegmentIndex{ alamofireModel.searchGetImageURL(searchKeyword: segmentFile.segmentContentsArray[sender.selectedSegmentIndex]) DispatchQueue.main.asyncAfter(deadline: .now() + 2) { self.collectionView.reloadData() } } }) } } CollectionViewの解説はこちらで簡単にしていますので、必要な方はご覧くださいUISliderとCollectionViewの簡単復習App ViewController extension ViewController: UICollectionViewDelegate,UICollectionViewDataSource,UICollectionViewDelegateFlowLayout{ func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return alamofireModel.jsonResultDatasArray.count } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) let cellImageView = cell.contentView.viewWithTag(1) as! UIImageView cellImageView.frame = CGRect(x: cell.bounds.minX, y: cell.bounds.minY, width: cell.frame.size.width, height: cell.frame.size.height) cellImageView.contentMode = .scaleAspectFill cellImageView.sd_setImage(with: URL(string: alamofireModel.jsonResultDatasArray[indexPath.row].imageURLData), completed: nil) return cell } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { return CGSize(width: collectionView.frame.size.width / 4 - 10, height: collectionView.frame.size.width / 4 - 10) } } 終わり 最近、同じbackGroundColorを何度も使ってる気がする。 ご指摘、ご質問などありましたら、コメントまでお願い致します。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

デリゲートメソッドを作成 -swift

◉デリゲートとは  プロコトルを用いたデザインパターンで「他のクラスのインスタンスに、処理を任せることができる」もの ◉主役 ・プロトコル ・処理を任せるクラス ・処理を任せられるクラス  ex) UITableViewを表示するUIViewControllerがあるとする。   UITableViewだけではUIとして完成しない(セルやセルの数自体がない為) ・プロトコル→UITableViewDelegateとUITableViewDataSource ・処理を任せるクラス→UITableView ・処理を任せられるクラス→UIViewController ◉デリゲートメソッド作成・使用方法 ①プロトコールの作成 protocol プロトコール名 { //規則のみ定める   func メソッド名(引数:外部引数) } ②プロトコールを変数化して使えるようにする var 変数名:プロトコール名? ③任意のタイミングで発動 @IBAction func Action名(_ sender: Any) { //処理を任せるクラスで発動させる 変数名.メソッド名(引数:外部引数) } ④処理を任せられるクラスでプロトコールを宣言 class ViewController: UIViewController,プロトコール名 {     override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. } } ⑤プロトコールに書かれたメソッドを記述 func メソッド名(引数:外部引数) { //外部引数には処理を任せるクラスの変数の値が入っている } ⑥ 処理を任せるクラスから処理を任せられるクラスへ委任を受ける override func prepare(for segue:UIStoryboard,sender:Any?) { let 変数 = segue.instination as! 処理を任せるクラス 変数.プロトコールの変数名 = self }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Swift MacアプリでDockにアイコンを表示しない方法

Dockにアイコンを表示するかの判定はInfo.plistにLSUIElementを定義することで実現できる。 LSUIElementはDockにアイコンを置かないアプリかどうか(エージェントアプリかどうか)を判定するキー。 以下のようにtrueを指定することでアイコンを非表示にできた。 <key>LSUIElement</key> <true/>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

イニシャライザの4種類(Swift)

Swiftのイニシャライザには4つの種類がある ①デフォルトイニシャライザ ②メンバーワイズイニシャライザ ③カスタムイニシャライザ ④失敗可能イニシャライザ デフォルトイニシャライザとは デフォルトイニシャライザとはデフォルトでストアドプロパティが入っているので引数がなくてもインスタンス化できるイニシャライザのこと。 暗黙的にイニシャライザが実装される struct Fish {    var area = "北海道"   var price = 3000 } メンバーワイズイニシャライザとは 構造体にしか使われないイニシャライザ。型指定のみのデータしか入っていない場合に使われる。 通常はinit()で初期化処理をしなくてはならないが、この場合は書かなくてもインスタンス化できてしまう struct Fish {    var area:String var price:Int //init(area:String,price:Int) { //self.area = area //self.price = price //} 通常はコメントアウト部分必要だが、なくてもいける } let fish = Fish(area:"北海道",price:3000) カスタムイニシャライザとは 通常のinit()を使って初期化処理をするイニシャライザのこと 失敗可能イニシャライザとは 文字通り失敗可能なイニシャライザ。 struct Fish{ var area:String init?(area:String?) { gurad let a = area else {//アンラップする処理 return nil//何も入っていなければnilを返す。 } self.area = a } } 初期化処理時にinit?で値が入ってこない場合は処理をスキップするようにし、その場合は後のguard let else構文もしくはif let などでアンラップしてあげてnilをかえしてあげる。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RxSwiftを理解する[初級編]

概要 今回は、RxSwiftを「RxSwift研究読本I 入門編」を用いて学習したので、その内容をまとめていきます。 この本は、とても内容が分かりやすく、間違いなくオススメの本なのでRxSwiftを学習したい方は是非チェックしてみてください!! RxSwiftの概要 iOSアプリ開発は、ユーザーによる操作やキーボード表示非表示などのイベント処理をリアルタイムで複数検出する必要があります。 これらを処理する場合は、delegateやKey Value observing、クロージャによるコールバックなどで処理をしていました。しかし、これらの方法は処理が複雑に絡み合い保守性が落ちることは言うまでもありません。 これらの問題を解決してくれるのがRxSwiftです。 RxSwiftは、複雑になりがちなイベント処理コードをObservableというクラスを用いて、処置を統一的に扱うことができます。 Observableを理解する Observableは、イベントを流す役割を持っています。 次の例では、アプリを使うユーザーがインクメンタル検索を行うことを想定しています。 具体的には、ユーザーが「Olympic」という文字列を入力していくとき、その入力は順次「O」や「Ol」などの情報が送られてきます。 では、実際のコードを見ていきましょう RxSwift let observable = Observable.of( "O", "Ol", "Oly", "Olym", "Olymp", "Olympi", "Olympic" ) _ = observable .subscribe(onNext: { print("onNext: ", $0) }, onCompleted: { print("終了") }) // 出力結果 onNext: O onNext: Ol onNext: Oly onNext: Olym onNext: Olymp onNext: Olympi onNext: Olympic 終了 また、次の例ではtextFieldに入力された値に関するObservableの例です。 これを実行すると、textFieldに入力された値が変わるたびに、その値がprintされます。 つまり、「値が更新されるたびに、登録された処理を実行」します。 RxSwift let observable = textField.rx.text.asObservable() let subscription = observable .subscribe(onNext: { string in print(string) }) onNext, onCompleted, onErrorを理解する それぞれ、onNextは「値が更新された」、onCompletedは「処理が完了した」、onErrorは「エラーが発生した」というイベントを表します。 onNextは値を渡すことができますが、onCompletedは値を渡すことができません。 (RxSwift/Event.swift)では、以下のような定義で書かれています。 RxSwift public enum Event<Element> { case next(Element) case error(Swift.Error) case completed } これら3つの組み合わせで色々な動作を表すことができます。 但し、ルールとして一度onCompletedやonErrorが発生するとそれ以降onNext等を呼ぶことはできなくなるので注意が必要です。 filterメソッドを理解する 次の例では、先程のコードに「入力が2文字以上の場合のみ処理を行う」という条件を加えてみます。 結果は以下のように「O」の1文字では検索できないようにフィルタリングされています。 RxSwift let observable = Observable.of( "O", "Ol", "Oly", "Olym", "Olymp", "Olympi", "Olympic" ) _ = observable .filter {$0.count >= 2 } // 2文字以上ならtrue .subscribe(onNext: { print("onNext: ", $0) }) // 実行結果 onNext: Ol onNext: Oly onNext: Olym onNext: Olymp onNext: Olympi onNext: Olympic mapメソッドを理解する 次の例では、先程のコードに全て小文字にするという条件を付け加えてみましょう。 小文字にするという条件を付け加えたい場合は、mapメソッドを使用します。 RxSwift let observable = Observable.of( "O", "Ol", "Oly", "Olym", "Olymp", "Olympi", "Olympic" ) _ = observable .filter {$0.count >= 2 } // 2文字以上ならtrue .map { $0.lowercased() } // 小文字にする .subscribe(onNext: { print("onNext: ", $0) }) // 実行結果 onNext: ol onNext: oly onNext: olym onNext: olymp onNext: olympi onNext: olympic 時間的概念を理解する RxSwiftの特徴として、時間の概念が扱えるというメリットがあります。 「時間の概念が扱える」とは何かというと、時間を軸とした制御が可能になるということです。 少しわかりにくいと思いますので、サンプルコードを見ていきましょう。 経過時間という条件を考慮したコードを書くために、RxSwiftではdebounceというオペレータが用意されています。 次の例では、debounceを使いユーザーの入力自体も1秒ごとであると仮定したコードをご紹介します。 説明のため、コードに対する時間についての表現はRxSwiftのテスト用フレームワークであるRxTestを使用します。 RxSwift import RxTest let scheduler = TestScheduler(initialClock: 0) let observable = scheduler.createHotObservable([ Recorded.next(1, "O"), Recorded.next(2, "Ol"), Recorded.next(3, "Oly"), Recorded.next(4, "Olymp"), Recorded.next(5, "Olympi"), Recorded.next(6, "Olympic") ]) _ = observable .debounce(1, scheduler: scheduler) .subscribe(onNext: { print("onNext: ", $0) }) scheduler.start() // 実行結果 onNext: Olympic ここから、さらにわかりやすくdebounceの効果が出力される例を示すために、debounceの条件1秒はそのままに、「Ol」から「Oly」の間が2秒ほど時間がかかるようにしてみます。 次の例では、「Ol」から「Oly」の間が2秒ほどかかっていることによって、debounceの1秒よりも長い時間となります。 よって、文字列「Ol」は経過時間条件を抜けるため、イベントとして伝わります。 RxSwift import RxTest let scheduler = TestScheduler(initialClock: 0) let observable = scheduler.createHotObservable([ Recorded.next(1, "O"), Recorded.next(2, "Ol"), // ここから Recorded.next(4, "Oly"), // ここまで2秒時間がかかった Recorded.next(5, "Olymp"), Recorded.next(6, "Olympi"), Recorded.next(7, "Olympic") ]) _ = observable .debounce(1, scheduler: scheduler) .subscribe(onNext: { print("onNext: ", $0) }) scheduler.start() // 実行結果 onNext: Ol onNext: Olympic Subjectを理解する RxSwiftで頻繁に利用されるPublishSubjectに代表されるSubjectは、ObservableとObserver両方の機能を有していると表現されています。 具体的にSubjectが柔軟にイベントを発火できる仕組みであるということを理解するために、PublishSubjectを使ったサンプルコードを見ていきましょう。 次の例のSubjectの動作からわかる通り、Subjectは購読され、Subjectのインスタンスはイベントを任意のタイミングで発火できることが分かります。 RxSwift let subject = PublishSubject<String>() subject.subscribe(onNext: { print("onNext: ", $0) }) subject.onNext("A") subject.onNext("B") subject.onNext("C") subject.onNext("D") subject.onCompleted() // 出力結果 onNext: A onNext: B onNext: C onNext: D dispose・DisposeBagを理解する dispose RxSwiftは開発者の任意のタイミングでObservableシーケンスを破棄することができます。 いつまでも、subscribe処理を続けてしまうとメモリリークするので、適切なタイミングでObservableシーケンスを破棄することが重要です。 先程のコードにdispose()メソッドを呼び出してみましょう。 下記の例からわかる通りsubscribeメソッドではdisposeされたことを購読でき、disposeされてからは以降のイベントは購読できなくなります。 RxSwift let subject = PublishSubject<String>() let subscription = subject .subscribe(onNext: { print("onNext: ", $0) }, onCompleted: { print("終了") }, onDisposed: { print("破棄") }) subject.onNext("A") subject.onNext("B") subscription.dispose() subject.onNext("C") subject.onNext("D") subject.onCompleted() // 出力結果 onNext: A onNext: B 破棄 しかし、一つ一つにdispose()メソッドを書いていると大変ですよね。 そこで、登場するのがDisposeBagです。 DisposeBagを使うと半自動的に開放してくれるので、ViewControllerのメンバには必ずと言ってよいほど定義して、画面に関わるsubscriptionを登録しておくことが多いです。 DisposeBag disposeBagは、disposeBagオブジェクト自身が開放されるタイミングで、登録されたsubscriptionをdisposeします。 RxSwiftを利用したコード内で以下の定義がされていることが多いみたいです。 次の例では、subscribeしているところでメソッドチェーンで.disposed(by: disposeBag)という定義がされています。 RxSwift let disposeBag = DisposeBag() /* * 省略 */ func bind() { hogehoge.subscribe(onNext: { [weak self] value in // 何らかの処理 }).disposed(by: disposeBag) } disposeBagはまとめてObservableシーケンスを処分するための仕組みです。 disposeBagにObservableシーケンスを保持させ、そのdisposeBagを破棄することで、bservableシーケンスをまとめて破棄することができます。 では、実際にdisposeBagをすると、どのような挙動になるかみていきましょう。 次のサンプルコードでは、didTapStartButtonが押されると、現在のcountが出力されて、その3秒後にcreateObservableLoadのevent.onNextが呼ばれています。 RxSwift class DetailViewController: UIViewController { let disposeBag = DisposeBag() var count: Int = 0 @IBAction func didTapStartButton() { print("start \(count)") createObservableLoad(count: count).subscribe { [weak self] event in guard let self = self else { return } switch event { case .next(let text): print("text desu \(text)") case .error(let error): print("error \(error)") case .completed: print("completed!") } } count += 1 } func createObservableLoad(count: Int) -> Observable<String?> { return Observable.create { event -> Disposable in DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) { event.onNext("next dayo") event.onNext("next dayo2 count \(count)") } return Disposables.create { } } } } 出力の結果は以下の通りです。 しかし、ここにdisposeBagがないと、循環参照が起こるのでメモリリークを起こしてしまいます。 それでは、disposeBagを書き加えてみましょう。 RxSwift class DetailViewController: UIViewController { let disposeBag = DisposeBag() var count: Int = 0 @IBAction func didTapStartButton() { print("start \(count)") createObservableLoad(count: count).subscribe { [weak self] event in guard let self = self else { return } switch event { case .next(let text): print("text desu \(text)") case .error(let error): print("error \(error)") case .completed: print("completed!") } } .disposed(by: disposeBag) // ここに書く count += 1 } func createObservableLoad(count: Int) -> Observable<String?> { return Observable.create { event -> Disposable in DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) { event.onNext("next dayo") event.onNext("next dayo2 count \(count)") } return Disposables.create {} } } } .disposed(by: disposeBag)を書くことによって、didTapStartButtonを押してもメモリリークしなくなります。 combineLatestを理解する RxSwiftは様々なオペレータによって成り立っています。 combineLatestは最新の値へと切り替わるごとに動作し、合成される全てのシーケンスの最新の値を使用します。 早速、簡単なサンプルコードを見ていきましょう。 次の例のイベントは2つのPublishSubjectを使い、次々に文字列イベントを作成し、そのシーケンスを合成した結果を出力しています。 RxSwift let password = PublishSubject<String>() let repeatedPassword = PublishSubject<String>() _ = Observable.combineLatest(password, repeatedPassword) { "\($0), \($1)"} .subscribe(onNext: { print("onNext: ", $0) }) password.onNext("a") password.onNext("ab") repeatedPassword.onNext("A") repeatedPassword.onNext("AB") repeatedPassword.onNext("ABC") // 出力結果 onNext: ab, A onNext: ab, AB onNext: ab, ABC repeatedPassword: PublishSubjectの値が変わっても、常にpassword: PublishSubjectの最新の値abのみを使っています。 zipを理解する Observableのzipオペレータは、「イベントが揃ったら動作する仕組み」として使われることが多いです。 では、早速サンプルコードを見ていきましょう。 次の例では、入力として実行する1,2,3,4の順序とA,B,C,Dのそれぞれの順序が、zipオペレータを使用することによって、出力時に揃っていることが分かります。 RxSwift let intSubject = PublishSubject<Int>() let stringSubject = PublishSubject<String>() _ = Observable.zip(intSubject, stringSubject) { "\($0) \($1)" } .subscribe(onNext: { print($0) }) intSubject.onNext(1) intSubject.onNext(2) stringSubject.onNext("A") stringSubject.onNext("B") stringSubject.onNext("C") stringSubject.onNext("D") intSubject.onNext(3) intSubject.onNext(4) // 出力結果 1 A 2 B 3 C 4 D まとめ 今回は、「RxSwift研究本I 入門編」を読んだので、理解した内容をまとめました。 最初、RxSwiftのコードを見たときは、何をやっているか全く分かりませんでしたが、何回も読んでいく内に少しずつ理解が深まっていくのを感じました。 また、このようにアウトプットをしていくことによって、基本的なところの理解が深まったと思います。ただ、実際の現場でゴリゴリコードが書けるのかと言ったらそうではないので、より実践的なRxSwiftのコードを見たり、RxSwiftを用いたサンプルアプリを作成したりして、より理解を深めていこうと思いました。 参考文献 ・「RxSwift研究読本I 入門編」 ・RxSwiftについてようやく理解できてきたのでまとめることにした(1)  RxSwiftについて分かりやすくまとめられています。是非、チェックしてみてください!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

iOS9以降はNotificationCenterのremoveObserverは不要になった話

投稿の経緯 業務でNotificationCenterを使うタイミングがあり、公式ドキュメントを見ていると、どうやらiOS9以降はremoveObserverが不要になったことに気付いたので備忘録として投稿。 公式情報 公式ページ にはこのように記載。 If your app targets iOS 9.0 and later or macOS 10.11 and later, and you used addObserver(_:selector:name:object:) to create your observer, you do not need to unregister the observer. If you forget or are unable to remove the observer, the system cleans up the next time it would have posted to it. 翻訳すると? アプリの対象がiOS 9.0以降またはmacOS 10.11以降で、addObserver(_:selector:name:object:)を使ってオブザーバーを作成した場合は、オブザーバーの登録を解除する必要はありません。オブザーバーを忘れたり、削除できなかったりした場合は、次にそのオブザーバーに投稿したであろう時にシステムがクリーンアップします。 とのこと? お知らせ 現在、iOS開発案件を業務委託で募集中です(副業)。TwitterDMでご依頼をお待ちしています。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SwiftUI Tutorialsから学ぶSwiftUI〜その2〜

はじめに 本記事は、Apple公式のSwiftUI Tutorialsを参考として内容をまとめた記事です。 英語が苦手な方や、概要をさっと理解したい方を対象に本記事を執筆しております。 注釈コメントを入れたプロジェクトファイルはこちら(GitHub) Drawing Paths and Shapes Drawing Paths and Shapes 図形の描画 頂点の座標を定義した図形を描画する場合はPathを用いる。 また、表示画面(=親ビュー)の高さや幅を利用する場合は、併せてGeometryReaderを利用する。 定義 // 始点の指定 Path#move(to p: CGPoint) // パラメータ // p: 始点となる2次元座標 // 現在の座標 -> 任意の座標 への線描 Path#addLine(to p: CGPoint) // パラメータ // p: 終点となる2次元座標 // メジェ曲線の描画 Path#addQuadCurve(to p: CGPoint, control cp: CGPoint) // パラメータ // p: 終点となる2次元座標 // cp: 制御点となる2次元座標 サンプル 線描の様子を表す図は、以下の通り。 メジェ曲線の描画を表す図は、以下の通り。 Animating Views and Transitions Animating Views and Transitions 擬似アニメーション 参考: Graphics and Rendering トグルスイッチの役割を持たせたビューに対して、ボタンの状態に応じたアイコンの変化を演出することができる。 ただし、アニメーション効果を設定しない場合はアイコンが切り替わるだけとなるため、 動きに連続性を持たせる場合は後述する.animation()修飾子を用いてアニメーション効果を付与する。 アイコンの変化 // ビューの回転 View#rotationEffect( _ angle: Angle, anchor: UnitPoint = .center ) -> some View // パラメータ // angle: 回転角度を表すAngleオブジェクト // -> 通常はAngle.degrees(_ degrees: Double)メソッドで指定 // anchor: アンカーポイント(基点) // ビューの拡大 View#scaleEffect( _ s: CGFloat, anchor: UnitPoint = .center ) -> some View また、Animation#withAnimation()メソッドを用いて、状態の変化に呼応してアニメーションを付与することができる。 状態の変化に呼応したアニメーション Animation#withAnimation<Result> ( _ animation: Animation? = .default, _ body: () throws -> Result ) rethrows -> Result // パラメータ // animation: アニメーション効果 // body: 状態を変更するメソッド(クロージャ) ビューへのアニメーション効果の追加 ビューに対して.animation()修飾子を付与することで、アニメーション効果を付与することができる。 また、.animation()修飾子はAnimation構造体インスタンスを引数にとり、タイププロパティで用意されているものもある。 さらに、Animation構造体を拡張定義(extension)し、アニメーション効果を定義したタイプメソッドを記述することで、 アニメーションを独自に定義することができる。 SwiftUIで用意されているAnimationのタイププロパティは、以下の通り。 プロパティ 内容 default ビューによって異なる easeIn 開始のみゆっくり easeInOut 開始・終了のみゆっくり easeOut 終了のみゆっくり linear 等速 バネのようなアニメーション効果 バネのようなアニメーションを付与する場合は、.animation()修飾子の引数として、以下のAnimation構造体のタイプメソッドを用いる。 ここで、単振動モデルを表すspring()・interactiveSpring()メソッドの違いは、初期値が異なるだけで、仕様は同じである。 バネのようなアニメーション // 単振動モデル① Animation.spring( response: Double = 0.55, dampingFraction: Double = 0.825, blendDuration: Double = 0 ) -> Animation // パラメータ // response: ばねの解放時間[秒] ※値が小さいほど動きが瞬発的になる // dampingFraction: 粘性抵抗力 ※値が小さいほど跳ね返りが維持 // blendDuration: 現在の状態(跳ね返り速度)を維持したまま次のアニメーションに移行するまでの時間[秒] // 単振動モデル② Animation.interactiveSpring( response: Double = 0.15, dampingFraction: Double = 0.86, blendDuration: Double = 0.25 ) -> Animation // パラメータ // response: ばねの解放時間[秒] ※値が小さいほど動きが瞬発的になる // dampingFraction: 粘性抵抗力 ※値が小さいほど跳ね返りが維持 // blendDuration: 現在の状態(跳ね返り速度)を維持したまま次のアニメーションに移行するまでの時間[秒] // 減衰振動モデル Animation.interpolatingSpring( mass: Double = 1.0, stiffness: Double, damping: Double, initialVelocity: Double = 0.0 ) -> Animation // パラメータ // mass: 荷重 ※ 値が小さいほど弾性力が小さくなる // stiffness: ばねの剛性 ※値が小さいほど動きが大きくなる // damping: 粘性抵抗力 ※値が小さいほど跳ね返りが維持 // initialVelocity: 初速 ビュー表示の切り替えアニメーション(トランジション; transition) ビューの表示・非表示に伴うアニメーション(=トランジション)を定義する場合、Viewプロトコルのtransition()メソッドを利用する。 SwiftUIで用意される、トランジションを表すAnyTransitionのタイププロパティは以下の4つ。 プロパティ 表示時 非表示時 identity - - opacity 不透明化 透明化 scale 拡大 縮小 slide 左からスライド 右へスライド また、AnyTransition構造体を拡張定義(extension)してタイププロパティを追加することで、トランジション効果を自由に作成することができる。 定義 ビューのトランジション // トランジション効果の指定 View#transition(_ t: AnyTransition) -> some View // パラメータ // t: AnyTransition構造体のタイププロパティ // 任意の方向(上下左右)から移動して表示 AnyTransition.move(edge: Edge) -> AnyTransition // パラメータ // edge: .top(上)/.bottom(下)/.leading(左)/.trailing(右)のいずれか // トランジション効果の組み合わせ AnyTransition#combined(with other: AnyTransition) -> AnyTransition // パラメータ // other: 追加するトランジション効果 // 表示・非表示でトランジション効果の異なる複合トランジションの生成 AnyTransition.asymmetric( insertion: AnyTransition, removal: AnyTransition ) -> AnyTransition // パラメータ // insertion: 表示時のトランジション効果 // removal: 非表示時のトランジション効果
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Macのアカウント名変更に伴い発生したPCHエラーについて

事象 Macのアカウント名変更を実施したところ、アプリケーションのビルド時に 以下エラーメッセージが出力しました。 エラーメッセージ1 PCH was compiled with module cache path '/Users/変更前のアカウント名/Library/Developer/Xcode/DerivedData/ModuleCache.noindex/3PQB3KXR6MCS9', but the path is currently '/Users/新変更後のアカウント名/Library/Developer/Xcode/DerivedData/ModuleCache.noindex/3PQB3KXR6MCS9' エラーメッセージ2 Missing required module 'SwiftShims' 原因 DerivedDataのパスが変更されたため、 コンパイラがキャッシュを見つけられなくなっているのだと思います。 以下に記述した解決策(2つのうちどちらか)を実施することで問題は解決しました。 解決策① DerivedDataを削除する 1.以下フォルダに存在するフォルダ「DerivedData」を削除する /Users/アカウント名/Library/Developer/Xcode/ ※備考:DerivedDataはキャッシュデータのため、削除してもビルド時に再度作成される。 解決策② DerivedDataの参照先を変更する 1.Xcodeより、タブバーにて以下へ移動する Preferences → Locations 2.DerivedDataのプルダウンにて以下を変更する 変更後:Default 変更前:Relative 3.テキストボックスに入力されている内容を修正する 修正前:DerivedData 修正後:DerivedData2 確認環境 Xcode12.5.1
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む