20210419のSwiftに関する記事は17件です。

UIPickerView swift

■UIPickerViewとは 縦にスクロールして項目を選ぶやつ ■基本的に2つのprotocolを記載しデリゲートメソッドを使う。 まずはUIPickerViewDatasource extension ViewController: UIPickerViewDataSource { //縦の列の数を決める。 func numberOfComponents(in pickerView: UIPickerView) -> Int { return 1 } //行の数を決める。 基本配列の中身の数を指定 func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { return weatherManager.prefecturesArray.count } } 次にUIPickerViewDelegate extension ViewController: UIPickerViewDelegate { //配列の中身をUipickerに表示 Array[row] rowは配列の中身の数ぶん,数字が入る。配列の中身が10個なら0~9のインデックス番号 func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { return weatherManager.prefecturesArray[row] }   //スクロールして選択 didSelectRowのrowは選択した配列の中身のインデックス番号になる。 func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { let selectPrefecture = weatherManager.prefecturesArray[row] weatherManager.getprefecture(for: selectPrefecture) } } 基本はこんな感じ
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

MVVMを壊す悪魔、@FetchRequestを駆除する(Core Data)

@FetchRequestが駆除されなければならない理由 (釣りタイトルですいません。) SwiftUIとCoreDataを組み合わせたアプリの例では、ほとんど全てと言っていいほど@FetchRequestが使われています。しかしこのプロパティラッパーは現時点でViewの内部でしか使えないため、必然的にViewがUIに表示するデータを持ってしまい、MVVMが成り立たなくなります。よってこの記事では、このプロパティラッパーを使わずにViewModel側からデータをフェッチし、なおかつViewを自動で更新する方法を紹介します。 (注意:初学者なので間違いがあるかもしれません。その場合コメントで指摘をお願いします。) 方法 1. NSFetchedResultsControllerDelegate https://developer.apple.com/documentation/coredata/nsfetchedresultscontrollerdelegate 公式ドキュメントには「フェッチ結果が変化したときに、関連するfetched result controllerによって呼び出されるメソッドを記述するデリゲート」とあります。簡単に言うと、データが変わったときにメソッドが呼ばれるよ、ということです。下記のコードは、DiaryというEntityをフェッチし、Viewに表示するViewModelです。 (DiaryDataModelというのはcontextを取得するためのクラスで、MVVMのModelにあたります。) DiaryListViewModel.Swift import Foundation import CoreData final class DiaryListViewModel: NSObject, NSFetchedResultsControllerDelegate, ObservableObject { private let fetchController: NSFetchedResultsController<Diary> var diaries: [Diary] { return fetchController.fetchedObjects ?? [] } override init() { fetchController = { let fetchRequest = NSFetchRequest<Diary>(entityName: "Diary") fetchRequest.sortDescriptors = [.init(keyPath: \Diary.createdDate, ascending: true)] return NSFetchedResultsController<Diary>( fetchRequest: fetchRequest, managedObjectContext: DiaryDataModel.context, sectionNameKeyPath: nil, cacheName: nil ) }() super.init() fetchController.delegate = self try? fetchController.performFetch() } func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) { print("❗️controllerWillChangeContent is called.") objectWillChange.send() //更新をSwiftUIへ知らせる } ...... } fetchControllerの初期化の書き方はわかりにくかもしれませんが、クロージャに最後()をつけて実行し、戻り値をセットしているだけです。 try? fetchController.performFetch()で実際にフェッチさせて、fetchControllerにデータを読ませます。どうやらデータベースの変更があっても再度フェッチする必要はないようです。 NSFetchedResultsControllerDelegateに定義されているcontrollerWillChangeContentメソッドでは、変更をSwiftUIに知らせるためにobjectWillChange.send()を呼びます。 diariesはget-onlyの計算型プロパティで、実際にView側からアクセスするところです。objectWillChange.send()が呼ばれるとSwiftUIによって再度読み込まれ、Viewに反映されます。 Viewの方はこんな感じです↓ DiaryListView.Swift import SwiftUI struct SidebarView: View { @StateObject private var viewModel = DiaryListViewModel() //ViewがViewModel以外にプロパティを持っていない...美しぃ...? var body: some View { List { if !viewModel.diaries.isEmpty { ForEach(viewModel.diaries) { diary in Label(diary.name, systemImage: "book") } } else { Text("ページがありません。") } } } } NSFetchedResultsControllerDelegateの使い方に関してはこの記事をかなり参考にさせていただきました。 2.NotificationCenter & Combine(失敗) NSFetchedResultsControllerDelegateを使う方法とほぼ同じですが、肝となる更新の方法は異なり、NSManagedObjectContextObjectsDidChangeというクソ長い名前のNotificationを使います。 DiaryListViewModel.Swift import Foundation import CoreData import Combine final class DiaryListViewModel: ObservableObject { private var cancellables: Set<AnyCancellable> = [] private let fetchController: NSFetchedResultsController<Diary> var diaries: [Diary] { return fetchController.fetchedObjects ?? [] } init() { fetchController = { let fetchRequest = NSFetchRequest<Diary>(entityName: "Diary") fetchRequest.sortDescriptors = [.init(keyPath: \Diary.createdDate, ascending: true)] return .init(fetchRequest: fetchRequest, managedObjectContext: DiaryDataModel.context, sectionNameKeyPath: nil, cacheName: nil) }() try? fetchController.performFetch() NotificationCenter.default.publisher(for: .NSManagedObjectContextObjectsDidChange) .sink { _ in print("objecgt was changed.") self.objectWillChange.send() } .store(in: &cancellables) } ...... } この場合、sinkのクロージャは呼ばれ、diariesプロパティも再読み込みされていることは確認できたのですが、なぜかUIは更新されませんでした。クロージャ内でobjectWillChange.send()を呼ぶことがいけないのでしょうか......(Combine使ったらカッコいいと思ったのに、残念)。問題点がわかった方、コメントで教えていただけると助かります??‍♂️ あとがき @AppStorage、@SceneStorageとかも「駆除」できそうですね。というか、ViewModelでも機能するようなプロパティラッパーを作るのもできそうです。作ったらまた記事にするかもしれません。 その他 参考記事 Using CoreData with SwiftUI : NSFetchedResultsControllerDelegateの使用法など Core Data Notifications With Swift : Core Dataの様々なNotification 環境 OS : macOS Big Sur 11.1 Xcode: 12.4 Swift : 5.3.2
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

URL型をData型に変換する

let fileData = try Data(contentsOf: url) 上記でデータ型に変換できます。 ついでに本当に変換できてるのか知りたいなんて方は print("文字",type(of: fileData)) 上記で調べられます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

"xcodeが予期しない理由で終了しました" が表示された時の解決方法

ある日突然"xcodeが予期しない理由で終了しました" と表示されXcodeの開発途中プロジェクトが 開かなくなってしまいました。 その時の解決法を備忘録として記載しておきたいと思います。 まずはエラーメッセージで検索し、先人が残してくれた記事を読みDerivedDataを削除してみたが 結果は変わらずでした。 解決法 project.xcworkspaceを削除し、再度起動したら解決しました。 削除方法は、ターミナルを開き起動しないプロジェクトフォルダまで移動します。 EX) Users/ユーザー名/Desktop/アプリ名/アプリ名.xcodeproj その後、アプリ名.xcodeproj内にあるproject.xcworkspaceを削除すれば完了です。 結局クラッシュした原因は分からずでしたが、とりあえず解決してよかったです。 また同じ事象が発生した時はログを見て少し解析してみようかなと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Swift】Cartographyを使ってコードだけで簡単に画像を実装

Swiftの超便利ライブラリ「Cartography」を使ってコードだけで画像を実装する方法をご紹介します。 感覚としてはCSSみたいに実装できます。 podで導入する際はPodfileに pod 'Cartography' を書いて 初めてPodをインストールするプロジェクトなら pod install Pod installしてあるプロジェクトなら pod update を実行。 実装するSwiftのコードは以下の通り。 import UIKit import Cartography (中略) let Image : UIImage = UIImage(named:"sample")! let imageView = UIImageView(image:Image) self.view.addSubview(imageView) constrain(imageView) { image in image.width == 50 image.height == 50 image.centerX == image.superview!.centerX image.centerY == image.superview!.centerY } コードの順番(特にaddsubview)とか間違えるとエラーになるのでご注意ください。 補足 ・superviewとは親要素のこと(今回はself.viewのこと) ・centerXとは中心のX軸の値 ・centerYとは中心のY軸の値
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Swift】コードだけでUIScrollViewの定義

UIScrollViewの定義を解説する記事の殆どがstoryboardでの実装ばかりだったので、コードでの実装をメモとして残しておきます。 let scrollView = UIScrollView() scrollView.frame = self.view.frame scrollView.contentSize = CGSize(width: UIScreen.main.bounds.size.width, height: UIScreen.main.bounds.size.height) self.view.addSubview(scrollView) たった4行だけ。 個人的にはstoryboardよりも実装しやすいのでこちらの方法を選択しています。 *UIScrollViewのwidthやheightは状況に応じてカスタマイズしてください。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Swift】ホームボタンのあるiPhoneと無いiPhoneでレイアウトの処理を分ける

Swiftのレイアウトで厄介なのが 「iPhone6やiPhone8などのホームボタンがあるiPhone」 と 「iPhoneXSやiPhone11などのホームボタンが無いiPhone」 とでレイアウトがずれてしまう問題。 このホームボタンのあるiPhoneと無いiPhoneでレイアウトの処理を分けるコードを書いたのでメモとして残しておきます。 if UIScreen.main.nativeBounds.height > 2208 || UIScreen.main.nativeBounds.height == 1792{ //ホームボタンがあるiPhoneでの処理 } else{ //ホームボタンが無いiPhoneでの処理 } 要は画面の縦幅から振り分けています。 縦幅の詳細はこちらの記事を参考にしました。 最初は2208をラインに振り分けていたのですが、iPhone11とiPhoneXRだけ縦幅のpxが2208より大幅に少ない1792でしたのでそれも考慮。 *iPadについては考慮していません。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Swiftで画面の縦幅・横幅を取得するプログラム

Swiftで画面の縦幅・横幅を取得するプログラムをメモとして残しておきます。 //横幅を取得 let screenWidth = UIScreen.main.bounds.size.width //縦幅を取得 let screenHeight = UIScreen.main.bounds.size.height iPhoneの画面サイズごとに処理を分けたい時などは各コードを使ってif文で条件分岐をすると便利
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Swift]複数のCoreAnimationを直列的にループ実行する

はじめに すみません、うまくタイトルが思いつかなかったので何がしたいのか詳しく説明すると、 Aのアニメーションを実行 Bのアニメーションを実行 (さらに任意の数のアニメーションを実行) 1~3をループ というのを、CoreAnimationでやりたかったという訳です。 具体的には前回の記事の虹色ボーダーアニメーションに少し手を加えて、 虹色のボーダーラインを時計回りのアニメーションで表示 描画された虹色ボーダーラインを時計回りに消去 1~2のアニメーションをループ というようにしたいと考えました。 完成図だけ先に載せておきます。 消える方にもアニメーション追加してみた pic.twitter.com/2JKwIQRFS7— M (@p_x9) April 19, 2021 autoreversesではダメなのか CoreAnimationにはautoreversesという機能がありますが、これはアニメーションを巻き戻すといった機能です。 そのため、以下のように描画されたボーダラインが反時計回りに消えていく形なので今回目的としたアニメーションとは違いました。 (こっちのアニメーションでもいいやないかと思う方もおられるかもしれませんが、そうするとこの記事の題名からして本末転倒なので許してください。) 本編 では今回実装した手順を説明します。 1. ループさせたい複数のCoreAnimationを作成 本題とズレる部分のコードの詳細についてはここでは避けますが(前回の記事を参考にしてもらえればと思います。)、以下のように繰り返したいアニメーションを作成します。 今回はanimation1についてはアニメーションが完了した時その状態を維持しておいて欲しいので、新たにisRemovedOnCompletionプロパティをfalseにしています。 //前回作成したアニメーション let animation1 = CABasicAnimation(keyPath: #keyPath(CAShapeLayer.strokeEnd)) animation1.fromValue = 0.0 animation1.toValue = 1.0 animation1.duration = 1.0 animation1.isRemovedOnCompletion = false aniamtion1.fillMode = .forwards //今回新しく作成したアニメーション let animation2 = CABasicAnimation(keyPath: #keyPath(CAShapeLayer.strokeStart)) animation1.fromValue = 0.0 animation1.toValue = 1.0 animation1.duration = 1.0 2. 2番目以降のアニメーションのbeginTimeを設定 2番目以降に実行したいアニメーションについて、beginTimeを beginTime = これまでに実行したアニメーションのdurationの合計 となるように計算して設定します。 今回は、animation1のdurationが1なので以下のようになります。 animation2.beginTime = 1 もし仮にanimation3があったとすると、animation1とanimation2のdurationの合計を設定する必要があるので、 animation3.beginTime = 2 となります。 3.CAAnimationGroupでアニメーションをグループ化 以下のようにアニメーションをグループ化します。durationには実行したいアニメーションのdurationの合計を設定します。 let group = CAAnimationGroup() group.duration = 2.0 group.repeatCount = .infinity group.animations = [animation1,animation2] 4.グループ化したアニメーションを目的のレイヤに追加 self.shapeLayer.add(group, forKey: "animation-group") 5.完了 これで正しく動くと思います。 timingFunction 以下のように任意のtimingFunctionを設定するとより映えるかもしれません。 group.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut) おわりに 読んでいただいてありがとうございました。 ご指摘ご質問等ございましたら、コメントまたはDMまでお願いします。 この記事での内容はこちらのgithubページからダウンロードできます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Swift】インターンで学んだことまとめ

はじめに インターンで教えていただいたことをほぼ箇条書きでまとめていきます。 学生の方は短期でも長期でもインターンに参加してみることをお勧めします。理由は主に以下の4つです。 ・実務ではどのようなことをするのかを学ぶ事ができる。 ・チーム開発の難しさを経験する事ができる。 ・お金をもらって働くことで自分のコードに責任を持つ意識がでる。 ・現役エンジニアにフィードバックや新しい知識を教えてもらえる。 独学だとこれらは難しいです。プログラミングスクールは現役エンジニアにフィードバックをもらえたりするかもしれませんが、実務経験もチーム開発も経験できません。その上こちらが高額な金額を払うことになるので、大学生にはお勧めしません。(僕の調べた範囲ではiOSのプログラミングスクールの講師はそもそも現役エンジニアではなく、アルバイトの大学生がメンターにつくところが多かったので、実務未経験者が未経験者に教えたところで学べることはたかが知れてます。) 学んだこと 3月30日〜3月31日 1.サイドメニューをゆっくり表示させる -> layoutIfNeeded() 画面は常に更新されているわけではないので、移動したり大きさが変わったりしていても、すぐ画面に反映されません。なので「更新して下さい」とお願いする。 sampleConstraint.constant = isDisplay ? 0 : 100 UIView.animate(withDuration: 0.16) { self.view.layoutIfNeeded() } 2.プロトコルのメソッドの引数にはできるだけ抽象的なものを渡す。 3.カスタムクラスの配列をUserDefaultsに保存する。参考記事 4.enumでCaseIterableを使い、rawValueで値の配列を作る方法 enum Color: String, CaseIterable { case red = "赤" case blue = "青" case green = "緑" } let colors = Color.allCases // [.red, .blue, .green] let rawValueColors = Color.allCases.map { $0.rawValue } // ["赤", "青", "緑"] 5.SearchBarでキーボードの検索を押した時にキーボードを閉じるには.self.view.resignFirstResponder()ではなくsearchBar.resignFirstResponder()にする。 6.enumでErrorを定義するならtypealiasを以下のようにする。 enum SampleError: Error { case invalidURL case invalidResponse case unknown var description: String { ... } } typealias ResultHandler<T> = (Result<T, SampleError>) -> Void 4月1日〜4月30日 7.タプルにはラベルをつけることができる 8.reduce(into: )の使い方 let ages = [27, 18, 41, 7, 65, 33] var result = [String: [Int]]() for age in ages { let text = age < 20 ? "under" : "over" result[text, default: []].append(age) } // result == ["over": [27, 41, 65, 33], "under": [18, 7]] これをreduce(into: )を使うと以下のように書ける。 let ages = [27, 18, 41, 7, 65, 33] let result = ages.reduce(into: [String: [Int]]()) { result, age in let text = age < 20 ? "under" : "over" result[text, default: []].append(age) } // result == ["over": [27, 41, 65, 33], "under": [18, 7]] 9.guard letなどで、できるだけオプショナルチェイニングを使う 10.機能を追加したいときはサブクラスを利用できることを頭に入れておく 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. おわりに インターン楽しい
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Swift】サロンで学んだことまとめ

はじめに 僕が参加しているオンラインサロンで学んだことを適宜書いていこうかと思います。 学んだこと ・合計値を求めるときなどは積極的に高階関数を使う let text1 = "1" let text2 = "2" let text3 = "3" let text4 = "4" let text5 = "5" //合計値を求める let array: [String?] = [text1, text2, text3, text4, text5] let sum = array .map { $0 ?? "" } .map { Int($0) ?? 0 } .reduce(0, +) print(sum) // 15 ・enumで無闇にswitchを使わない enum Weather { case sunny case cloudy case rainy var text: String { switch self { case .sunny: return "晴れです" case .cloudy: return "曇りです" case .rainy: return "雨です" } } } print(Weather.sunny.text) 以下のようにすることで、メッセージを定義するだけなら足りる。 enum WeatherMessage { static let sunny = "晴れです" static let cloudy = "曇りです" static let rainy = "雨です" } print(Weather.sunny) ・正誤判定や成功失敗の判定の時はenumで結果を定義する、その判定結果をenumの連想値に持たせることで、扱いやすくする。 enum CalculationResult { case success(Double) case failure(String) } enum Calculation { case addition case subtraction case multiplication case division func calculate(_ num1: Double, _ num2: Double) -> CalculationResult { switch self { case .addition: return .success(num1 + num2) case .subtraction: return .success(num1 - num2) case .multiplication: return .success(num1 * num2) case .division: guard !num2.isZero else { return .failure("0で割れません") } return .success(num1 / num2) } } } ・guardを連ねる時はflatMapを使えないか考える func someFunc() { let optionalText: String? = "REON" guard let text = optionalText, let num = Int(text) else { return } print(num) } 以下のようにすることで、Optional(Optional())から2回アンラップしないといけないところをflatMapを使い、一行で済ませることができます。 func someFunc() { let optionalText: String? = "REON" guard let num = optionalText.flatMap({ Int($0) }) else { return } print(num) } ・使わないoutletなどは消去する ・guardをまとめる guard let a = Int(str) else { return } guard let b = Int(str2) else { return } を以下のようにまとめることができる guard let a = Int(str), let b = Int(str2) else { return } ・LayoutSubViewsを理解する ViewControllerの表示、画面の向きの変更など、ViewControllerのViewのframeが更新された時に呼び出される。 private let label: UILabel! override func viewDidLoad() { super.viewDidLoad() let label = UILabel() self.view.addSubview(label) } override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() setupLabel() } private func setupLabel() { label.text = "text" label.textAlignment = .center label.backgroundColor = .red label.frame = CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: self.view.frame.size.height ) } ・boundsとframeの違い (参考) bounds: 基準はローカルビュー frame: 基準はスーパービュー ・ ・ ・ ・ ・ ・ ・ ・ ・ ・ ・ ・ ・ ・ ・ ・ ・ ・ ・ ・ ・ ・ ・ ・ ・ ・ ・ ・ ・ ・ ・ ・ ・ ・ ・ ・ ・ ・ ・ ・ ・ おわりに 随時更新予定 オンラインサロンに入ってよかった!!!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

URLセッションとは

■URLセッションとは APIのデータを取得する際に使う APIのデータを取得する方法みたいな物。 ■書き方 func paseRequest(urlString: String) { let url = URL(String:urlString) let session = URLSession(configuration: .default) let task = session.dataTask(with:url) {(data, response, error) in if let error != nil { print(error) return } if let safeData = data { paseJSON(data:safeData) } } task.resume() } ■1行づつ説明 APIのurlを引数に持たせて持ってくる。 func paseRequest(urlString:String) 引数でもってきたurlStringを使いstruct URLに入れ込み変数urlに入れ込む let url = URL(string: urlString) データの転送管理してくれるセッションをつくる let session = URLSession(configuration: .default) セッションの機能を使ってエラー処理やデータの格納などをする。 クロージャを使っている。 関数の中に関数 let task = session.dataTask(with:url) {(data, response, error) in if let error != nil { print(error) return } if let safeData = data { //データを機械に読みこむためにデコードする dataを引数として渡す paseJSON(data:safeData) } } ■POINT★ クロージャは下記のメソッドを省略して書いてる。 func handle(data: Data?, response: URLResponse?, error: Error?) { } taskを開始する。 task.resume() こんな感じです。 後はJSONデータをデコードするための paseJSON(data:Data)メソッドを作ります。 他記事に記載してます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

シミュレーターのローカルにデータを保存する

do { let fileManager = FileManager.default let docs = try fileManager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false) let path = docs.appendingPathComponent("myFile.txt") let data = "Hello, world!".data(using: .utf8)! fileManager.createFile(atPath: path.path, contents: data, attributes: nil) let documentDirPath = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true) print(documentDirPath) } catch { print(error) } 上記はコピペでオッケーです。 また配置場所はドキュメント配下に置いてます。 イメージはこんな感じ。 printのやり方はiOS シミュレーターのフォルダの場所をprintしたいをみてください。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

iOS シミュレーターのフォルダの場所をprintしたい

let documentDirPath = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true) print(documentDirPath) 上記を配置したい場所にてprintすればオッケーです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【swift】複数のTableViewやCollectoinViewのスクロールを同期する方法

概要 一画面に複数の3つのcollection view(collectionView1,collectionView2,collectionView3とする)があるとする collectionView1とcollectionView2は縦軸の動きがそれぞれ連動、collectionView1とcollectionView3は横軸の動きがそれぞれ連動する仕様 という画面を作ろうとして少しつまづいたのでメモ書き 方法 /// 縦横スクロールを同期する private func syncronizeScroll(scrollView: UIScrollView) { // 縦スクロール if scrollView == collectionView1 { collectionView2.contentOffset.y = collectionView1.contentOffset.y } if scrollView == collectionView2 { collectionView1.contentOffset.y = collectionView2.contentOffset.y } // 横スクロール if scrollView == collectionView1 { collectionView3.contentOffset.x = collectionView1.contentOffset.x } if scrollView == collectionView3 { collectionView1.contentOffset.x = collectionView3.contentOffset.x } } func scrollViewDidScroll(_ scrollView: UIScrollView) { syncronizeScroll(scrollView: scrollView) } 当初はsetContentで実装していたが、スクロールはするけど慣性スクロールが全くしない状況だった(スワイプしたらすぐ止まるような感じ) おそらく処理が重すぎたためと思われたので、setContentOffsetではなく直接contentOffsetを設定する方法で実装
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Swift】自分の記事を見やすく整理する

はじめに Qiitaは20記事ごとにページが切り替わってしまうので、以前自分が書いた記事を探すときに検索がしにくいので不便です。 この記事で整理して自分の書いた記事を探しやすくします。 インターン オンラインサロン アルゴリズム SQL RxSwift 設計 原則 MVC MVP MVVM VIPER Storyboard Firebase API 文法 UIKitまわり その他 ライブラリ・フレームワーク その他 おわりに 見やすくなりました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Swift】⭐️自分の記事を見やすいように整理するための記事⭐️

はじめに Qiitaは20記事ごとにページが切り替わってしまうので、以前自分が書いた記事を探すときに検索がしにくいので不便です。 この記事で整理して自分の書いた記事を探しやすくします。 コードレビュー RxSwift 設計 MVVM MVC Storyboard API 文法 UIKitまわり その他 おわりに 見やすくなりました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む