- 投稿日:2020-07-07T21:24:19+09:00
iPhoneアプリ学習:ストップウォッチ
はじめに
某プログラミング学習サイトでの学習記録を記します。
No1:割愛
No2:部品配置
・複数の部品にまとめて制約を付ける場合は、
・対象となる部品を全て選ぶ
・Embed InからStack Viewに登録する
・View Controller Scene > View Controller > View > Stack Viewを選択する
・位置の制約を付けるNo3:部品をコードに接続する
・時間表示用のラベル
・ボタン3つ(Start,Stop, Reset)No4:ボタン押下でタイマーを開始する
@IBAction func (ボタン名) (_ sendor:Any) {
Timer.scheduledTimer(
timeInterval: 0.01, // 実行間隔[秒]
target: self, // タイマーで実行するメソッドのある場所
selector: #selector(self.update), // 実行するメソッド名
userinfo:nil, // selectorに渡す情報。なければnil
repeats:true) // 繰り返し実行するかどうか// selectorはobjective-cの仕様のため先頭に@objcが必要らしい
@objc func update() {
print(Date.timeIntervalSinceReferenceDate);
// 2001年からの経過秒数を取得
}
- 投稿日:2020-07-07T20:09:24+09:00
実行した時刻を数字として取得することで時系列でソートできるようにする
日時を数字として取得しドキュメントとして保存することで、Firebaseの非同期処理を乗り越えることができました。
他にもやり方はあると思いますが、これによってドキュメントを時系列でソートすることができます。
例)
2020年7月8日8時19分38分に実行
timeNumberDocument()
-->20070881938import UIKit func timeNumberDocument() -> Int { let dt = Date() var firstCha = "" let dateFormatter = DateFormatter() dateFormatter.dateFormat = DateFormatter.dateFormat(fromTemplate: "yyMMddHHmmss", options: 0, locale: Locale(identifier: "ja_JP")) var time = dateFormatter.string(from: dt) var timeNumber = time.compactMap { $0.hexDigitValue }.map({String($0)}) for cha in timeNumber { var cha = cha firstCha += cha } return Int(firstCha) ?? 0 }
- 投稿日:2020-07-07T20:09:24+09:00
日時を数字として取得する
日時を数字として取得しドキュメントとして保存すれば、Firebaseの非同期処理を楽に乗り越えられるのでは?と思い勢いで作りました。
import UIKit func timeNumberDocument() -> Int { let dt = Date() var firstCha = "" let dateFormatter = DateFormatter() dateFormatter.dateFormat = DateFormatter.dateFormat(fromTemplate: "yyMMddHHmmss", options: 0, locale: Locale(identifier: "ja_JP")) var time = dateFormatter.string(from: dt) var timeNumber = time.compactMap { $0.hexDigitValue }.map({String($0)}) for cha in timeNumber { var cha = cha firstCha += cha } return Int(firstCha) ?? 0 }
- 投稿日:2020-07-07T20:09:24+09:00
時刻を数字として取得する
日時を数字として取得しドキュメントとして保存すれば、Firebaseの非同期処理を楽に乗り越えられるのでは?と思い勢いで作りました。
import UIKit func timeNumberDocument() -> Int { let dt = Date() var firstCha = "" let dateFormatter = DateFormatter() dateFormatter.dateFormat = DateFormatter.dateFormat(fromTemplate: "yyMMddHHmmss", options: 0, locale: Locale(identifier: "ja_JP")) var time = dateFormatter.string(from: dt) var timeNumber = time.compactMap { $0.hexDigitValue }.map({String($0)}) for cha in timeNumber { var cha = cha firstCha += cha } return Int(firstCha) ?? 0 }
- 投稿日:2020-07-07T16:58:11+09:00
iOS 13 起動時/終了時メモ
従来の処理ができない。
iOS13から次のメソッドが効かなくなったようです。 Xcodeから新規にプロジェクトを作成した時は注意。
・ applicationDidBecomeActive
・ applicationWillResignActive
・ applicationDidEnterBackground
・ applicationWillEnterForeground代わりの処理を用意する
NotificationCenterを利用する。起動時/終了時のイベント名がUIScene系列で定義されているのでそれを利用する。
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. NotificationCenter.default.addObserver(self, selector: #selector(appWillResignActive(_:)), name: UIScene.willDeactivateNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(appDidBecomeActive(_:)), name: UIScene.didActivateNotification, object: nil) return true }呼び出すメソッドを定義。
@objc func appWillResignActive(_ application: UIApplication) { } @objc func appDidBecomeActive(_ application: UIApplication) { }
- 投稿日:2020-07-07T16:51:31+09:00
[iOS]project.pbxprojなどのマージコンフリクト解消を最速でやるには
おすすめツール
- Visual Studio Code
project.pbxprojファイルに限らないが、ファイル内にコンフリクトマーカーがある場合、そこを色付けで表示し、一発で飛べる。その上、「Accept Incoming Change」「Accept Current Change」「Accept Both Changes」の各ボタンが用意されており、ワンタッチで「マージされてきたブランチ」「現在のブランチ」「両方のブランチ」の変更を適用する事ができる。その他、軽く使いやすいためおすすめ。
解消の考え方
project.pbxproj
ファイルは、プロジェクト内にあるファイルの定義およびそれらのフォルダ階層構造などを定義している。そのため、マージ(またはチェリーピック)するブランチ同士でファイルの位置が違ったり、新規ファイルを追加したりしていると必ずマージコンフリクトを起こしてしまう。まず、現在のブランチと、マージによって持ってきたブランチで、ファイル・フォルダの階層構造がどう違っているかを確認する。
その上で、マージ後はどういった階層構造にしたいのかを決める。
その後は、その通りになるように
pbxproj
の記述を編集していけば良い。どういった状態にしたいかによって、
「現在のブランチの変更を受け入れる」
「持ってきたブランチの変更を受け入れる」
「両方の変更を受け入れる」
で済むこともあるし、変更を手動で調整する必要がある場合もある。トラブルシューティング
コンフリクトを全て解消したはずだが、エラーが出る
エラーメッセージを読むと、pbxprojファイルの何に問題があるのか基本的には書いてある。
よくあるのは、同じ名前のファイルが二箇所で重複定義されている、逆に消してしまったはずの存在していないファイルを参照しているなど。マージコンフリクトがあった箇所とは違う箇所にこうしたエラーが生ずるのもよくある。
また、文法が間違っていると言われることがあるが、これはpbxprojファイルを編集中に誤って不要な文字を入れてしまったり、必要な文字まで消してしまったりして、記述の形式がおかしくなっていることが多い。問題のある箇所が何行目かもエラー文で教えてもらえるはずなので、そこを直す。
.xcworkspace
ファイルが開けなくなる
- Xcodeのクリーン、Derived Dataの削除、再起動などを試す。
.xcworkspace
を一回消してから作り直す(pod install
をもう一回叩くなど)
- 投稿日:2020-07-07T16:38:27+09:00
UITabBarItemにシステムアイコンをセットする
はじめに
掲題の通り、UITabBarItemにシステムアイコンをセットする方法がなかなか見つからなかったため、備忘録として残す
設定方法
以下のように引数Imageに UIImage(systemName: "アイコン名")を設定するだけ
アイコン名は以下から取得可能
https://developer.apple.com/design/human-interface-guidelines/ios/icons-and-images/system-icons/v.tabBarItem = UITabBarItem(title: R.string.localizable.bookList(), image: UIImage(systemName: "book"), tag: 0)以上!!!
- 投稿日:2020-07-07T14:42:11+09:00
画像取り込み
フォトショップやイラストレータで作成した素材をswiftに取りこむ手順を教えてもらえないでしょうか?
- 投稿日:2020-07-07T10:17:20+09:00
不適切な単語を含むメッセージを**に置き換える
はじめに
この記事ではタイトルの通り不適切な単語を扱うため、配慮をしておりますが一部の方にとって不快に感じる恐れがあります。
文字を置き換えたい
匿名でやりとりができるapp storeが審査を通り抜けるためにはフィルタリング機能をつける機能が必要です。
置き換えたい文字を要素にもつ配列を作れば利用することができます。
Swiftには指定した文字を置き換えるreplacingOccurrences
,指定した文字列を繰り返すString(repeating:, count:)
があるのでそれらを組み合わせていきます。ソースコード
import UIKit let prohibittenWordArray = ["落とし前","ゲーセン"] //不適切な文字を置き換える関数 func checkMessage(_ message: String) -> Void { var message = message for prohibittenWord in prohibittenWordArray { if message.contains(prohibittenWord) { message = message.replacingOccurrences(of: prohibittenWord, with: convertWord(prohibittenWord.count), options: .literal, range: nil) } } print(message) } //禁止用語を文字数分の*に置き換える func convertWord(_ wordCount: Int) -> String { return String(repeating: "*", count: wordCount) } checkMessage("こんにちは")//こんにちは checkMessage("ゲームセンターで落とし前をつける")//ゲームセンターで****をつける checkMessage("ゲーセンで財布落とした")//****で財布落とした
- 投稿日:2020-07-07T10:17:20+09:00
メッセージに含まれる特定の単語を*で置き換える
文字を置き換えたい
メッセージに含まれる不適切な単語を*で置き換える処理を考える際に作った関数です。
例)
置き換える単語の配列 = ["ハンバーグ","アメリカ合衆国"]
「今日のご飯はハンバーグだ」-->「今日のご飯は*****だ」
「アメリカ合衆国に留学したい。」--> 「*******に留学したい」のように、指定した文字列を*に置き換える処理をします。
置き換えたい文字を要素にもつ配列を作れば利用することができます。
Swiftには指定した文字を置き換えるreplacingOccurrences
,指定した文字列を繰り返すString(repeating:, count:)
があるのでそれらを組み合わせていきます。ソースコード
import UIKit let prohibittenWordArray = ["落とし前","ゲーセン"] //不適切な文字を置き換える関数 func checkMessage(_ message: String) -> Void { var message = message for prohibittenWord in prohibittenWordArray { if message.contains(prohibittenWord) { message = message.replacingOccurrences(of: prohibittenWord, with: convertWord(prohibittenWord.count), options: .literal, range: nil) } } print(message) } //禁止用語を文字数分の*に置き換える func convertWord(_ wordCount: Int) -> String { return String(repeating: "*", count: wordCount) } checkMessage("こんにちは")//こんにちは checkMessage("ゲームセンターで落とし前をつける")//ゲームセンターで****をつける checkMessage("ゲーセンで財布落とした")//****で財布落とした
- 投稿日:2020-07-07T00:11:11+09:00
【Swift】避けてきたRealm migrationを学ぶ。
こんにちは。いるべさんです。
経緯
アプリを作るとき、とてもお世話になるローカルデータベース。
リリース前の開発段階ではDBの構成を変えて不整合エラー出してもリセットして逃げて乗り越えてきた。ただ、リリース後にそんなことしてユーザ様に「ローカルデータ捨ててインストールし直してくださいよw」なんて言えないのでコチラが譲歩する。(やれやれ。)
ということで
その為にマイグレーションを行う。
書き方を知ればとても簡単なので、いるべさんはこの記事に残す。準備!
ネコちゃん
今回はネコちゃんのデータをこねくり回す。
最初は名前と年齢を持っておくだけ。CatRealm.swiftclass Cat: Object { @objc dynamic var name = "" @objc dynamic var age = 0 }データ保存
ここは今回の本筋では無いのでさらーっと。
ViewController.swiftlet realm = try! Realm() let mofu = Cat() mofu.name = "たぬ" mofu.age = 5 try! realm.write() { realm.add(mofu) }うちにはタヌキみたいな見た目のモフモフしたネコがいる。
カワイイ。
「だから」たぬちゃんのデータを保存しておく。写真みたいなもんだ。リネームしてみる。
ネコも家族。
まず変数名を変更してみる。
nameプロパティをfullNameに変更する。CatRealm.swiftclass Cat: Object { @objc dynamic var fullName = "" @objc dynamic var age = 0 }この時点・この状態で実行してみる。
もちろん不整合エラー
↓↓↓↓↓解決策
マイグレーション
マイグレーションを行う。
コードスラスラ読めないマンからすると「うっ!」となるコードですが、、
ほとんどこのまま流用できるので最初は脳死で使いましょう!!!Realm.swiftfunc migration() { Realm.Configuration.defaultConfiguration = Realm.Configuration( schemaVersion: 1, // ① migrationBlock: { migration, oldSchemaVersion in if(oldSchemaVersion < 1) { migration.renameProperty(onType: Cat.className(), from: "name", to: "fullName") //② } } }) }大事なのは①schemaVersionと②migration.renamePropertyの行だけ。
- ①のschemaVersionは何も変更していなければ最初は0なので、初回変更時は1に設定する。
変更するたびに1ずつインクリメントすることでマイグレーションを行える。
schemaVersionはUInt型なので1.4みたいなバージョンは指定できない。
例えばschemaVersionに一度例えば5を指定してマイグレーション後、3など5より下に指定するとエラー
今回はnameをfullNameに変更するので
renamePropertyのfromに変更前のプロパティ名。
toに変更後のプロパティ名を入れれば変更できる。ViewController.swiftlet realm = try! Realm() var mofu = realm.objects(Cat.self).first try! realm.write { mofu.fullName = "いるべ" + mofu.fullName }確かめる。
上のmigration関数をCatを読み出す前に実行させる。
→ しないと不整合エラーCatの情報を見てみると、
Results<Cat> <0x7f9ca7809210> ( [0] Cat { fullName = "いるべたぬ"; age = 5; } )たぬもこれで家族の一員だとハッキリする。
プロパティを追加&削除する。
種類も覚えておきたい。
ラグドールって聞いたことありますか?
あんまり聞かないよなぁと思うので種類のデータも保存しましょう!あと、たぬは女の子なので年齢は隠します。(今更)
CatRealm.swiftclass Cat: Object { @objc dynamic var fullName = "" @objc dynamic var kind = "ラグドール" }初期値にうちのたぬの種類:「ラグドール」を入れておく。
!実際にはこんなに具体的な初期値を入れるのはお勧めしません〜。マイグレーションする!
Realm.swiftRealm.Configuration.defaultConfiguration = Realm.Configuration( schemaVersion: 2, migrationBlock: { migration, oldSchemaVersion in if(oldSchemaVersion < 2) { migration.enumerateObjects(ofType: Cat.className()) { _, _ in // pass: 何もいらない } } } })2回目の変更なので前回のschemaVersionに1プラスすることが大事。
確かめる。
Results<Cat> <0x7f9ca7809210> ( [0] Cat { fullName = "いるべたぬ"; kind = "ラグドール"; } )プロパティの追加、削除は元のオブジェクトクラスにプロパティを追加してマイグレーションを行うだけ。思ってたよりずっと簡単なんだなぁと思った。
前のデータを利用する。
kindが何となく邪魔になったので名前と合体させることにした。
もはやうちのたぬが可愛いだけの実用性のないオブジェクトになっていくのは目を瞑りましょう。タス、ケテ、、
CatRealm.swiftclass Cat: Object { @objc dynamic var nameAndKind = "" }実際にこんなプロパティは作らないでくださいというアンチパターン!!!
流石に誰もこんなことしないか。笑Realm.swiftRealm.Configuration.defaultConfiguration = Realm.Configuration( schemaVersion: 3, migrationBlock: { migration, oldSchemaVersion in if(oldSchemaVersion < 3) { migration.enumerateObjects(ofType: Cat.className()) { old, new in new!["nameAndKind"] = old!["fullname"] + ":" + old!["kind"] } } } })migration.enumerateObjectsの、oldとnewについて
- oldは変更前のname, ageを持ったCatオブジェクト。
- newは変更後のfullName, ageを持ったCatオブジェクト。
これだけで新しいプロパティを追加できる。
確かめる。
Results<Cat> <0x7f9ca7809210> ( [0] Cat { nameAndKind = "いるべたぬ:ラグドール"; } )一個前の削除も同時に行われて救いようがない、もしくは救うのがめんどくさいオブジェクトができました!
いるべさんと学ぶカンタンマイグレーションでした。
ここまでお読み頂きありがとうございました。