- 投稿日:2021-03-29T18:21:29+09:00
【Swift】SlideshowAppで学んだこと
はじめに
SlideshowAppを作成しましたので、そこで学んだことの備忘録です。
GitHub
学んだこと
グラデーションの扱いです。
let width = self.view.frame.size.width let height = self.view.frame.size.height let gradientLayer = CAGradientLayer() gradientLayer.frame = CGRect(x: 0, y: 0, width: width, height: height) gradientLayer.colors = [UIColor(red: 0.8, green: 0.0, blue: 0.73, alpha: 1).cgColor, UIColor(red: 0.1, green: 0.0, blue: 0.40, alpha: 1).cgColor] gradientLayer.startPoint = CGPoint.init(x: 0, y: 0.5) gradientLayer.endPoint = CGPoint.init(x: 1, y: 0.5) self.view.layer.insertSublayer(gradientLayer, at: 0)このようにすることで、背景色がグラデーションでいい感じになってくれます。
公式ドキュメント: CAGradientLayer
スニペットなどに登録しておくことをお勧めします!おわりに
おわりです。
- 投稿日:2021-03-29T17:57:28+09:00
【Swift】Extensionまとめ(初級)
はじめに
Extensionの基礎的な扱い方をまとめました。
Extensionとは?
Extensionキーワードを使うことで既存の型に方を構成する要素(プロパティやメソッドやイニシャライザ)を追加する事ができます。
具体例を見ていきましょう。メソッドでの利用
以下のようにすることで、
plusOne
メソッドをIntに追加できます。
self
はこのメソッドの呼び出し元(今回は5)を表しています。(詳細は後述します)extension Int { func plusOne() -> Int { return self + 1 } } print(5.plusOne()) // 6プロパティでの利用
extensionはストアドプロパティは追加できませんが、コンピューテッドプロパティを追加する事ができます。
extension Int { var minus10: Int { return self - 10 } } print(100.minus10) // 90イニシャライザでの利用
イニシャライザを追加してみます。
enum BloodType { case A case B case O case AB var title: String { return "あなたの血液型は" } var message: String { switch self { case .A: return "Aです" case .B: return "Bです" case .O: return "Oです" case .AB: return "ABです" } } } extension UIAlertController { convenience init(blood: BloodType) { self.init(title: blood.title, message: blood.message, preferredStyle: .alert) } } let blood = BloodType.AB let alert = UIAlertController(blood: blood)プロトコルでの利用
プロトコルをエクステンションすることもできます。
protocol Book { var name: String { get } var price: Int { get } } extension Book { func printName() { print(name) } func printPrice() { print(price) } func printSelf() { print(self) } func printMyType() { print(type(of: self)) } } struct MyBook: Book { var name: String var price: Int } let myBook = MyBook(name: "マイブック", price: 1000) myBook.printPrice() // 1000 myBook.printSelf() // MyBook(name: "マイブック", price: 1000) myBook.printMyType() // MyBookこれで
Book
プロトコルを採用した型はextension Book
内のメソッドを扱う事ができます。制約の追加
プロトコルエクステンションは型による制約をつけることができ、この条件を満たすもののみプロトコルエクステンションを有効にできます。
extension Collection where Element == Int { var total: Int { return self.reduce(0) { $0 + $1 } } } let intArray = [1, 2, 3, 4, 5] print(intArray.total) // 15 let stringArray = ["a", "b", "c"] print(stringArray.total) // error条件は
Collection
の要素Element
がInt
であると言う意味です。ですので、intArray
の要素はInt
なのでtotal
プロパティを扱う事ができますが、stringArray
は要素がString
なのでtotal
プロパティにアクセスしようとするとエラーになります。おわりに
エクステンションの基礎的な使い方を紹介しました。以上です。
- 投稿日:2021-03-29T17:57:28+09:00
【Swift】Extensionまとめ(基礎)
はじめに
Extensionの基礎的な扱い方をまとめました。
Extensionとは?
Extensionキーワードを使うことで既存の型に方を構成する要素(プロパティやメソッドやイニシャライザ)を追加する事ができます。
具体例を見ていきましょう。メソッドでの利用
以下のようにすることで、
plusOne
メソッドをIntに追加できます。
self
はこのメソッドの呼び出し元(今回は5)を表しています。(詳細は後述します)extension Int { func plusOne() -> Int { return self + 1 } } print(5.plusOne()) // 6プロパティでの利用
extensionはストアドプロパティは追加できませんが、コンピューテッドプロパティを追加する事ができます。
extension Int { var minus10: Int { return self - 10 } } print(100.minus10) // 90イニシャライザでの利用
イニシャライザを追加してみます。
enum BloodType { case A case B case O case AB var title: String { return "あなたの血液型は" } var message: String { switch self { case .A: return "Aです" case .B: return "Bです" case .O: return "Oです" case .AB: return "ABです" } } } extension UIAlertController { convenience init(blood: BloodType) { self.init(title: blood.title, message: blood.message, preferredStyle: .alert) } } let blood = BloodType.AB let alert = UIAlertController(blood: blood)プロトコルでの利用
プロトコルをエクステンションすることもできます。
protocol Book { var name: String { get } var price: Int { get } } extension Book { func printName() { print(name) } func printPrice() { print(price) } func printSelf() { print(self) } func printMyType() { print(type(of: self)) } } struct MyBook: Book { var name: String var price: Int } let myBook = MyBook(name: "マイブック", price: 1000) myBook.printPrice() // 1000 myBook.printSelf() // MyBook(name: "マイブック", price: 1000) myBook.printMyType() // MyBookこれで
Book
プロトコルを採用した型はextension Book
内のメソッドを扱う事ができます。制約の追加
プロトコルエクステンションは型による制約をつけることができ、この条件を満たすもののみプロトコルエクステンションを有効にできます。
extension Collection where Element == Int { var total: Int { return self.reduce(0) { $0 + $1 } } } let intArray = [1, 2, 3, 4, 5] print(intArray.total) // 15 let stringArray = ["a", "b", "c"] print(stringArray.total) // error条件は
Collection
の要素Element
がInt
であると言う意味です。ですので、intArray
の要素はInt
なのでtotal
プロパティを扱う事ができますが、stringArray
は要素がString
なのでtotal
プロパティにアクセスしようとするとエラーになります。おわりに
エクステンションの基礎的な使い方を紹介しました。以上です。
- 投稿日:2021-03-29T17:43:15+09:00
【iOS】Gmail APIでメール情報を取得
目的と手順の説明
今回は、私の最新のGmailを誰から受信したのか?を取得することを目的にするよ〜〜〜〜〜。
1.Gmail APIを有効にする
2.認証画面を作成
3.OAuth2.0を利用して、クライアントアプリにトークンを受けとる
4.Gmail APIを叩く
下の記事を読んでから、この記事を読み進めることをおすすめします。
一番分かりやすい OAuth の説明1.Gmail APIを有効にする
(もしかしたら、1より先に2をしなあかんかったかもしれん。。。)
Gmail APIを有効
アプリを公開しないなら、全てを記述する必要がないので5分ぐらいで終わります。認証情報の隣にある認証情報を作成というボタンをクリックしてOuthクライアントIDを選択する
→アプリケーションの種類をiOSにする→バンドルIDはXcodeで作成したアプリのBoundleIdentifierをコピペする。2.認証画面を作成
認証画面は、クライアントアプリがアクセストークンを作成する時に、エンドユーザーに対して認可する権限を与える画面です。この画面自体プログラマーが作る必要がありません。もうすでに用意されている。
認証画面の途中でスコープ
を聞かれると思います。そのとき、制限付きのスコープのhttps://mail.google.com/を選択する。(今回はメール情報を取得するから)
テストユーザーも追加しておくこれでサーバーサイドの設定はしゅーーーりょーーーーーーーーう!!
これから、Xcodeで作業します。3.OAuth2.0を利用して、クライアントアプリにトークンを受けとる
OAuthを利用する準備
この記事みながらセットアップした。
ライブラリのインストールにCocoaPodsを使用します。
ターミナルを開けて〜〜〜〜〜cd アプリのディレクトリアプリのディレクトリに移動します。
pod initポッドファイルを作成。
pod 'GoogleAPIClientForREST/Gmail', '~> 1.3.0' pod 'GTMAppAuth'podファイルに上記を追加して
pod updateアップデートしたら、大丈夫だぁ〜〜〜〜〜
アクセストークンをリクエスト
今回使う変数を全て定義するぞ〜
その前に、したのインポートを忘れずにimport AppAuth import GoogleAPIClientForREST import GTMAppAuthprivate let scopes = ["https://mail.google.com/"] private let kClientID = "クライアントID" private let kRedirectURL = URL.init(string: "リダイレクトURL"+":/oauthredirect")! private let configuration = GTMAppAuthFetcherAuthorization.configurationForGoogle() private var authorization: GTMAppAuthFetcherAuthorization? private let userId=Gメールアドレスscopes
2の認証画面の設定で使用したスコープをいれる。
kClientIDとkRedirectURL
google.com/api
上のリンクから認証情報のOAuth2.0クライアントIDに追加されているアプリの編集画面に行けばみれる!
クライアントID
とiOSのURLスキーム
あります。
kClientID=クライアントID
kRedirectURL=iOSのURLスキーム+":/oauthredirect"です
(:/oauthredirect"を忘れずに! 俺は忘れていた。)認証画面を表示させる
private func showAuthorizationDialog() { let request = OIDAuthorizationRequest.init(configuration: configuration, clientId: self.kClientID, scopes: scopes, redirectURL: kRedirectURL, responseType: OIDResponseTypeCode, additionalParameters: nil) let appDelegate: AppDelegate = UIApplication.shared.delegate as! AppDelegate appDelegate.currentAuthorizationFlow=OIDAuthState.authState(byPresenting: request, presenting: self, callback: { (authState, err) in if let error = err { NSLog("\(error)") } else { if let authState = authState { self.authorization = GTMAppAuthFetcherAuthorization.init(authState: authState) GTMAppAuthFetcherAuthorization.save(self.authorization!, toKeychainForName: "authorization") } } }) }軽く説明していきます。
let request = OIDAuthorizationRequest.init(configuration: configuration, clientId: self.kClientID, scopes: scopes, redirectURL: kRedirectURL, responseType: OIDResponseTypeCode, additionalParameters: nil)認証画面を表示するためのサーバーに対するリクエストを表しています。
responseType:は応答のタイプを表しています。
additionalParameters:はクライアントの追加の許可パラメーターです。今回はないのでnilを渡します。let appDelegate: AppDelegate = UIApplication.shared.delegate as! AppDelegateAppDelegateにアクセスしている理由は後ほどわかります。
appDelegateに変数を置いている理由は、異なるSwiftファイル間で変数を共有するためです。
AppDelegateに宣言した変数は、どこのViewControllerでも共有できる。appDelegate.currentAuthorizationFlow=OIDAuthState.authState(byPresenting: request, presenting: self, callback: { (authState, err) in if let error = err { NSLog("\(error)") } else { //OIDAuthStateからGTMAppAuthFetcherAuthorizationを作成します。 if let authState = authState { self.authorization = GTMAppAuthFetcherAuthorization.init(authState: authState) GTMAppAuthFetcherAuthorization.save(self.authorization!, toKeychainForName: "authorization") } } })上記のコードで認証要求を実行します。
appleDelegateクラスにcurrentAuthorizationFlowをOIDExternalUserAgentSession?型で宣言しておく。
より詳しく知りたいかたは下のリンクに飛べ〜〜〜〜〜〜〜〜
README.md// Serialize to Keychain GTMAppAuthFetcherAuthorization.save(_authorization, toKeychainForName: kGTMAppAuthExampleAuthorizerKey) // Deserialize from Keychain let authorization = GTMAppAuthFetcherAuthorization.init(fromKeychainForName: kGTMAppAuthExampleAuthorizerKey) // Remove from Keychain GTMAppAuthFetcherAuthorization.removeFromKeychain(forName: kGTMAppAuthExampleAuthorizerKey)一番上のコードでキーチェーンを利用して、ログイン状態を保存します。
いくつもログインしたい場合は、名前を変えて保存したらいいだけだから、すごい便利ですね。
したの記事でキーチェーンの理解をしておくといいかももも桃もお
キーチェーンの記事ここまでで、認証完了!!
4.Gmail APIを叩く
やっと最後の章になります。長かった〜、、、、
コード全体
@objc func message(_ senfder:UIButton){ let query = GTLRGmailQuery_UsersMessagesList.query(withUserId: userId) let service = GTLRGmailService() service.authorizer = GTMAppAuthFetcherAuthorization.init(fromKeychainForName: "authorization") service.executeQuery( query, delegate: self, didFinish: #selector(callback) ) } @objc func callback( ticket : GTLRServiceTicket, finishedWithObject response : GTLRGmail_ListMessagesResponse, error : NSError? ){ if let error = error { print("メッセージリストの取得に失敗しました。") print(error) return } print("メッセージリストの取得に成功しました。") guard let message=response.messages?.first else {return} guard let messageid=message.identifier else {return} guard let url=URL(string: "https://gmail.googleapis.com/gmail/v1/users/\(userId)/messages/\(messageid)") else {return} var urlRequest=URLRequest(url: url) guard let token=authorization?.authState.lastTokenResponse?.accessToken else {return} urlRequest.addValue("Bearer \(token)", forHTTPHeaderField: "Authorization") let session=URLSession(configuration: URLSessionConfiguration.default) let task=session.dataTask(with: urlRequest){ (data,urlResponse,err) in if let err=err { print("失敗しました\(err)") } do{ let responseJson=try JSONDecoder().decode(res.self, from: data!) responseJson.payload.headers.forEach { if($0.name=="From"){ print($0.value) } } }catch(let err){ print("失敗した\(err)") } }.resume() }コードの解説
説明ながなる〜〜〜〜〜〜〜〜疲れた〜〜〜〜〜〜〜〜
@objc func message(_ senfder:UIButton){ let query = GTLRGmailQuery_UsersMessagesList.query(withUserId: userId) let service = GTLRGmailService() service.authorizer = GTMAppAuthFetcherAuthorization.init(fromKeychainForName: "authorization") service.executeQuery( query, delegate: self, didFinish: #selector(callback) ) }messageメソッドはボタンが押されたら呼ばれるメソッドです。
詳しく見ていきます。let query = GTLRGmailQuery_UsersMessagesList.query(withUserId: userId)GTLRGmailQuery_UsersMessagesList.queryはユーザーのメールボックス内のメッセージを一覧表示する命令です。useIdはGmailアドレスです。
let service = GTLRGmailService() service.authorizer = GTMAppAuthFetcherAuthorization.init(fromKeychainForName: "authorization")GTLRGmailServiceはGmailAPIクエリを実行するための作業人みたいな感じだと思います。
この作業する人が本人かどうか調べます。authorizerに、認証完了している情報をキーチェーンを用いて、GTMAppAuthFetcherAuthorizationを生成して代入する。これは、認証許可している人かどうか調べるためです。service.executeQuery( query, delegate: self, didFinish: #selector(callback) )作業人が本人だったら作業開始します。
その時、callback関数の引数に結果が帰っていきます。
callbackの中身を具体的に見ていきます。@objc func callback( ticket : GTLRServiceTicket, finishedWithObject response : GTLRGmail_ListMessagesResponse, error : NSError? ){ if let error = error { print("メッセージリストの取得に失敗しました。") print(error) return } print("メッセージリストの取得に成功しました。") guard let message=response.messages?.first else {return} guard let messageid=message.identifier else {return} guard let url=URL(string: "https://gmail.googleapis.com/gmail/v1/users/\(userId)/messages/\(messageid)") else {return} var urlRequest=URLRequest(url: url) guard let token=authorization?.authState.lastTokenResponse?.accessToken else {return} urlRequest.addValue("Bearer \(token)", forHTTPHeaderField: "Authorization") let session=URLSession.shared session.dataTask(with: urlRequest){ (data,urlResponse,err) in if let err=err { print("失敗しました\(err)") } do{ let responseJson=try JSONDecoder().decode(res.self, from: data!) responseJson.payload.headers.forEach { if($0.name=="From"){ print($0.value) } } }catch(let err){ print("失敗した\(err)") } }.resume() }if let error = error { print("メッセージリストの取得に失敗しました。") print(error) return }引数のerrorに値が入っていたら、エラーを表示し、returnでcallback関数を抜けます。
guard let message=response.messages?.first else {return} guard let messageid=message.identifier else {return}response.messagesは,配列でいーーーっぱい、messageIDとthreadIDが格納されています
今回最初のメッセージが取得したいのでresponse.messages?.firstで配列の最初のメッセージ情報を取得します
ほんでもって,message.identifierでメッセージIDを取得します.このメッセージIDを用いて,URLを生成します.
guard let url=URL(string: "https://gmail.googleapis.com/gmail/v1/users/\(userId)/messages/\(messageid)") else {return} var urlRequest=URLRequest(url: url) guard let token=authorization?.authState.lastTokenResponse?.accessToken else {return} urlRequest.addValue("Bearer \(token)", forHTTPHeaderField: "Authorization")先ほどのmessageidを用いてURLを生成します。
その後、URLRequest型を定義します。
URLRequest型は通信のリクエストを表現する型です。HTTPリクエストのURL、ヘッダ、メソッド、ボディなどの情報を持ちます。
ヘッダにはアクセストークンが必要です。
(urlRequest.httpMethod="GET"を途中に書いても問題ないと思います。)
ヘッダにアクセストークンを追加します。
forHTTPHeaderFieldについては、通信に関するお話でよくわからないです。( i _ i )
また、勉強しておきます。
Appleのドキュメントに詳しく乗っています。let session=URLSession.shared session.dataTask(with: urlRequest){(data,urlResponse,err) in if let err=err { print("失敗しました\(err)") } do{ let responseJson=try JSONDecoder().decode(res.self, from: data!) responseJson.payload.headers.forEach { if($0.name=="From"){ print($0.value) } } }catch(let err){ print("失敗した\(err)") } }.resume()URLSessionクラスは,URL経由でのデータの送受信に使います。
リクエストはタスクと呼ばれます。
詳しく知りたい方はしたの記事を参考に
【Swift】URLSessionまとめ
URLSession.sharedはURLSessionクラスにデフォルト値が設定されたインスタンスです。if let err=err { print("失敗しました\(err)") }通信が完了して、エラーが帰ってきたらエラーを表示します
do{ let responseJson=try JSONDecoder().decode(res.self, from: data!) responseJson.payload.headers.forEach { if($0.name=="From"){ print($0.value) } } }catch(let err){ print("失敗した\(err)") }通信が完了してdataの中に一番最新のメール情報に関するJSONファイルがあるのでパースします。
JsonDecoder().decode()でSwiftの型に変換しています。
今回、誰から連絡きたのかが知りたいのでforEachでループして探しています。。。
resは以下のようです。struct res: Codable{ var payload:Playload struct Playload:Codable { var headers:[Header] } struct Header:Codable { var name:String var value:String } }誰から、連絡きていたのか?を記事に載せようとしたけど、しょーもない人だったのでやめときます。。
感想
初心者なのでわからないことばかりですが、少しでもお力になれれば幸いです。
- 投稿日:2021-03-29T16:00:25+09:00
[iOS14]UITableViewCellに置いたボタンが押せなくなった
かなり昔に書かれたObjective-Cソースコードで、iOS14になってから発生したバグについてです。
何が起きたか
UITableViewCellサブクラスの実装で、コードでレイアウトしていたソースがありました。
こんな感じです。- (void)awakeFromNib { [super awakeFromNib]; UIButton *button = [[UIButton alloc] init]; [button addTarget:self action:@selector(doSomething:) forControlEvents:UIControlEventTouchUpInside]; [self addSubView: button]; }このボタンが、iOS14ではなぜか押せなくなりました。
原因
iOS14からは、UITableViewCell.contentView(UITableViewCellContentView)がUITableViewCellの大きさになっており、かつ一番手前に来るようになりました。
なので、上記のソースではUITableViewCell
自体にaddSubView
していたため、UIButtonよりも前面にUITableViewCellContentView
が来てしまい、UIButtonが押せなくなりました。解決方法
//[self addSubView: button]; [self.contentView addSubView: button];これで押せるようになります。
参考資料
- 投稿日:2021-03-29T13:58:32+09:00
【Swift】TargetedExtensionとは
はじめに
僕のSwiftで最も好きな文法はExtensionです笑
基礎的なExtensionの使い方はこちらをみてみてください。
TargetedExtensionというものがあるらしいので、みていきましょう。Extensionとは?
詳細なエクステンションの説明は省きますが、エクステンションは以下のように構造体やクラス、プロトコルを拡張してメソッドなどを追加できます。
例を見てみましょう。extension Int { var minus10: Self { return self - 10 } func plus100() -> Self { return self + 100 } } print(200.minus10) //190 print(200.plus100().plus100().minus10) //390こちらも合わせてご覧ください(メソッドチェーン)
TargetedExtensionとは?
前項の例だといいのですが、命名によってはこのメソッドは既にあるものなのか、自分で作ったものなのかわからない場合がありますし、Xcodeの予測変換でメソッドやプロパティが自分で作った数だけ多くなるので、見辛くなってしまうかもしれません。できればXcodeの補完を汚さずに、自作のメソッドなどにアクセスしたいです。
このような時に役に立つのがTargetedExtensionです。
前項のプログラムを修正していきましょう。protocol SomeComapatible { associatedtype ComapatibleType var own: ComapatibleType { get } } class Some<T> { private let base: T init(_ base: T) { self.base = base } } extension SomeComapatible { var own: Some<Self> { return Some(self) } } extension Int: SomeComapatible { } extension Some where T == Int { var minus10: Int { return base - 10 } func plus100() -> Int { return base + 100 } }こうすることで、以下のように
own
を間に挟まないとminus10
プロパティやplus100
メソッドにアクセスできないようにできました。print(1000.minus10) //エラー print(1000.own.plus100()) //1100解説
ステップ1
このプロトコルを採用したものは
own
プロパティを持つようにする。protocol SomeComapatible { associatedtype ComapatibleType var own: ComapatibleType { get } }ステップ2
Int
に先ほどのプロパティを準拠させます。しかし、own
プロパティの実装がありませんので、次で実装しましょう。extension Int: SomeComapatible { }ステップ3
以下のように実装します。
Some<Self>
のSelf
はプロトコルSomeComapatible
を準拠させた具体的な型なので、今回はInt
ですね。
そして、return Some(self)
のself
はprint(1000.own.plus100())
このように書いた時の1000
のことです。
このように、ジェネリクスも用いてあげることで、Int
だけでなくString
などでも同じown
を利用する事ができます。class Some<T> { private let base: T init(_ base: T) { self.base = base } } extension SomeComapatible { var own: Some<Self> { return Some(self) } }ステップ4
では、具体的にメソッドなどを定義していきます。
extension Some where T == Int { var minus10: Int { return base - 10 } func plus100() -> Int { return base + 100 } }この
minus10
とplus100
はT
がInt
の時のみ使えると言う条件を付け足しました。
base
はInt
なので、実際は1000
などが入ってきます。おわりに
メソッドチェーンはこのような考慮がかけています。ぜひ、この記事を参考に書き換えてみてください。
- 投稿日:2021-03-29T13:57:31+09:00
Flutter で pod install が失敗する [!] The 'Pods-Runner' target has transitive dependencies that include statically linked binaries
出力されているエラー
$ pod install Analyzing dependencies cloud_firestore: Using Firebase SDK version '6.33.0' defined in 'firebase_core' firebase_analytics: Using Firebase SDK version '6.33.0' defined in 'firebase_core' firebase_core: Using Firebase SDK version '6.33.0' defined in 'firebase_core' firebase_messaging: Using Firebase SDK version '6.33.0' defined in 'firebase_core' firebase_remote_config: Using Firebase SDK version '6.33.0' defined in 'firebase_core' Downloading dependencies [!] The 'Pods-Runner' target has transitive dependencies that include statically linked binaries: (/path-to/ios/Flutter/Flutter.framework)解決方法
今後実行するスクリプトは全てiosディレクトリ内で実行してください
$ cd iosまずは更地に戻します
$ flutter clean $ rm -rf Flutter/Flutter.framework Pods Podfile.lockPodfileに追記します
# NOTE: For detail ? https://github.com/CocoaPods/CocoaPods/issues/3289 pre_install do |installer| Pod::Installer::Xcode::TargetValidator.send(:define_method, :verify_no_static_framework_transitive_dependencies) {} endパッケージを入れ直してビルドします
$ flutter pub get $ flutter build ios $ pod install次のようなログが出ると成功です
$ pod install Analyzing dependencies cloud_firestore: Using Firebase SDK version '6.33.0' defined in 'firebase_core' firebase_analytics: Using Firebase SDK version '6.33.0' defined in 'firebase_core' firebase_core: Using Firebase SDK version '6.33.0' defined in 'firebase_core' firebase_messaging: Using Firebase SDK version '6.33.0' defined in 'firebase_core' firebase_remote_config: Using Firebase SDK version '6.33.0' defined in 'firebase_core' Downloading dependencies Generating Pods project Integrating client project Pod installation complete! There are 17 dependencies from the Podfile and 39 total pods installed.間違った解決方法
use_frameworks! をコメントアウトする
[!] The 'Pods-Runner' target has transitive dependencies that include static frameworks: (FirebaseCore, FirebaseDatabase, FirebaseMessaging, FirebaseDatabase, FirebaseCore, FirebaseMessaging, FirebaseMessaging, FirebaseCore, and FirebaseDatabase)cf. https://github.com/flutter/flutter/issues/20045#issuecomment-409492211
- 投稿日:2021-03-29T13:31:12+09:00
いい感じのランダムの色が欲しいなぁって時にこれ。
例えば10個のいい感じのランダムの色が欲しいって時。
ガチのランダムにすると意味わからない変な色が出来上がったりしてしまうので。
っていうものをメモ程度に残しておきます。
意外と使える!hueの部分の分母を欲しい個数にするだけです。
saturationとbrightnessに関しては自分の好みの色味にしてください。下記コードそのまま使用した際にはこんな感じの色が出力されます。
似てる色があるとか言わないで
#CC8B29
#ABCC29
#49CC29
#29CC6A
#29CCCC
#296ACC
#4929CC
#AB29CC
#CC298B
#CC2929
swift.swiftlet colorList = [ UIColor(hue: 0/10, saturation: 0.8, brightness: 0.8, alpha: 1), UIColor(hue: 1/10, saturation: 0.8, brightness: 0.8, alpha: 1), UIColor(hue: 2/10, saturation: 0.8, brightness: 0.8, alpha: 1), UIColor(hue: 3/10, saturation: 0.8, brightness: 0.8, alpha: 1), UIColor(hue: 4/10, saturation: 0.8, brightness: 0.8, alpha: 1), UIColor(hue: 5/10, saturation: 0.8, brightness: 0.8, alpha: 1), UIColor(hue: 6/10, saturation: 0.8, brightness: 0.8, alpha: 1), UIColor(hue: 7/10, saturation: 0.8, brightness: 0.8, alpha: 1), UIColor(hue: 8/10, saturation: 0.8, brightness: 0.8, alpha: 1), UIColor(hue: 9/10, saturation: 0.8, brightness: 0.8, alpha: 1) ]java.java/* * いい感じの色のリストを指定数分作成する * colorCount: 色の数 * brightness: 色味 0.0~1.0 * */ private int[] createRandomColorList(int colorCount, float brightness) { float [] f = new float[3]; f[1] = brightness; f[2] = brightness; int[] cList = new int[colorCount]; for (int i = 0; i < colorCount; i++) { f[0] = 360f * (i+1)/colorCount; cList[i] = Color.HSVToColor(f); } return cList; }
- 投稿日:2021-03-29T10:13:12+09:00
[Swift] 大文字のSelfまとめ
はじめに
小文字のselfはよく使いますが、大文字のSelfというのもあります。
大文字のSelfについてまとめてみました。型の内部で型自身へのアクセス
以下のように、
Self.value
で構造体A
に紐付いたvalueにアクセスすることができます。struct A { static let value = 10 func printValue() { print(Self.value) } }ちなみに、Selfを書かずに直接スタティックプロパティ(value)にアクセスしようとすると
Static member 'value' cannot be used on instance of type 'A'
このようなエラーが出てきてしまいます。スタティックメソッドでも同様です。
struct B { var name: String var age: Int static func isOld(_ x: Int) -> Bool { return x > 60 } func ageFunc() { print(Self.isOld(age)) } }ちなみに、
Self
を構造体名のB
と書き換えても同じことです。ここではSelf == B
ですね。プロトコルの定義での利用
プロトコルはそれに準拠させた型の振る舞いを事前に宣言しておくことができますが、プロトコル自身はどの型で準拠されるのかは知りません。例えるなら、車の大きさ、色、ハンドルの位置などは製造の段階で決める事ができますが、製造の段階では一体この車はどんな人が乗るのかは知りません。利用者(型)が購入(準拠)して初めてどんな人が乗るのか(プロトコル自身がどの型で準拠されるのか)を知る事ができます。
しかし、その型と同じ型のデータを返すメソッドや、同じ型同士を比較したい時もあります。その時にSelfキーワードを使います。注意点として、前項ではその定義を含む型自身をSelfと置いていましたが、本項では少し扱い方が異なります。つまり、プロトコル定義内でSelfを用いてもプロトコルがSelfというわけではありません。ここでのSelfとは、
プロトコルを採用した具体的な型 == Self
です。protocol C { associatedtype Element var x: Element { get } var y: Element { get } func replace() -> Self static func +(lhs: Self, rhs: Self) -> Self }このように、xとyを入れ替えるreplaceメソッド、左辺の右辺を加算するスタティックメソッドをprotocol Cで宣言しています。replaceメソッドの戻り値にSelfを指定しています。この時点ではどのような型に準拠するのかわからないためです。スタティックメソッド+も同様です。
構造体Dに準拠させてみましょう。struct D: C { typealias Element = Int var x: Int var y: Int func replace() -> D { return D(x: y, y: x) } static func +(lhs: D, rhs: D) -> D { return D(x: lhs.x + rhs.x, y: lhs.y + rhs.y) } }このように、Selfのところが具体的なかたDに変わったのがわかります。このプロトコルCは他の型にも紐付く事ができるようにするため、できるだけ具体的な型はプロトコル宣言時に書きたくないためです。(associatedtypeもそのため)
クラス内での利用
プロトコルとは違い、メソッドの返り値の型としてのみ扱う事ができます。
class E { func someFunc() -> Self { return self } }エクステンションでの利用
前述の通り、プロトコルを準拠する型をSelfで参照できますが、一定の条件を満たす型のみプロトコルエクステンションを有効にしたいこともあります。
そのような条件を記述するときにSelfでその型を表す事ができます。protocol SomeProtocol { } class SomeClass { } extension SomeProtocol where Self: SomeClass { func someFunc() { print(type(of: self)) } } class SomeSubClass: SomeClass, SomeProtocol { } let someSubClass = SomeSubClass() someSubClass.someFunc()この例では、
SomeProtocol
を準拠する型が条件Self: SomeClass
つまり、SomeClassを継承している場合のみsomeFunc()
を利用する事ができます。
以下のように、SomeProtocol
に準拠していてもSomeSubClass2
はSomeClass
を継承していない(条件を満たしていない)ため、someFunc()
にアクセスしようとするとエラーが出てきてしまいます。class SomeSubClass2: SomeProtocol { } let someSubClass2 = SomeSubClass2() someSubClass2.someFunc()エラー
Referencing instance method 'someFunc()' on 'SomeProtocol' requires that 'SomeSubClass2' inherit from 'SomeClass'
おわりに
selfと違ってSelfはややこしいですね。
- 投稿日:2021-03-29T10:13:12+09:00
【Swift】大文字のSelfまとめ
はじめに
小文字のselfはよく使いますが、大文字のSelfというのもあります。
大文字のSelfについてまとめてみました。型の内部で型自身へのアクセス
以下のように、
Self.value
で構造体A
に紐付いたvalueにアクセスすることができます。struct A { static let value = 10 func printValue() { print(Self.value) } }ちなみに、Selfを書かずに直接スタティックプロパティ(value)にアクセスしようとすると
Static member 'value' cannot be used on instance of type 'A'
このようなエラーが出てきてしまいます。スタティックメソッドでも同様です。
struct B { var name: String var age: Int static func isOld(_ x: Int) -> Bool { return x > 60 } func ageFunc() { print(Self.isOld(age)) } }ちなみに、
Self
を構造体名のB
と書き換えても同じことです。ここではSelf == B
ですね。プロトコルの定義での利用
プロトコルはそれに準拠させた型の振る舞いを事前に宣言しておくことができますが、プロトコル自身はどの型で準拠されるのかは知りません。例えるなら、車の大きさ、色、ハンドルの位置などは製造の段階で決める事ができますが、製造の段階では一体この車はどんな人が乗るのかは知りません。利用者(型)が購入(準拠)して初めてどんな人が乗るのか(プロトコル自身がどの型で準拠されるのか)を知る事ができます。
しかし、その型と同じ型のデータを返すメソッドや、同じ型同士を比較したい時もあります。その時にSelfキーワードを使います。注意点として、前項ではその定義を含む型自身をSelfと置いていましたが、本項では少し扱い方が異なります。つまり、プロトコル定義内でSelfを用いてもプロトコルがSelfというわけではありません。ここでのSelfとは、
プロトコルを採用した具体的な型 == Self
です。protocol C { associatedtype Element var x: Element { get } var y: Element { get } func replace() -> Self static func +(lhs: Self, rhs: Self) -> Self }このように、xとyを入れ替えるreplaceメソッド、左辺の右辺を加算するスタティックメソッドをprotocol Cで宣言しています。replaceメソッドの戻り値にSelfを指定しています。この時点ではどのような型に準拠するのかわからないためです。スタティックメソッド+も同様です。
構造体Dに準拠させてみましょう。struct D: C { typealias Element = Int var x: Int var y: Int func replace() -> D { return D(x: y, y: x) } static func +(lhs: D, rhs: D) -> D { return D(x: lhs.x + rhs.x, y: lhs.y + rhs.y) } }このように、Selfのところが具体的なかたDに変わったのがわかります。このプロトコルCは他の型にも紐付く事ができるようにするため、できるだけ具体的な型はプロトコル宣言時に書きたくないためです。(associatedtypeもそのため)
クラス内での利用
プロトコルとは違い、メソッドの返り値の型としてのみ扱う事ができます。
class E { func someFunc() -> Self { return self } }エクステンションでの利用
前述の通り、プロトコルを準拠する型をSelfで参照できますが、一定の条件を満たす型のみプロトコルエクステンションを有効にしたいこともあります。
そのような条件を記述するときにSelfでその型を表す事ができます。protocol SomeProtocol { } class SomeClass { } extension SomeProtocol where Self: SomeClass { func someFunc() { print(type(of: self)) } } class SomeSubClass: SomeClass, SomeProtocol { } let someSubClass = SomeSubClass() someSubClass.someFunc()この例では、
SomeProtocol
を準拠する型が条件Self: SomeClass
つまり、SomeClassを継承している場合のみsomeFunc()
を利用する事ができます。
以下のように、SomeProtocol
に準拠していてもSomeSubClass2
はSomeClass
を継承していない(条件を満たしていない)ため、someFunc()
にアクセスしようとするとエラーが出てきてしまいます。class SomeSubClass2: SomeProtocol { } let someSubClass2 = SomeSubClass2() someSubClass2.someFunc()エラー
Referencing instance method 'someFunc()' on 'SomeProtocol' requires that 'SomeSubClass2' inherit from 'SomeClass'
おわりに
selfと違ってSelfはややこしいですね。
- 投稿日:2021-03-29T07:16:39+09:00
try Swift:スケジュール画面の解析
はじめに
try Swiftのスケジュール画面を解析する。ゴールは登場するクラスの洗い出しと関連性のざっくりとした把握。
スケジュール画面のスクリーンショット
スケジュール画面の構成及び構成する主なクラス
画面構成
[SessionTableViewCell達]
↑
[SessionTableViewController1, SessionTableViewController2, SessionTableViewController3]
↑
ScheduleViewControllerUI部分
ScheduleViewController:スケジュール画面の母体
ButtonBarView:画面上部のバーボタン(ボタンによって画面のコンテンツが表示される)
SessionTableViewController:画面のコンテンツが表示される画面
SessionTableViewCell:スケジュールコンテンツ画面モデル部分
ConferenceDay:会議日付及びSession情報を含む構造体
SessionBlock:Sessionsのスタート、エンド、Session情報達
Session:Session情報