- 投稿日:2021-01-17T22:56:13+09:00
JUCE: iOSアプリにSplash Screen (Launch Screen) を追加する
問題
ボクのようなアマチュアJUCERはProjucerが無いと何もできないし、でもiOS用のアプリを作りたい。でも、iOS個別のお作法なんて知らないし、UIViewController?は?という状況でも起動時に表示されるSplash Screen(Launch Screen)を追加しなければならないが、勿論全くやり方がわからないし、世の中に転がってる情報は我々には冷たすぎる。
神様発見
まず、下記の記事がなければ完全に死亡でした。本当にありがとうございます。
https://qiita.com/Riscait/items/89eca8260535d020e82e
具体的には「Is Initial View Controllerがないせい」の部分です。ビンゴ!でした。前提
もちろんProjucerでアプリを作っている。
解決方法
Storyboardのファイルを作成
まず、Xcode上でStoryboardなるものを作成しなければなりません。Xcodeで、メニューのFile->New->File...を選択。
Storybosardを選択してからNextをクリックし、Splash.storyboardとして作成します。そしたら、Xcodeを一旦閉じます。←ココ重要Projucerでstoryboardを追加
ProjucerのFile explorerで、作成したSplash.storyboardをプロジェクトに追加します。Binary Resourceのチェックを外し、Xcode Resourceのチェックを入れます。あと、LaunchImage.pngという適当な画像も同じくXcode Resourceとして追加しておきます。
次に、ExportersのXcode(iOS)でCustom Launch StoryboardにSplash.storyboardと記述します。
Xcodeでstoryboardを編集
ここでProjucerからXcodeを起動すると、Resourcesの中にSplash.storyboardが入っているはず。かつ、プロジェクト設定のGeneralでLaunch Screen FileがSplashになっているはず。
そしたら、左側のペインでSplash.storyboardを選択して編集します。この画面、あぁ、そっちの人はそうやって作ってるみたいだよね…という画面ですよね。我々にとっては未知の世界。しかし何も表示されていないし何をして良いかすらわからない。冷たすぎる…。よく見ると、実はウィンドウの右上に「+」というボタンがあって、ここから始まります。クリック。
検索窓にview conとでも入れればView Controllerというのが出てくるので、それをXcodeのエディタ部分、だだっ広い灰色のエリアにドラッグしてドロップ。
ほらほら出てきた!なんかそっちの人たちがiOSのアプリをSwiftでサクサク作ってそうな画面!
ここで更に右上の「+」ボタンをクリックして、今度はimageとでも打つと、Image Viewというのが出るので、それをiPhoneっぽい画面のところにドロップ。
アレだな、さっきのが我々の世界でいうWindow+メインのComponentで、今追加したのがImage Componentだな。きっと。
このままだと画像が何もないので、ドロップしたUIImageViewを選択して、右ペインの上の方にあるスライダーが並んだようなアイコンをクリックして、ImageにLaunchImage.pngを選択してやる。
このままだと画面の真ん中にちょこんとイメージがあるだけでしょぼいので、画面いっぱいに広げる。ただ、それだけだと他のデバイスで表示する時におかしなことになってしまうので、右下の変なアイコンをクリックして、上下左右に吸い付いて拡大縮小されるようにする。"Add 4 Constraints"をクリックするの忘れずに!これはProjucerでいうところのアレ!←
ちなみに、View ControllerのViewの色を変えると、余白になってしまう部分の色を変えられる。
で、これで終わりではなく、View Controllerを選択し、次の2点をチェック。
"Use as Launch Screen"にチェックが入っていること。
"Is Initial View Controller"にチェックを入れること。これが冒頭に紹介した神様のお達し。
ちなみに、横向き専用アプリの場合、上記だと間違ってます。Orientationでデバイスを回して横向きにした状態で作ろう!
これでビルドすると起動時の画面が真っ黒じゃなくてちゃんと出るよ!
- 投稿日:2021-01-17T22:38:35+09:00
Swift チャットアプリのチャット画面が作りたい!
チャットアプリのチャット画面に必要なもの
コロナ禍の中なかなか就職が決まらず暇なので、チャットアプリのチャット画面を作り方を記事にしたいと思います。
チャット画面に最低限必要なものってなんだと思いますか?
今回は僕が勝手に思うチャット画面に最低限必要なもの
で実装していきます。
必要なコンポーネントは以下tableView: メッセージを一覧表示するのに使います。
MessageCell: プロフィールとかメッセージをまとめます。
profileImageView: プロフィール画像を表示をします。
messageLabel: メッセージの表示をします。他にも送信時間表示用のlabelとかは?と思われると思いますが、データベースとか使って実際にメッセージの送受信をするわけではないので今回はこれだけにします。
実装
まずはViewControllerの方から
class ViewController: UIViewController { private let tableView = UITableView() //メッセージを格納する用の配列 private var messages = [String]() override func viewDidLoad() { super.viewDidLoad() initTableView() initMessages() view.addSubview(tableView) } //tableViewの設定 private func initTableView() { tableView.tableFooterView = UIView() tableView.separatorStyle = .none tableView.register(MessageCell.self, forCellReuseIdentifier: MessageCell.id) tableView.delegate = self tableView.dataSource = self tableView.frame = view.bounds } //messagesにメッセージを追加 private func initMessages() { messages.append("こんにちは") messages.append("今日の12時ごろ予定空いてますか?") messages.append("ひまですね") messages.append("寿限無(じゅげむ) 寿限無(じゅげむ) 五劫(ごこう)のすりきれ 海砂利(かいじゃり)水魚(すいぎょ)の水行末(すいぎょうまつ) 雲来末(うんらいまつ) 風来末(ふうらいまつ) 食(く)う寝(ね)るところに 住(す)むところ やぶらこうじの ぶらこうじ パイポ パイポ パイポの シューリンガン シューリンガンの グーリンダイ グーリンダイの ポンポコピーのポンポコナの 長久命(ちょうきゅうめい)の長助(ちょうすけ)") } } extension ViewController: UITableViewDelegate, UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return messages.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: MessageCell.id, for: indexPath) as! MessageCell cell.message = messages[indexPath.row] return cell } }
initTableView()
の中を見ていきましょう。
tableFooterView = UIView()
は要素数以上の時に表示される空のcellを消すためです。
separatorStyle = .none
はcellとcellの間の薄い灰色のボーダー線を消します。
register
はカスタムセルを表示するためです。
こちらを参照してください。
コードでカスタムセルを使う時register(_:forCellReuseIdentifier:)関数を入れそびれていた
ViewControllerの中についての説明はこれくらいですかね。次はカスタムセルを作っていきます。
Message.swiftclass MessageCell: UITableViewCell { static let id = "MessageCell" public var message: String? { didSet { messageLabel.text = message } } private let profileImageView: UIImageView = { let imageView = UIImageView() imageView.image = #imageLiteral(resourceName: "image.jpeg") imageView.layer.masksToBounds = true imageView.layer.cornerRadius = 25 return imageView }() private let backView: UIView = { let view = UIView() view.backgroundColor = .opaqueSeparator view.layer.cornerRadius = 20 view.layer.masksToBounds = true return view }() private let messageLabel: UILabel = { let label = UILabel() label.font = .systemFont(ofSize: 16) // labelの行数を指定 label.numberOfLines = 0 return label }() override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) addSubview(profileImageView) addSubview(backView) backView.addSubview(messageLabel) setupAutoLayout() } private func setupAutoLayout() { profileImageView.translatesAutoresizingMaskIntoConstraints = false backView.translatesAutoresizingMaskIntoConstraints = false messageLabel.translatesAutoresizingMaskIntoConstraints = false let constraints = [ profileImageView.topAnchor.constraint(equalTo: topAnchor, constant: 10), profileImageView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 10), // profileImageView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -10), profileImageView.widthAnchor.constraint(equalToConstant: 50), profileImageView.heightAnchor.constraint(equalToConstant: 50), backView.topAnchor.constraint(equalTo: profileImageView.topAnchor), backView.leadingAnchor.constraint(equalTo: profileImageView.trailingAnchor, constant: 5), backView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -10), backView.widthAnchor.constraint(lessThanOrEqualToConstant: 250), messageLabel.topAnchor.constraint(equalTo: backView.topAnchor, constant: 8), messageLabel.leadingAnchor.constraint(equalTo: backView.leadingAnchor, constant: 8), messageLabel.bottomAnchor.constraint(equalTo: backView.bottomAnchor, constant: -8), messageLabel.trailingAnchor.constraint(equalTo: backView.trailingAnchor, constant: -8), ] NSLayoutConstraint.activate(constraints) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } }
profileImageView
とbackView
とmessageLabel
の宣言をしています。
クロージャでまとめるといいですね。
backView
はmessageLabel
に直接背景色を指定してしまうと背景と文字の間に余白がなくなって見ずらいのでいい感じに余白を作るために使います。
setupAutoLayout
の中を見ていきましょう。
各view
にtranslatesAutoresizingMaskIntoConstraints = false
という記述があります。
これをしないとautolayout
が使えないことがあるらしいです。
Auto Layoutをコードから使おう
constraints
からはautoLayout
でレイアウトを設定しています。
配列でまとめているのでNSLayoutConstraint.activate()
でまとめて有効にできます。
一部分だけ変更したい場合は[viewの変数名].topAnchor.constraint(equalTo: topAnchor).isActive = trueでできます。
まとめ
今回は相手側だけになりましたが制約を変更すれば自分の方も作れます。
もっと簡単にできる方法があれば教えてください。
わざわざ自分で実装するのめんどくさいという方はMessageKit
を調べてみてください。冒頭でも言ったように就職先募集してます。
あー、優しい企業様就職先をください!
- 投稿日:2021-01-17T19:55:20+09:00
SceneEditorから追加したSCNActionに、コードからアクセスする
SCNActionはコードからもしくはSceneEditorから追加できます。
SceneEditorから追加する方法はApple Engineさんの以下の記事がわかりやすいかと思います。iOS で SceneKit を試す(Swift 3) その15 - Scene Editor の Action Editor を使ってみる
SceneEditorで追加したアクションをコードからリピートしたり組み合わせたりしたい時に。
ノードがアクションを持っているかどうかの確認
node.hasActionsノードが持っているアクションのKeyの確認
SceneEditorからアクションを追加した場合、英数字の文字列がデフォルトで割り当てられます。
node.actionKeysアクションの取得
たとえば、ノードに割り当てられているアクションの最初のものを取得するには以下
let actionFromNode = node.action(forKey: node.actionKeys[0])取得したアクションは、SCNActionとして再利用できる
anotherNode.runAction(actionFromNode)?
フリーランスエンジニアです。
お仕事のご相談こちらまで
rockyshikoku@gmail.comCore MLを使ったアプリを作っています。
機械学習関連の情報を発信しています。
- 投稿日:2021-01-17T19:51:28+09:00
Swift UIScrollViewの使い方
UIScrollView
をコードで実装独学でアプリ制作しているんですけど、そういえば
scrollView
使ったことないなと思ったので勉強しました!
まず、全く調べずにscrollView
を使ってみようとしました。
そのコードがこちらclass ViewController: UIViewController { private let label = UILabel() override func viewDidLoad() { super.viewDidLoad() let scrollView = UIScrollView() scrollView.frame = .init(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height) view.addSubview(scrollView) scrollView.addSubview(label) label.text = "hello world" label.frame = .init(x: 0, y: 0, width: 100, height: 50) label.center = scrollView.center } }結果はスクロールできませんでした。
label
はscrollView
がちゃんと表示されているか確認するために追加しています。なんで!?と思い調べてみると
contentSize
というものを指定しないといけないらしい。
contentSize
はスクロール領域を決めるらしい。
ということで修正したものがこちらclass ViewController: UIViewController { private let label = UILabel() override func viewDidLoad() { super.viewDidLoad() let scrollView = UIScrollView() //scrollViewの大きさを設定。 scrollView.frame = .init(x: 0, y: 0, width: view.frame.size.width * 2, height: view.frame.size.height * 2) //スクロール領域の設定 scrollView.contentSize = CGSize(width:view.frame.size.width * 2, height:view.frame.size.height * 2) //scrollViewをviewのSubViewとして追加 view.addSubview(scrollView) scrollView.addSubview(label) label.text = "hello world" label.frame = .init(x: 0, y: 0, width: 100, height: 50) label.center = scrollView.center } }これでどうだ!と思いビルドしましたがスクロールできませんでした。
さらに調べると、
frame
がcontentSize
より小さくないといけないみたい。class ViewController: UIViewController { private let label = UILabel() override func viewDidLoad() { super.viewDidLoad() let scrollView = UIScrollView() //scrollViewの大きさを設定。 scrollView.frame = .init(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height) //スクロール領域の設定 scrollView.contentSize = CGSize(width:view.frame.size.width * 2, height:view.frame.size.height * 2) //scrollViewをviewのSubViewとして追加 view.addSubview(scrollView) scrollView.addSubview(label) label.text = "hello world" label.frame = .init(x: 0, y: 0, width: 100, height: 50) label.center = scrollView.center } }結果は...いけたーーーー!!!
まとめ
UIScrollView
を使うときは
contentSize
を指定しないといけない
contentSize
はframe
より大きくないといけないということに注意して実装しましょう!
引用
- 投稿日:2021-01-17T16:09:01+09:00
[Swift] Realmを実装して分かったこと色々
はじめに
Realmを以下の記事を参考に実装しました。
https://qiita.com/pe-ta/items/616e0dbd364179ca284b
以下のリンクから動き確認できます。
https://twitter.com/vex3ex5frd2xcvg/status/1350637210106699777?s=21
そこから色々調べてみたので、自分なりに理解した事を書いてみようと思います。
実装の手順などは、以下の記事から確認してください。実装
① importする
まずは
RealmSwift
をimportします。
RealmSwift
が必要な箇所でそれぞれimportしてみてください。
はじめはエラーになりますので、1度ビルドする事でimport出来ます。ViewController.swiftimport UIKit // RealmSwiftをimportするとエラーになるが、ビルドしたら治る import RealmSwift② モデルクラスの作成
ファイルの追加でswiftファイルを作成してください。
このファイルでは、Realmで扱うデータの項目とその型を定義します。
要は、このプロパティに値を保存していくイメージになります。
Realmモデルのプロパティには@objc
とdynamic
をつける必要があります。TodoModel.swiftimport Foundation import RealmSwift class TodoModel: Object { @objc dynamic var koumoku: String? = nil }③ viewControllerに変数を用意
Results
は「オブジェクトから返される Realm の自動更新コンテナ型」だそうです。
この変数にデータを追加していくことになります。ViewController.swift// 作成したTodoModel型の変数を用意。 // Realmから受け取るデータを入れる変数 var itemList: Results<TodoModel>!④ viewDidLoadでインスタンス化
今回使用するRealmをインスタンス化して使えるようにします。
次に、object()
を使用します。これは()内に指定したデータを全て取得できるものなので、
取得を行い、取得したデータを③で作成した変数に入れます。ViewController.swiftoverride func viewDidLoad() { super.viewDidLoad() // Realmをインスタンス化する let realm = try! Realm() // objects()はRealmに保存されている指定されたデータを全て取得する! // object().filter()でさらにフィルタリングして指定することもできるそうです。 self.itemList = realm.objects(TodoModel.self) }⑤ データを追加して保存していく
今回はbuttonを押したらtextFieldに書いた内容が追加されるようにします。
追加・保存を行いたいAction内で書いてみてください。1.追加していく場所は、②で作成したモデルクラス内のプロパティになりますので、モデルクラスをインスタンス化していきます。
2.textField.text
を②で作成したkoumoku
プロパティに入れます。
3.buttonAction内でもRealmを使うので、viewDidLoad内で作成したように、Realmをインスタンス化します。
4.realm.write
はRealmインスタンスを最新版に更新し、該当する場合は通知を作成すると公式ドキュメントにありました。
2でデータを更新しているので、realm.write
で最新のものを取得しているわけです。
realm.add()
は管理されていないオブジェクトを()内のRealmに追加します。
つまり、realm.add()
で管理されてない(追加したデータ)をRealmに追加して、
realm.write
でその最新バージョンを取得しているわけです。ViewController.swift@IBAction func addButton(_ sender: Any) { // 1.モデルクラスをインスタンス化。これでアクセスできるようになる! let instanceTodoModel: TodoModel = TodoModel() // 2.textFieldの値をぶち込む instanceTodoModel.koumoku = self.textField.text // 3.Realmをインスタンス化してデータベースを取得!viewDidLoadでも行ったやつ let realm = try! Realm() // 4.textFieldの値をデータベースに追加する // write は refresh() が呼び出されたかのように Realm インスタンスを最新の Realm バージョンに更新し、該当する場合は通知を生成します。 try! realm.write { realm.add(instanceTodoModel) } self.tableView.reloadData() }⑥ TableViewの実装
itemList
にRealmから受け取ったデータが入っているので、それを使用してTableViewCellを作成していきます。ViewController.swiftextension ViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return self.itemList.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "Cell1", for: indexPath) let item: TodoModel = self.itemList[indexPath.row] cell.textLabel?.text = item.koumoku return cell }⑦ 削除機能
Viewcontroller.swiftfunc tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { // この中でもRealmを使えるようにインスタンス化 let realm = try! Realm() if editingStyle == .delete { // 削除したいデータを検索する。今回はdeleteしたいcellのindexpath.row番目の項目を削除したいので、それをobjectで指定。 let deleteItem = realm.objects(TodoModel.self)[indexPath.row] do{ // write は refresh() が呼び出されたかのように Realm インスタンスを最新の Realm バージョンに更新し、該当する場合は通知を生成します。 try realm.write{ // 追加の時のaddの削除版 realm.delete(deleteItem) } }catch{ print("Error") } } tableView.reloadData() }実際のコード
ViewController.swiftimport UIKit // RealmSwiftをimportするとエラーになるが、ビルドしたら治る import RealmSwift class ViewController: UIViewController, UITextFieldDelegate { @IBOutlet weak var textField: UITextField! @IBOutlet weak var tableView: UITableView! // 作成したTodoModel型の変数を用意。 // Realmから受け取るデータを入れる変数 var itemList: Results<TodoModel>! override func viewDidLoad() { super.viewDidLoad() // Realmをインスタンス化する let realm = try! Realm() // objects()はRealmに保存されている指定されたデータを全て取得する! self.itemList = realm.objects(TodoModel.self) } @IBAction func addButton(_ sender: Any) { // モデルクラスをインスタンス化。これでアクセスできるようになる! let instanceTodoModel: TodoModel = TodoModel() // textFieldの値をぶち込む instanceTodoModel.koumoku = self.textField.text // Realmをインスタンス化してデータベースを取得!viewDidLoadでも行ったやつ let realm = try! Realm() //Realmインスタンスからaddを叩くと、データベースにレコードが追加される // textFieldの値をデータベースに追加する // write は refresh() が呼び出されたかのように Realm インスタンスを最新の Realm バージョンに更新し、該当する場合は通知を生成します。 try! realm.write { realm.add(instanceTodoModel) } self.tableView.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 cell = tableView.dequeueReusableCell(withIdentifier: "Cell1", for: indexPath) let item: TodoModel = self.itemList[indexPath.row] cell.textLabel?.text = item.koumoku return cell } func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { let realm = try! Realm() if editingStyle == .delete { // 削除したいデータを検索する。今回はdeleteしたいcellのindexpathの項目を削除したいので、それをobjectで指定。 let deleteItem = realm.objects(TodoModel.self)[indexPath.row] do{ // write は refresh() が呼び出されたかのように Realm インスタンスを最新の Realm バージョンに更新し、該当する場合は通知を生成します。 try realm.write{ realm.delete(deleteItem) } }catch{ print("Error") } } tableView.reloadData() } }TodoModel.swiftimport Foundation import RealmSwift class TodoModel: Object { @objc dynamic var koumoku: String? = nil }起動してみた
ツイートしてあるので、リンク貼っておきます。
https://twitter.com/vex3ex5frd2xcvg/status/1350637210106699777?s=21参考サイト
https://qiita.com/pe-ta/items/616e0dbd364179ca284b
https://naoya-ono.com/swift/realm-update-delete/
- 投稿日:2021-01-17T12:52:00+09:00
iosアプリ開発に便利なライブラリ一覧
概要
iosアプリ開発において、簡単に便利なライブラリを紹介します。
ライブラリのインストールはcocoa podsを利用しています。目次
1.Alamofire
2.AlamofireImage
3.APIkit
4.Reachability.swift
5.ObjectMapper
6.Cadable
7.Rswift
8.lottie-ios
9.PKHUD
10.GradientCircularProgress
11.Realm Swift
12.RxSwift
13.promiseKit
14.Bright-future
15.swiftStoreKit
16.Graphs
17.SwiftDateネットワーク系
Alamofire
アプリからネットワーク通信をしたい場合、定番なツール
swift標準機能のURLRequestを使用するより、シンプルで書きやすいAlamofireImage
URLを指定して、ネットワーク上の画像を取得できる
APIkit
シンプルなコードが書ける
ネットワーク接続監視系
Reachability.swift
クロージャやローカル通知で接続状況の変化を受け取ることができる
デコーダ系
ObjectMapper
デコーダとして定番
Cadable
swift標準のデコーダ
リソースの定義系
Rswift
ソースコードの文字列を始めとするリソースの定義を構造体を使って自動的にリストアップしてくれる
アニメーション系
lottie-ios
リッチなアニメーションを作ることができる
インジケーター系
ソースコードの文字列を始めとするリソースの定義をstructを使って自動的にリストアップしてくれる
PKHUD
HUDを表示できる
GradientCircularProgress
データベース系
Realm Swift
データベースのコードをかんたんに書ける
アプリ自身にデータベースを持ち、サーバー側のデータベースと動機できるため、オフライン状態でもアプリを動作できる。
リレーションも使用可非同期処理系
RxSwift
非同期処理のイベントを受け取ることができる
promiseKit
シンプルに非同期処理を実現できる
Bright-future
シンプルに非同期処理が書けるが、日本語記事が少ない
課金系
swiftStoreKit
簡単に課金処理が作れる
グラフ系
Graphs
簡単アプリでグラフを作成できる
日付系
SwiftDate
簡単に日付処理ができる