- 投稿日:2020-11-22T23:09:51+09:00
【SwiftUI】Firebaseからデータ読み取り、ListViewのRowを更新する
はじめに
SwiftUIでFirebaseからデータを読み取り、これまで作成したカード内容を更新することを目的とする。
前回までの記事は以下を参考ください。
参考記事
【SwiftUi】TabViewとListの実装とViewのフォルダ管理開発環境
OSX 10.15.7 (Catalina)
Xcode 12.2.0
CocoaPods 1.10.0Firestoreへのドキュメントの追加
FirebaseのFirestoreデータベースは以下のように設定しました。
今回は書き込みの機能は設定していないため、直接入力しています。
今後はユーザーが投稿できるよう機能を実装します。
mapImageとuserImageはFireBaseのStorageに保存してある画像のURLを入力しています。
それぞれのファイルをStorageにアップロードします。
必要なURLは"ファイルの場所"にある"アクセストークン"をクリックし、内容をコピーし、Firestoreのそれぞれのフィールドに貼り付けます。NoSQLの知識が乏しいため、データの構造化がうまくありません。この点は今後学習とともに、現在のデータ構造と変更点が出てくると思います。
現状はFirebaseの使用方法と、SwiftUIの実装方法に焦点を当てます。参考資料
データベースの構造化
脱RDB脳!Firebase Databse導入のために考えた4つのポイントFirestoreからのデータの読み取り
datatype
structの実装Firebaseのデータベースと読み取りと書き出しを行うため、
struct
を実装します。
ここで実装したStruct
はgetData
クラスにおいて呼び出されます。
記載内容については読み取り時に、投稿日時を元に並び替えを行う場合はcreatedDate
のみで大丈夫です。
今回は過去の俳人の俳句を投稿しているため、並び替えのためにnow
を定義しました。struct dataType : Identifiable { var id : String var user : String var haiku : String var place : String var likes : String var mapImage : String var userImage : String var stamp : Date var createdDate : String var now :Date }
getData
クラスの実装Firebaseデータベースからドキュメントを読み取るための部分です。
特にエラーが出て、実装に時間がかかった部分はTimestampのデータの取り扱いです。
Timestamp firebase Cannot convert value of type 'Timestamp' to expected argument type 'String'
Timestampのデータの変化時にエラーが出てしまい、適切にデータベースからの読み取りができませんでした。
実装にあたっては以下のサイトを参考にしました。参考資料
Convert timestamp from Firebase to readable date
Firebase FirestoreのTimestamp型のdateへの変換class getData: ObservableObject { @Published var datas = [dataType]() init() { let db = Firestore.firestore() db.collection("ikku").order(by: "now", descending: true).addSnapshotListener{ (snap, err) in if err != nil{ print((err?.localizedDescription)!) return } for i in snap!.documentChanges{ if i.type == .added{ let id = i.document.documentID let user = i.document.get("userName") as! String let haiku = i.document.get("haiku") as! String let place = i.document.get("place") as! String let likes = i.document.get("likes") as! String let mapImage = i.document.get("mapImage") as! String let userImage = i.document.get("userImage") as! String let now = i.document.get("now") as! Timestamp let stamp = i.document.get("createdDate") as! Timestamp let formatterDate = DateFormatter() formatterDate.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ" let createdDate = formatterDate.string(from: stamp.dateValue()) DispatchQueue.main.async { self.datas.append(dataType(id: id, user: user, haiku: haiku, place: place, likes: likes, mapImage: mapImage, userImage: userImage, stamp: stamp.dateValue(), createdDate: createdDate, now: now.dateValue() )) } } } } } }ContentRowViewへの実装とデータベースからの情報更新
Firebaseからデータを読み取りにあたって、以下の点を変更しました。
変更前の実装については以下を参考ください。
【SwiftUi】経過時間の表示とListViewのセルデザイン変更点
1.ContentRowViewにおけるSDWebImageSwiftUIのインポートとvarで定義した変数を空にする。ContentRowView.swiftimport SwiftUI import Firebase import SDWebImageSwiftUI struct ContentRowView: View { var user = "" var userImage = "" var haiku = "" var mapImage = "" var place = "" var likes = "" // ○秒、○日、○年前を表示 var createdDate = "" static let formatter = RelativeDateTimeFormatter() var body: some View { ContentRowView.formatter.locale = Locale(identifier: "ja_JP") let fmt = ISO8601DateFormatter() let date1 = fmt.date(from: createdDate)! let components = Calendar.current.dateComponents( [.day, .year, .month, .minute, .second], from: Date(), to: date1 ) let timeAgo = ContentRowView.formatter.localizedString(from: components) return VStack { VStack { AnimatedImage(url: URL(string: mapImage)!) .resizable() //.aspectRatio(contentMode: .fit) .cornerRadius(12.0, antialiased: /*@START_MENU_TOKEN@*/true/*@END_MENU_TOKEN@*/) HStack { Spacer() Text(place).font(.caption).foregroundColor(.gray) .padding(.bottom, 5) } } Text(haiku).font(.title3).fontWeight(.bold) HStack { Image(systemName: "heart").font(.headline).foregroundColor(Color("pinkColor")) Text(likes).font(.headline).foregroundColor(Color("pinkColor")) Spacer() }.padding(.top, 5) HStack { AnimatedImage(url: URL(string: userImage)!) .resizable() .frame(width: 30, height: 30) .clipShape(Circle()) HStack { Text(user).font(.headline).fontWeight(.light) + Text("・").font(.headline).fontWeight(.light) + Text(timeAgo).font(.headline).fontWeight(.light) } Spacer() HStack { Image(systemName: "text.bubble").font(.title).foregroundColor(.gray) Image(systemName: "heart.fill").font(.title).foregroundColor(Color("pinkColor")) } } Spacer() }.padding(.top, 8).frame(height: 391) } }2.SDWebImageSwiftUIのインポート
→URLを画像に変換するためのフレームワークを追加AnimatedImage(url: URL(string: userImage)!) .resizable() .frame(width: 30, height: 30) .clipShape(Circle())3.HomeViewにおけるObservedObjectの実装
→Firebaseのデータベースからドキュメントを受け取り、ListのそれぞれのRowの情報を更新する。home.swiftstruct Home: View { @ObservedObject var observedData = getData() var body: some View { List(observedData.datas) { i in ContentRowView(user: i.user, userImage: i.userImage, haiku: i.haiku, mapImage: i.mapImage, place: i.place, likes: i.likes, createdDate: i.createdDate) }.environment(\.defaultMinListRowHeight, 391) } }今後実装予定のもの
1.カード部分のLike機能とComment機能の実装
2.mapTabやその他のTab機能の実装
3.Post機能の実装
4.ユーザー登録機能の実装
5.俳人の俳句の登録以上です。
- 投稿日:2020-11-22T23:09:51+09:00
【SwiftUI】Firebaseからデータを読み取り、ListViewのRowを更新する
はじめに
SwiftUIでFirebaseからデータを読み取り、これまで作成したカード内容を更新することを目的とする。
前回までの記事は以下を参考ください。
参考記事
【SwiftUi】TabViewとListの実装とViewのフォルダ管理開発環境
OSX 10.15.7 (Catalina)
Xcode 12.2.0
CocoaPods 1.10.0Firestoreへのドキュメントの追加
FirebaseのFirestoreデータベースは以下のように設定しました。
今回は書き込みの機能は設定していないため、直接入力しています。
今後はユーザーが投稿できるよう機能を実装します。
mapImageとuserImageはFireBaseのStorageに保存してある画像のURLを入力しています。
それぞれのファイルをStorageにアップロードします。
必要なURLは"ファイルの場所"にある"アクセストークン"をクリックし、内容をコピーし、Firestoreのそれぞれのフィールドに貼り付けます。
NoSQLの知識が乏しいため、データの構造化がうまくありません。この点は今後学習とともに、現在のデータ構造と変更点が出てくると思います。
現状はFirebaseの使用方法と、SwiftUIの実装方法に焦点を当てます。参考資料
データベースの構造化
脱RDB脳!Firebase Databse導入のために考えた4つのポイントFirestoreからのデータの読み取り
datatype
structの実装Firebaseのデータベースと読み取りと書き出しを行うため、
struct
を実装します。
ここで実装したStruct
はgetData
クラスにおいて呼び出されます。
記載内容については読み取り時に、投稿日時を元に並び替えを行う場合はcreatedDate
のみで大丈夫です。
今回は過去の俳人の俳句を投稿しているため、並び替えのためにnow
を定義しました。struct dataType : Identifiable { var id : String var user : String var haiku : String var place : String var likes : String var mapImage : String var userImage : String var stamp : Date var createdDate : String var now :Date }
getData
クラスの実装Firebaseデータベースからドキュメントを読み取るための部分です。
特にエラーが出て、実装に時間がかかった部分はTimestampのデータの取り扱いです。
Timestamp firebase Cannot convert value of type 'Timestamp' to expected argument type 'String'
Timestampのデータの変換時にエラーが出てしまい、適切にデータベースからの読み取りができませんでした。
実装にあたっては以下のサイトを参考にしました。参考資料
Convert timestamp from Firebase to readable date
Firebase FirestoreのTimestamp型のdateへの変換class getData: ObservableObject { @Published var datas = [dataType]() init() { let db = Firestore.firestore() db.collection("ikku").order(by: "now", descending: true).addSnapshotListener{ (snap, err) in if err != nil{ print((err?.localizedDescription)!) return } for i in snap!.documentChanges{ if i.type == .added{ let id = i.document.documentID let user = i.document.get("userName") as! String let haiku = i.document.get("haiku") as! String let place = i.document.get("place") as! String let likes = i.document.get("likes") as! String let mapImage = i.document.get("mapImage") as! String let userImage = i.document.get("userImage") as! String let now = i.document.get("now") as! Timestamp let stamp = i.document.get("createdDate") as! Timestamp let formatterDate = DateFormatter() formatterDate.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ" let createdDate = formatterDate.string(from: stamp.dateValue()) DispatchQueue.main.async { self.datas.append(dataType(id: id, user: user, haiku: haiku, place: place, likes: likes, mapImage: mapImage, userImage: userImage, stamp: stamp.dateValue(), createdDate: createdDate, now: now.dateValue() )) } } } } } }ContentRowViewへの実装とデータベースからの情報更新
Firebaseからデータを読み取りにあたって、以下の点を変更しました。
変更前の実装については以下を参考ください。
【SwiftUi】経過時間の表示とListViewのセルデザイン変更点
1.ContentRowViewにおけるSDWebImageSwiftUIのインポートとvarで定義した変数を空にする。ContentRowView.swiftimport SwiftUI import Firebase import SDWebImageSwiftUI struct ContentRowView: View { var user = "" var userImage = "" var haiku = "" var mapImage = "" var place = "" var likes = "" // ○秒、○日、○年前を表示 var createdDate = "" static let formatter = RelativeDateTimeFormatter() var body: some View { ContentRowView.formatter.locale = Locale(identifier: "ja_JP") let fmt = ISO8601DateFormatter() let date1 = fmt.date(from: createdDate)! let components = Calendar.current.dateComponents( [.day, .year, .month, .minute, .second], from: Date(), to: date1 ) let timeAgo = ContentRowView.formatter.localizedString(from: components) return VStack { VStack { AnimatedImage(url: URL(string: mapImage)!) .resizable() //.aspectRatio(contentMode: .fit) .cornerRadius(12.0, antialiased: /*@START_MENU_TOKEN@*/true/*@END_MENU_TOKEN@*/) HStack { Spacer() Text(place).font(.caption).foregroundColor(.gray) .padding(.bottom, 5) } } Text(haiku).font(.title3).fontWeight(.bold) HStack { Image(systemName: "heart").font(.headline).foregroundColor(Color("pinkColor")) Text(likes).font(.headline).foregroundColor(Color("pinkColor")) Spacer() }.padding(.top, 5) HStack { AnimatedImage(url: URL(string: userImage)!) .resizable() .frame(width: 30, height: 30) .clipShape(Circle()) HStack { Text(user).font(.headline).fontWeight(.light) + Text("・").font(.headline).fontWeight(.light) + Text(timeAgo).font(.headline).fontWeight(.light) } Spacer() HStack { Image(systemName: "text.bubble").font(.title).foregroundColor(.gray) Image(systemName: "heart.fill").font(.title).foregroundColor(Color("pinkColor")) } } Spacer() }.padding(.top, 8).frame(height: 391) } }2.SDWebImageSwiftUIのインポート
→URLを画像に変換するためのフレームワークを追加AnimatedImage(url: URL(string: userImage)!) .resizable() .frame(width: 30, height: 30) .clipShape(Circle())3.HomeViewにおけるObservedObjectの実装
→Firebaseのデータベースからドキュメントを受け取り、ListのそれぞれのRowの情報を更新する。home.swiftstruct Home: View { @ObservedObject var observedData = getData() var body: some View { List(observedData.datas) { i in ContentRowView(user: i.user, userImage: i.userImage, haiku: i.haiku, mapImage: i.mapImage, place: i.place, likes: i.likes, createdDate: i.createdDate) }.environment(\.defaultMinListRowHeight, 391) } }今後実装予定のもの
1.カード部分のLike機能とComment機能の実装
2.mapTabやその他のTab機能の実装
3.Post機能の実装
4.ユーザー登録機能の実装
5.俳人の俳句の登録以上です。
- 投稿日:2020-11-22T22:36:12+09:00
UE4でIOS/Android用のアイコン画像を爆速で入れる
UE4でIOS/Android用アイコンを爆速で入れよう
https://qiita.com/Chi__no_/items/f7d9f8e943d0da0b5f80
この記事は↑の画像作成スクリプトを実行している事が前提です。UE4は一つ一つの画像を選択する度にプロジェクトルートから辿らないと行けないから面倒くさい。。。
そんな問題を一気に解決しちゃおうって話!一番上の記事の内容を実行するとこんな画像ファイル達が出来ると思う。
Engineのデフォルトに設定する。
そしたらEngineのPathに行く。
Androidでは色々ファイルが分かれているから各自でやって欲しい。
参考Directory→C:\Program Files\UnrealEngine\UE_4.25\Engine\Build\Android\Java\res
IOSの場合:Engine\Build\IOS\Resources\Graphics
僕の場合は↓のPath
C:\Program Files\UnrealEngine\UE_4.25\Engine\Build\IOS\Resources\Graphics
↑のPathに行ったらとりあえずBackUpフォルダを作ってUE4のデフォルト画像をBackUpの中にコピーしておこう
そのフォルダに前記事で作った大量の画像データを全部コピーしてやる。
そうしたらUE4を再起動するだけ!
もし他の画像に変更しているなら全ての画像をデフォルトにリセット→再起動で画像が変わる。メリットと欠点
メリット
何より早い。面倒くさい作業がほぼないから楽
画像を変えたい時もスピーディーに対応できる。欠点
再起動したタイミングで毎回フォルダから更新するため、複数のプロジェクトを同時進行して居る時、ビルド毎に画像を差し替える必要がある。
最後に
エラー、不具合等あれば報告して下さい!
色々雑でしたがqiita初心者なので多めに見てやって下さい。中学生の時に起業し、今高校三年生で色々なプロダクトをリリースしています!一回だけでも見ていって下さい!
https://geekline.biz ←会社のページ
- 投稿日:2020-11-22T17:38:53+09:00
モバイルアプリのアイコン画像用意する面倒くさくない?自動化しちゃおう!
PhotoShopでいちいち何十回も解像度を変える作業めんどくさ・・・
AndroidとiPhone合わせるとこのくらいの画像が必要になる。
しかもiPhoneの画像は20@2xみたいな語尾を付けているからややこしい。
だからPhotoshopのスクリプトで自動化してしまおう
まず出力したいPSDのレイヤーは全てスマートオブジェクトにして下さい。←これ重要
これを忘れると出力された画像がボケるから気をつけて!
レイヤーを右クリック→スマートオブジェクトに変換
全部出来たらこんな感じで変なマークが追加される
スクリプトファイルダウンロード
https://drive.google.com/file/d/19dk19n3V2ciiC1BXWAHbdFAilxHzvqO_/view?usp=sharing
↑からスクリプトファイルをダウンロードしたらPhotoShopのディレクトリを探してPhotoshop\Presets\Scripts下に配置
僕の場合は↓だった
C:\Program Files\Adobe\Adobe Photoshop 2021\Presets\Scripts実行
念の為Photoshopでは何も開いていない状態からスタートする事をおすすめする。
メニュー→ファイル→スクリプト→MakePngsForMobileを選択
ファイル選択ダイアログが出現するので実行したいPSDを選択してOKを押す
PSDとPNGが同ディレクトリに出力されていれば成功!
もしこのスクリプトが既存であったり、エラー、不具合等あれば報告して下さい!
色々雑でしたがqiita初投稿なので多めに見てやって下さい。
- 投稿日:2020-11-22T17:38:53+09:00
モバイルアプリのアイコン画像用意するの面倒くさくない?自動化しちゃおう!
PhotoShopでいちいち何十回も解像度を変える作業めんどくさ・・・
AndroidとiPhone合わせるとこのくらいの画像が必要になる。
しかもiPhoneの画像は20@2xみたいな語尾を付けているからややこしい。
だからPhotoshopのスクリプトで自動化してしまおう
まず出力したいPSDのレイヤーは全てスマートオブジェクトにして下さい。←これ重要
これを忘れると出力された画像がボケるから気をつけて!
レイヤーを右クリック→スマートオブジェクトに変換
全部出来たらこんな感じで変なマークが追加される
スクリプトファイルダウンロード
https://drive.google.com/file/d/19dk19n3V2ciiC1BXWAHbdFAilxHzvqO_/view?usp=sharing
↑からスクリプトファイルをダウンロードしたらPhotoShopのディレクトリを探してPhotoshop\Presets\Scripts下に配置
僕の場合は↓だった
C:\Program Files\Adobe\Adobe Photoshop 2021\Presets\Scripts実行
念の為Photoshopでは何も開いていない状態からスタートする事をおすすめする。
メニュー→ファイル→スクリプト→MakePngsForMobileを選択
ファイル選択ダイアログが出現するので実行したいPSDを選択してOKを押す
PSDとPNGが同ディレクトリに出力されていれば成功!
もしこのスクリプトが既存であったり、エラー、不具合等あれば報告して下さい!
色々雑でしたがqiita初投稿なので多めに見てやって下さい。
- 投稿日:2020-11-22T12:29:00+09:00
Develop in Swift Data CollectionsでiOS開発を学ぶ (2): Lesson 1.3からLesson1.4
https://qiita.com/mk2/items/6091f8eb195fa3237c4e の続き
Lesson 1.3: Model-View-Controller
iOS/Mac開発といえば、MVCだと個人的には思っています。(SwiftUIの登場で考え方も変わるのかもしれませんが…)
View Controller
、Model Controller
はなんとなく知っていたのですが、Helper Controller
という考え方があるのは知りませんでした。ただ、この説明を見る限り、ユーティリティ的な感じがしますね。“Helper Controllers
Helper controllers are useful anytime you want to consolidate related data or functionality so that it can be accessed by other objects in your app. One common example of a helper controller is a NetworkController, which manages all the network requests in a given app.”抜粋:: Apple Education “Develop in Swift Data Collections”。 Apple Inc. - Education、2020年 Apple Books https://books.apple.com/jp/book/develop-in-swift-data-collections/id1511183970
また、下記のようにソースコード、リソースをグルーピングするのが良いというのはiOS開発をがっつりやったことがないので、へーという感じでした。
“Many developers make groups for the following:
- View controllers
- Views
- Models
- Model controllers
- Other controllers
- Protocols
- Extensions
- Resources
- Storyboards
- Frameworks
”抜粋:: Apple Education “Develop in Swift Data Collections”。 Apple Inc. - Education、2020年 Apple Books https://books.apple.com/jp/book/develop-in-swift-data-collections/id1511183970
実験
今回は、好きなアスリートを記入できるアプリのようです。最終的に作成したコードを載せておきますね。
Athlete.swiftimport Foundation struct Athlete { var name: String var age: Int var league: String var team: String var description: String { return "\(name) is \(age) years old and plays for the \(team) in the \(league)." } }AthleteTableViewController.swiftimport UIKit class AthleteTableViewController: UITableViewController { struct PropertyKeys { static let athleteCell = "AthleteCell" } var athletes: [Athlete] = [] override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) tableView.reloadData() } // MARK: - Table view data source override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return athletes.count } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: PropertyKeys.athleteCell, for: indexPath) let athlete = athletes[indexPath.row] cell.textLabel?.text = athlete.name cell.detailTextLabel?.text = athlete.description return cell } @IBSegueAction func addAthlete(_ coder: NSCoder) -> AthleteFormViewController? { return AthleteFormViewController(coder: coder) } @IBSegueAction func editAthlete(_ coder: NSCoder, sender: Any?) -> AthleteFormViewController? { let athleteToEdit: Athlete? if let cell = sender as? UITableViewCell, let indexPath = tableView.indexPath(for: cell) { athleteToEdit = athletes[indexPath.row] } else { athleteToEdit = nil } return AthleteFormViewController(coder: coder, athlete: athleteToEdit) } @IBAction func backToTable(_ segue: UIStoryboardSegue) { guard let controller = segue.source as? AthleteFormViewController, let athlete = controller.athlete else { return } if let selectedIndexPath = tableView.indexPathForSelectedRow { athletes[selectedIndexPath.row] = athlete } else { athletes.append(athlete) } } }AthleteFormViewController.swiftimport UIKit class AthleteFormViewController: UIViewController { @IBOutlet weak var nameTextField: UITextField! @IBOutlet weak var ageTextField: UITextField! @IBOutlet weak var leagueTextField: UITextField! @IBOutlet weak var teamTextField: UITextField! var athlete: Athlete? required init?(coder: NSCoder) { self.athlete = nil super.init(coder: coder) } init?(coder: NSCoder, athlete: Athlete?) { self.athlete = athlete super.init(coder: coder) } override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. updateView() } func updateView() { nameTextField.text = athlete?.name if let age = athlete?.age { ageTextField.text = "\(age)" } leagueTextField.text = athlete?.league teamTextField.text = athlete?.team } /* // MARK: - Navigation // In a storyboard-based application, you will often want to do a little preparation before navigation override func prepare(for segue: UIStoryboardSegue, sender: Any?) { // Get the new view controller using segue.destination. // Pass the selected object to the new view controller. } */ @IBAction func save(_ sender: Any) { guard let name = nameTextField.text, let ageString = ageTextField.text, let age = Int(ageString), let league = leagueTextField.text, let team = teamTextField.text else { return } athlete = Athlete(name: name, age: age, league: league, team: team) performSegue(withIdentifier: "SaveAthlete", sender: self) } }わかりにくかった点
本の通りに進めていけば、多分だいたい完成させられると思うのですが、
Step 6 Perform the Unwind Segue in Storyboard
の節の下記の記述がよくわからずかなり四苦八苦しました。“Finally, you need to create the unwind segue. In the storyboard, Control-drag from the athlete form scene in the Document Outline to the view controller's Exit, then choose your unwind segue. Give this segue a name by selecting it in the Document Outline and adding the identifier in the Attributes inspector.”
抜粋:: Apple Education “Develop in Swift Data Collections”。 Apple Inc. - Education、2020年 Apple Books https://books.apple.com/jp/book/develop-in-swift-data-collections/id1511183970
正解は、下のようにAthele Form View ControllerからcontrolドラッグでExitまで接続すれば良い感じでした。
Lesson 1.4: Scroll Views
Scrolling Form
AutoLayoutの設定が難しいですね。自分は5、6回constrainsを全部消してやりなおしました。また、折角なのでiOS各端末で幅を綺麗に表示させたいと思ったのですが、そこが結構大変でした。下のように、Stack Viewの幅を親のScroll Viewのwidthと同じにすれば、各端末で同じにできるようです。
実験
画像をズームできるようにするものでした。
ViewController.swiftimport UIKit class ViewController: UIViewController, UIScrollViewDelegate { @IBOutlet weak var scrollView: UIScrollView! @IBOutlet weak var imageView: UIImageView! override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. scrollView.delegate = self } override func viewDidAppear(_ animated: Bool) { updateZoomFor(size: view.bounds.size) } func viewForZooming(in scrollView: UIScrollView) -> UIView? { return imageView } func updateZoomFor(size: CGSize) { let widthScale = size.width / imageView.bounds.width let heightScale = size.height / imageView.bounds.height let scale = min(widthScale, heightScale) scrollView.minimumZoomScale = scale scrollView.zoomScale = scale } }
- 投稿日:2020-11-22T09:21:17+09:00
[macOS/iOS] Autolayout覚書
はじめに
本文書では, macOS/iOSのソフトウェアのautolayout処理に関して、気づいた点/注意すべき点についてまとめます。おそらく目新しい内容はなく、よしなし事を順不同で記載します。
Autolayout処理
サイズ変更
- Viewのサイズ変更は、setFrameSizeで行われる
優先順位
NSLayoutConstraint.Priorityで定義される定数の値とその意味です(macOS 10.15)。説明はマニュアルのgoogle翻訳です。
名前 即値 説明 required 1000.0 必要な制約 defaultHigh 750.0 ボタンがコンテンツの圧縮に抵抗する優先度レベル dragThatCanResizeWindow 510.0 ウィンドウのサイズを変更する可能性のあるドラッグの適切な優先度レベル windowSizeStayPut 500.0 ウィンドウの現在のサイズの優先度 dragThatCannotResizeWindow 490.0 たとえば、分割ビューの仕切りがドラッグされる優先度レベル defaultLow: 250.0 ボタンがその内容を水平方向に保持する優先度レベル fittingSizeCompression 50.0 fitingSizeの結果は、ビューのコンテンツを表示するのに十分な大きさのサイズです Autolayout下でのViewの処理
NSTextView
- スクロールバー付きのNSTextViewのサイズ変更については、NSTextViewそのものでなく、それを含有するNSScrollViewに対して実施する。
- 投稿日:2020-11-22T01:47:23+09:00
構造体について【Swift】
構造体とは?
- 変数、定数、関数を1つのグループとしてまとめる仕組み。
- 構造体の中で作られた変数・定数を プロパティ 、関数を メソッド という。
- 構造体の中で作られた変数・定数を初期化することを イニシャライズ という。
- 構造体を利用するにはインスタンス化が必要。
- データ型は全て構造体である。(
Int
、Float
、Double
、String
、Bool
)実装例
struct Human { // プロパティを定義 let name: String let age: Int // initを使用することでイニシャライザ定義 init(name: String, age: Int) { self.name = name self.age = age } func introduction() { print("私の名前は\(name)です。年齢は\(age)歳です。") } } // Human構造体をインスタンス化 let human = Human(name: "山田太郎", age: 25) // introductionメソッドへアクセス human.introduction() // 私の名前は山田太郎です。年齢は25歳です。定義方法
struct 構造体名 { // 構造体の定義 }プロパティとは?
インスタンスプロパティ
- 型のインスタンスに紐付くプロパティ
- 型のインスタンスに紐付くため、インスタンス化しなければ使うことができません。
スタティックプロパティ
- 型自身に紐付くプロパティ
- 型の性質を表すプロパティを定義したいとき(大規模なプロジェクト以外はあまり使う機会がない?)
- 静的という意味なので
let
を推奨インスタンスプロパティとスタティックプロパティの違い
struct Human { // スタティックプロパティ static let name = "山田太郎" // インスタンスプロパティ let age = 25 } // スタティックプロパティへアクセス Human.name // OK Human().name // NG // インスタンスプロパティへアクセス Human.age // NG Human().age // OK
型名().プロパティ名
(型名に()
をつけることでデフォルトのイニシャライザが働き、インスタンス化される)イニシャライザとは?
- プロパティを初期化する為のもの(他の言語ではコンストラクタとほぼ同様)
- インスタンス化時に実行される特殊なメソッド
書式
init(引数名: 型名, ...) { // selfは構造体自身のインスタンスを意味する self.プロパティ名 = 値(引数を使ったりする) }メンバーワイズイニシャライザ
- デフォルトで用意されている特殊なイニシャライザ
- イニシャライザは定義しない
- 単純な初期化ならメンバーワイズイニシャライザが使えるが、イニシャライザを定義すると使えなくなる
インスタンス化の方法
イニシャライザを定義していない時
let 変数名 = 構造体名()イニシャライザを定義した時
let 変数名 = 構造体名(引数名: 型名, ...)メンバーワイズイニシャライザを使用する時
let 変数名 = 構造体名(プロパティ名: 値, ...) // プロパティを直接指定するアクセス方法
let 変数名 = 構造体名() 変数名.プロパティ名 変数名.メソッド名参考