20200707のSwiftに関する記事は11件です。

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年からの経過秒数を取得
 }

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

実行した時刻を数字として取得することで時系列でソートできるようにする

日時を数字として取得しドキュメントとして保存することで、Firebaseの非同期処理を乗り越えることができました。
他にもやり方はあると思いますが、これによってドキュメントを時系列でソートすることができます。
例)
2020年7月8日8時19分38分に実行
timeNumberDocument()
-->20070881938

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
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

日時を数字として取得する

日時を数字として取得しドキュメントとして保存すれば、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
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

時刻を数字として取得する

日時を数字として取得しドキュメントとして保存すれば、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
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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) {

}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[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をもう一回叩くなど)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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)

以上!!!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

画像取り込み

フォトショップやイラストレータで作成した素材をswiftに取りこむ手順を教えてもらえないでしょうか?

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

不適切な単語を含むメッセージを**に置き換える

はじめに

この記事ではタイトルの通り不適切な単語を扱うため、配慮をしておりますが一部の方にとって不快に感じる恐れがあります。

文字を置き換えたい

匿名でやりとりができる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("ゲーセンで財布落とした")//****で財布落とした
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

メッセージに含まれる特定の単語を*で置き換える

文字を置き換えたい

メッセージに含まれる不適切な単語を*で置き換える処理を考える際に作った関数です。
例)
置き換える単語の配列 = ["ハンバーグ","アメリカ合衆国"]
「今日のご飯はハンバーグだ」-->「今日のご飯は*****だ」
「アメリカ合衆国に留学したい。」--> 「*******に留学したい」

のように、指定した文字列を*に置き換える処理をします。
置き換えたい文字を要素にもつ配列を作れば利用することができます。
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("ゲーセンで財布落とした")//****で財布落とした
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Swift】避けてきたRealm migrationを学ぶ。

こんにちは。いるべさんです。

経緯

アプリを作るとき、とてもお世話になるローカルデータベース。
リリース前の開発段階ではDBの構成を変えて不整合エラー出してもリセットして逃げて乗り越えてきた。

ただ、リリース後にそんなことしてユーザ様に「ローカルデータ捨ててインストールし直してくださいよw」なんて言えないのでコチラが譲歩する。(やれやれ。)

ということで

その為にマイグレーションを行う。
書き方を知ればとても簡単なので、いるべさんはこの記事に残す。

準備!

ネコちゃん

今回はネコちゃんのデータをこねくり回す。
最初は名前と年齢を持っておくだけ。

CatRealm.swift
class Cat: Object {
    @objc dynamic var name = ""
    @objc dynamic var age = 0
}

データ保存

ここは今回の本筋では無いのでさらーっと。

ViewController.swift
let realm = try! Realm()

let mofu = Cat()
mofu.name = "たぬ"
mofu.age = 5

try! realm.write() {
    realm.add(mofu)
}

うちにはタヌキみたいな見た目のモフモフしたネコがいる。
カワイイ。
「だから」たぬちゃんのデータを保存しておく。写真みたいなもんだ。

リネームしてみる。

ネコも家族。

まず変数名を変更してみる。
nameプロパティをfullNameに変更する。

CatRealm.swift
class Cat: Object {
    @objc dynamic var fullName = ""
    @objc dynamic var age = 0
}

この時点・この状態で実行してみる。

もちろん不整合エラー

↓↓↓↓↓解決策

マイグレーション

マイグレーションを行う。

コードスラスラ読めないマンからすると「うっ!」となるコードですが、、
ほとんどこのまま流用できるので最初は脳死で使いましょう!!!

Realm.swift
func 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.swift
let 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.swift
class Cat: Object {
    @objc dynamic var fullName = ""
    @objc dynamic var kind = "ラグドール"
}

初期値にうちのたぬの種類:「ラグドール」を入れておく。
!実際にはこんなに具体的な初期値を入れるのはお勧めしません〜。

マイグレーションする!

Realm.swift
Realm.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.swift
class Cat: Object {
    @objc dynamic var nameAndKind = ""
}

実際にこんなプロパティは作らないでくださいというアンチパターン!!!
流石に誰もこんなことしないか。笑

Realm.swift
Realm.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 = "いるべたぬ:ラグドール";
    }
)

一個前の削除も同時に行われて救いようがない、もしくは救うのがめんどくさいオブジェクトができました!

いるべさんと学ぶカンタンマイグレーションでした。

ここまでお読み頂きありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む