- 投稿日:2021-01-14T23:58:07+09:00
iOSのlaunchImageNameで大ハマりしたメモ
iOSでローカル通知に画像を表示したい時に、launchImageNameを使用してくださいという記事がいくつもある。
しかし、実際は動かないし、使い方を書いてある記事は一つも無かった。
僕と同じようにハマってほしくないので、launchImageNameは使えないよ。とメモをここに残します。
- 投稿日:2021-01-14T20:48:51+09:00
【Swift】動的な画面でのパフォーマンスを考慮したUIImageViewの使用
はじめに
動的な画面ではUIImageViewを高速に使用する場合、CALayerの使用方法によっては処理が重くなってしまう場合があります。
コメント欄などでのパフォーマンスを考慮した角丸やBorderを使用したUIImageViewの使用について書きます。一般的な実装方法
一般的にフチのある丸画像を使用する際は
ViewController.swiftimageView.layer.cornerRadius = imageView.frame.height / 2 imageView.layer.borderWidth = 4 imageView.layer.borderColor = UIColor.orange.cgColorこのようにCALayerにたいして指定し、実装するかと思います。
問題点
しかしCALayerに指定して実装した場合は、コンテンツをスクロールしたとき、毎フレームで画像処理のためのオフスクリーンレンダリングが走ります。
画像の変更がない場合や関係ない画像に関係ないViewの動きに対してもオフスクリーンレンダリング発生してしまう場合があります。
そのためCPUへの負荷が非常に高くなってしまいます。
また、画像が一瞬四角に表示されるなど、意図せぬ挙動をしてしまう場合があります。
よってLayerのcornerRadiusは、高速な処理をする画面での使用には向きません。そのため
- 角丸などの加工済みの画像を用意する
- ライブラリなどで使用前に画像を加工する
- 角丸のViewを上から重ねる などで対策する必要があります。
実装方法については// TODOということでまた書きます()
- 投稿日:2021-01-14T18:56:24+09:00
【Swift】アクセス修飾子を理解して使い分ける
はじめに
Swiftでコーディングする際、アクセス修飾子を適当に使っちゃってないですか?
雰囲気で使いわけてレビューで突っ込まれてないですか?なぜ定数(変数)を使うのか、なぜその修飾子を使うのか理解して使うために自分なりにまとめたので、よかったら参考にしてください!
変数と定数
let(定数)
値を変更する可能性がない場合に使う
static let
全インスタンスから共通で利用できる
var(変数)
後から値を変更することができるlet hoge = hogehoge static let hoge = hogehoge var hoge = hogehogeアクセス修飾子
Set
入れた値を使って他のプロパティの値に渡すことができる
Get
他のプロパティの値を受け取るのことができる追記: getterは同一モジュール内、setterは同一スコープ内からアクセスできる
(@takehito-koshimizu さんありがとうございます?)private(set) var hoge = hogehogePublic
別モジュールから呼び出せるが継承やオーバーライドが不可能
Private
同スコープ内からのみ呼び出せる
fileprivate
同じfile内であれば呼び出せるprivate let hoge = hogehoge private var hoge = hogehogepublic let hoge = hogehoge public var hoge = hogehogefileprivate let hoge = hogehoge fileprivate var hoge = hogehoge参考になりましたでしょうか?よければLGTMくれると僕がめっちゃ喜びます!
- 投稿日:2021-01-14T18:49:47+09:00
Swift UITextViewのplaceholder
UITextViewにplaceholderがない!?
twitter風アプリを作ろうと思った時にtextfieldと同じように
textView.placeholder = ""
って感じでコードを書くと
Value of type 'UITextView' has no member 'placeholder'
と怒られてしまいました。
え、textView
ってplaceholder
ないの...
ということで、なんとかしてtextView
にplaceholder
を実装していきましょう。今回は
UITextView
を継承したTextView
というclass
を作成してそのTextView
の中にUILabel
をaddSubView
していきます。TextView.swiftclass TextView: UITextView { public let placeholderLabel: UILabel = { let label = UILabel() label.textColor = .lightGray label.font = .systemFont(ofSize: 18) label.numberOfLines = 0 return label }() override init(frame: CGRect, textContainer: NSTextContainer?) { super.init(frame: frame, textContainer: textContainer) addSubview(placeholderLabel) placeholderLabel.translatesAutoresizingMaskIntoConstraints = false let cons = [ placeholderLabel.topAnchor.constraint(equalTo: topAnchor, constant: 8), placeholderLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 5), placeholderLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -5), ] NSLayoutConstraint.activate(cons) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } }こんな感じでTextViewを定義しました。
あとは
ViewController
でtextView
を表示するだけですね!ViewController.swiftclass ViewController: UIViewController { let textView: TextView = { let textView = TextView() textView.placeholderLabel.text = "placeholderだよー" textView.font = .systemFont(ofSize: 18) return textView }() override func viewDidLoad() { super.viewDidLoad() view.addSubview(textView) textView.frame = .init(x: 0, y: 100, width: view.frame.size.width, height: view.frame.size.height-200) } }これでいい感じに表示されました!
でも、このままだと入力した文字とplaceholderLabel
の文字が重なってしまいますね。
んー、どうしよっかと考えた結果UITextViewDelegate
のtextViewDidChange(_ textView: UITextView)
を使うことにしました。ViewController.swiftextension ViewController: UITextViewDelegate { func textViewDidChange(_ textView: UITextView) { if textView.text.isEmpty { self.textView.placeholderLabel.alpha = 1 } else { self.textView.placeholderLabel.alpha = 0 } } }こんな感じです。
textView.text
が空だったらplaceholderLabel
を表示し、そうでなかったらplaceholderLabel
を非表示にする。まとめ
textViewにplacehoderを実装するだけで結構コード書かないといけないですね笑
もっと簡単に実装できる方法があればどなたか教えてください!
- 投稿日:2021-01-14T17:08:19+09:00
swiftのErrorとNSErrorの違いがよく分からないので自分なりにまとめてみた
はじめに
いつも、swiftで開発しているのですが、Error・NSErrorの2つのエラーオブジェクトが出てきて
いつも何で分けられてるの?と疑問に思いながらErrorからNSErrorに型キャストしたりしてました。よく分からないままだと流石にまずいなと思い、Qiitaに備忘録として残そうと思った感じです。
ErrorとNSErrorの違いとは
そもそも、この2つの違いは何でしょうか?
調べてみると
・NSErrorは
Objective-C
のエラーオブジェクトでクラス
として定義されている。・Errorは
swift
のエラーオブジェクトでプロトコル
として定義されている。という感じでした。
更に、深く調べてみましょう。
NSError
NSErrorは
Objective-C
で使われていたエラーオブジェクトでエラー状態に関する情報
が含まれています。どんな情報が含まれているのかというと
domain
・code
・userInfo
という3つの情報が含まれています。分かりやすようにまとめてみました↓
情報 意味 domain エラーの種類を識別するための文字列 code エラーの種類を識別するための整数値 userInfo エラーに関する付加情報 swiftのErrorはNSErrorに常に
キャスト
できるようになっており、それらの情報を参照
することができます。実際にコードを書いてみるとこんな感じです↓
func manager(error: Error) { // ErrorからNSErrorにキャスト let nsError = error as NSError // 欲しい情報を参照する print(nsError.domain) print(nsError.code) print(nsError.userInfo) }例えば何かのマネージャークラスのデリゲートメソッドがあって
エラーが発生したら呼び出されるメソッドがあるとします。そしたら、そのエラーを
NSError
にキャストして欲しい情報を参照
していくという流れですね。NSErrorの使いどころとは
swiftでは
Objective-C
との互換性を保つために存在していますが、swiftのエラーハンドリングが強力なのもあって、NSErrorで使っていたdomainやcodeを使う機会は減ってしまったそうです。ただ、
2つの言語が混在しているプロジェクト
であればNSErrorを意識する場面に直面するそうですね。私は、全くObjective-Cを触ったことがないので分かりませんが自分の開発しているアプリでは
domainやcodeの情報が欲しい時があるので割とキャストして使っています。Error
最初の方で述べたように
swift
で使われているエラーオブジェクトでプロトコル
として定義されています。そんなswiftのErrorは
準拠した型がエラーを表現する型として扱えることを示すためのプロトコル
であって
準拠するために必要な実装はないです。つまり、swiftのErrorは
エラー情報を表現するプロトコル(規約)
なんですね。因みに余談ですが、swift2ではErrorは
ErrorType
という名前だったそうで
swift3からError
という名前に変わったそうです。Errorの使いどころとは
このErrorプロトコルに準拠する型は、
列挙型(enum)
に準拠するのが一般的で
発生するエラーをまとめて記述できる
というメリットがあるからです。更に、プログラム全体で起こり得るあらゆるエラーを一つの列挙型で定義せずに
エラーの種類によって別の型を定義する
のが常だそうですね。では実際に、Errorプロトコルを準拠して列挙型で定義してみましょう。
今回は、API通信を行う時に発生するであろうエラーを列挙型で定義してみます。
enum ApiError: Error { case networkError case decodeFailed case responceFailed case unknown }このように定義することができます。
後は
引数
を持たせて、このような使い方が出来ます。
以下の例では、computedプロパティを使って、ケースごとにメッセージを返すようにしています↓enum ApiError: Error { case networkError case decodeFailed(Error) case responceFailed(Error?) case unknown var message: String { switch self { case .networkError: return "通信エラーが発生しました" case .decodeFailed(let error): return "デコードに失敗しました \(error.localizedDescription)" case .responceFailed(let error): return "レスポンスの取得に失敗しました \(error?.localizedDescription ?? "error")" case .unknown: return "不明なエラーが発生しました" } } } let networkErrorMessage = ApiError.networkError.message let decodeErrorMessage = ApiError.decodeFailed(error).messageErrorからエラーメッセージを取得したい場合は、localizedDescriptionプロパティを使う方法があります。
このプロパティは元々、NSErrorが定義しているプロパティなので、swiftのErrorが持っているプロパティではないのですがFoundationフレームワークをリンクしていると使えるようになります。
なぜ使えるのかというと、そもそも
Foundation
フレームワークはObjective-Cで作られており、Objective-CではFoundationフレームワークに含まれるNSErrorクラスを使ってエラーハンドリングしていたからです。なので、swiftでも
FoundationをインポートすればNSErrorクラスが使える
ということです。他にも
Int型
やString型
なども引数に持たせることが可能で
エラーに付随する情報
を表現することが出来ます。enum DatabaseError { case networkError case invalidEntry(reson: String) } let invalidEntryReson = DatabaseError.invalidEntry(reson: "~が理由で無効です")おわり
ErrorとNSErrorの違いをまとめてみました。
swiftしか触ったことがないので、かなりswift寄りの記事になってしまいましたね。もし、間違っている部分があればコメントして下さると有り難いです。
最後に下記にて参考資料を載せておきます。
参考資料
Swift3時代のErrorとNSErrorに関するいくつかの実験
Swift 4.0 エラー処理入門
[Swift] Swiftのエラー処理についてざっくりとまとめてみた
- 投稿日:2021-01-14T17:07:21+09:00
iOSのバックアップで、FirebaseのAuthは機種変更では引き継がれない
はじめに
タイトル通りですが、Firebaseの認証情報
Auth.auth().currentUser
は、あるiOS端末のバックアップを取って、他のiOS端末で復元したとしても認証情報を引き継ぐことはできません。iOS端末Aのバックアップを取って、端末Bで復元する
この場合、復元された端末Bでアプリを動作させてもcurrentUserは
nil
です。iOS端末Aのバックアップを取って、再度端末Aで復元する
この場合、端末AのcurrentUserのuidはバックアップを取った時と同一です。つまり同一端末での復元ではFirebaseの認証情報は復元されます。
まとめ
このようにバックアップの復元先が元の端末かそうでないかでFirebaseの認証情報が復元されるかどうかが変わってきます。
特にUserDefaultsやキーチェーンなど、復元される項目と組み合わせて情報を組み立てている場合、バグの温床となります。
お気をつけください。
- 投稿日:2021-01-14T14:22:24+09:00
Swift よくつまずくdelegateについて
delegateとは
Swiftを勉強しているとdelegateというのがよく出てきます。
例えば以下の例ViewController.swifttableView.delegate = self tableview.dataSource = selfdelegateを和訳すると委譲や委任らしいですが日本語の意味は理解できても、「ほぉ、それで?」と思いますよね。
てことで、どういう使われ方をしているか見ていきましょう。delegateの使い方
そのクラスができない処理を他のクラスに代わりにしてもらうというものなんですが、すこしコードを見てみましょう
今回はUITextViewについてです。ViewController.swifttextView.delegate = selfViewController.swiftextension ViewController: UITextViewDelegate { func textViewDidChange(_ textView: UITextView) { // textViewのtextに変更が会った時の処理 if !textView.text.isEmpty { print(textView.text) } } }こんな感じでtextViewだけじゃできない処理をViewControllerに代わりにやってもらうことができます。
他にもpresentで画面を表示したりするのはViewControllerじゃないとできないので、タップして画面表示させたいときとかに使います。delegateを実装
delegateはprotocolで宣言します。
: AnyObject
の部分はdelegateをclassでしか使えなくするためらしい??
weak
を使えるようにするためらしいです。
詳しくは以下を参照してください。protocol CustomViewDelegate: AnyObject { public func customView() }CustomView.swiftclass CustomView { weak var delegate: CustomViewDelegate? @IBOutlet var button: UIButton! @IBAction func button() { delegate.customView() } }delegate変数の宣言の時のweakはメモリリークを避けるためのなんたら〜らしいです。
追記:
weakについてコメントしてくださっているのでそちらも見てください。まとめ
なんやかんや書いてきましたが、Twitterとかのプロフィール画像をタップした時に相手のプロフィール画面に飛ぶ時とかにつかえるってことですね!