20210725のSwiftに関する記事は10件です。

Buttonを押すとSegmentが増える(UISegmentedControl)

完成形はこちら 機能説明 Buttonを押すと、UISegmentedControlのSegmentが増えます。 コードと簡単説明 最初に表示するUISegmentedControlを作成 .insertSegment(withTitle: String, at: Int, animated: Bool)でSegmentを追加していきます SegmentControl import Foundation import UIKit class SegmentControl{ var uiSegmentControl = UISegmentedControl() //インスタンス作成 var segmentContentsArray = ["0","1","2"] //最初に表示するSegment } extension SegmentControl{ func createSegmentControl(targetView:UIView){ uiSegmentControl = UISegmentedControl(items: []) //同じitemが入らないようにする for count in 0...segmentContentsArray.count - 1{ uiSegmentControl.insertSegment(withTitle: segmentContentsArray[count], at: count, animated: true) // Segmentを追加する } uiSegmentControl.frame = CGRect(x: targetView.bounds.minX, y: targetView.bounds.minY + 100, width: targetView.frame.size.width / CGFloat(segmentContentsArray.count), height: targetView.frame.size.height / 20) uiSegmentControl.backgroundColor = UIColor.white uiSegmentControl.selectedSegmentTintColor = UIColor.gray //選択中のSegmentの色を変えています targetView.addSubview(uiSegmentControl) } } segmentControl.createSegmentControl(targetView: self.view)でUISegmentedControlを表示します .allSatisfy({Int($0)! < segmentCount})はBool値を返すので、今回はsegmentControl.segmentContentsArrayの値が全てsegmentCount未満ならば、同じ値が存在しないという事になるので新しいSegmentを追加します。 Segmentを増やす度に、segmentControl.uiSegmentControl.frame.widthも増やさないとSegmentが細かくなっていきます。 ViewController import UIKit class ViewController: UIViewController { let segmentControl = SegmentControl() var segmentCount = 0 override func viewDidLoad() { super.viewDidLoad() } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) segmentControl.createSegmentControl(targetView: self.view) } @IBAction func plusSegmentControl(_ sender: Any) { segmentCount += 1 if segmentControl.segmentContentsArray.allSatisfy({Int($0)! < segmentCount}) == true{ //segmentContentsArrayの全ての値がsegmentCountの値未満なら segmentControl.segmentContentsArray.append(String(segmentCount)) segmentControl.uiSegmentControl.insertSegment(withTitle: String(segmentCount), at: segmentCount , animated: true) segmentControl.uiSegmentControl.frame = CGRect(x: segmentControl.uiSegmentControl.bounds.minX, y: segmentControl.uiSegmentControl.bounds.minY + 100, width: segmentControl.uiSegmentControl.frame.width + 35, height: segmentControl.uiSegmentControl.frame.size.height) print(segmentControl.segmentContentsArray) }else{ print(segmentControl.segmentContentsArray) } } } ボタンを押した時の、segmentContentsArrayの増え方 print結果 ["0", "1", "2"] //1回目の処理は、segmentCount=1で、すでにsegmentContentsArrayに存在するのでSegmentが追加されません ["0", "1", "2"] //2回目の処理は、segmentCount=2で、すでにsegmentContentsArrayに存在するのでSegmentが追加されません ["0", "1", "2", "3"] //3回目以降は、segmentContentsArrayに存在しない値なのでSegmentが追加されます。 ["0", "1", "2", "3", "4"] ["0", "1", "2", "3", "4", "5"] ["0", "1", "2", "3", "4", "5", "6"] ["0", "1", "2", "3", "4", "5", "6", "7"] ["0", "1", "2", "3", "4", "5", "6", "7", "8"] ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"] 終わり UISegmentedControlのSegmentのTitleが被らないようにしたいと思い考えてみました。 ご指摘、ご質問などありましたら、コメントまでお願い致します。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

プログラミングを始めて1年間で読んだ書籍

概要 これまでexcelやAccessは使用してきましたが,プログラミングには一切触れてきませんでした。 ずっとスマホアプリを作りたいと思っていたので、26歳にしてプログラミングの学習を始めました。 学習の目的は第一にスマホアプリの作成とリリース、第二にアプリエンジニアに転職するためです。 そこで学習も1年経ちましたので、1年間で読んだ書籍を恐れ多くも紹介します。 ベテランの皆様、おすすめの書籍があったらご教授下さい!! 書籍一覧 ・HTML&CSSとWebデザインが 1冊できちんと身につく本 ・デザイン入門教室[特別講義] 確かな力を身に付けられる ~学び、考え、作る授業~ ・確かな力が身につくJavaScript「超」入門 第2版 ・詳細! SwiftUI iPhoneアプリ開発入門ノート[2020] iOS 14+Xcode 12対応 ・絶対に挫折しない iPhoneアプリ開発「超」入門 第8版 ・【Swift5/Xcode12】超入門クイズアプリ開発講座〜自分のアプリをリリースしてみよう〜 ・Swift実践入門 ── 直感的な文法と安全性を兼ね備えた言語 ・よくわかるAuto Layout iOSレスポンシブデザインをマスター ・App Development with Swift ・キタミ式イラストIT塾 基本情報技術者 HTML&CSSとWebデザインが 1冊できちんと身につく本 プログラミング知識ゼロで一番最初に読んだ本です。 いきなりアプリは作れないと思い、ネットでどの言語からスタートするのが良いか調べていたところ, HTMLが難易度が比較的低く始めやすいとのことでしたので近所の本屋でこの本を買いました。 結果としてHTMLから始めたのは正解でした。この本は事前知識ゼロを前提として書かれていると思います。 デザイン入門教室[特別講義] 確かな力を身に付けられる ~学び、考え、作る授業~ プログラミングの記事なのに2番目で早速プログラミングと関係ない本が出てきました。笑 1番目の本で学んだhtmlでページを書いているうちにどのようなデザインが見やすいのか、自身の勝手なセンスでデザインして良いのかと疑問が出てきました。この本はユーザーインターフェイス等のデザインだけでなく、 PowerPointやKeynoteでスライドを作る時に知識が大いに役立つ思います。 配色や文字サイズ、フォント、余白が読み手に与える印象をわかりやすく解説しています。 "人に見せる資料を作成する全ての社会人"はデザインの基本を身につけるべき!とさえ思いました。 確かな力が身につくJavaScript「超」入門 第2版 HTMLとCSSでページを作成できるようになりましたが、いまいちなんか古臭い、windowsXPを使用していた時代のようなページになってしまうことに気づきました。調べてみると現在のHPはほとんどがJavaScriptというものが使われているようです。この本でJavaScriptの基本やjQueryについて学び、ハンバーガーメニューの実装をしました。 詳細! SwiftUI iPhoneアプリ開発入門ノート[2020] iOS 14+Xcode 12対応 一通りHTMLついて学んだので、ここからアプリ作成にシフトしていきます。 iPhoneとiPadを持っていたので、言語はSwiftにしました。 私はSwiftとSwiftUIの違いもわからずSwiftUIから始めましたが、初心者はAutolayoutとswiftで始めた方が良いと感じます。理由はSwiftUIが比較的新しいものであり、ネットでの情報がSwiftUIよりSwiftの方が多いからです。実際現場での開発もSwiftUIはまだあまり使われていないようなQittaの記事をいくつか見ました。 絶対に挫折しない iPhoneアプリ開発「超」入門 第8版 kindle版で購入しました。 初めてkindleの電子書籍を購入したのですが、重い本を持ち歩かなくて良い(→出張へ持ち運びや電車で読むのに便利!)というメリットの反面、使い勝手が非常に悪く閲覧しにくいという大きなデメリットがありました。具体的には拡大・縮小が思ったように操作できない、拡大しても解像度は変わらない(つまり拡大するとぼやける)という点です。本自体も初心者には非常に良いのですが、内容が先程紹介した「詳細! SwiftUI iPhoneアプリ開発入門ノート」とかなり似通っているのでどちらかの購入でいいと思いました。 【Swift5/Xcode12】超入門クイズアプリ開発講座〜自分のアプリをリリースしてみよう〜 初心者向けにSwift/Xcodeのブログを書かれているsatorikuさんという方の本です。 電子書籍限定です。 ブログの説明が非常に丁寧でわかりやすかったので購入しました。 広告の実装(google AdMob)やリリース方法まで詳しく書かれていました。 Swift実践入門 ── 直感的な文法と安全性を兼ね備えた言語 初心者向けのアプリ開発の本は本の通りにコーディングすればアプリは完成するのですが、あまり応用が聞きませんでした。オリジナルのアプリの開発としての知識は不十分です。 "〇〇アプリの作り方!"ではなく"プログラミング言語"としてのswiftを学ぶために本書を購入しました。 内容は初心者向けではなく中級者以上と感じました。私のレベルでは理解できない部分も多いですが、内容が網羅されていると感じ、これから何を勉強すれば良いかがわかり非常に満足です。 よくわかるAuto Layout iOSレスポンシブデザインをマスター Amazonで新品が4980円からと高額なので中古で購入しました。(定価は2600円) アプリ開発をはじめていくつか壁がありましたがその一つはAutoLayoutでした。 詳細に書かれていますが、読むに当たって注意点があります。 2016年に出版された本ですのでios10くらいでしょうか。少し古い内容・今では使用されていないものがありますのでこの点を自身で調べて更新する必要があります。 App Development with Swift "App Development with Swift”はSwiftの基礎知識を証明する資格です。 こちらはその資格のApple公式ドキュメントです。電子書籍のAppleBooksのみ配信です。 無料ですが、総1015ページ全て英語の文書になります。 難しい英語は使われておりませんので、TOEIC630点程度の英語力でも概ね理解できました。 キタミ式イラストIT塾 基本情報技術者 転職について情報を調べると、ブラックな情報ばかり目についてしまいます。 Youtubeで転職について語っている方が、「ブラックだとかいう前に入社する前に基本情報技術者くらいは取りましょう。」とおっしゃっておりこの資格の存在を知り取ろうと思いました。 この本はイラストが多用されており、わかりやすい反面ページを食ってしまい内容の割に本が分厚いです。 どんぐり君ときのこ君がほぼ毎ページで親父ギャグを言っています。 今まで名前は聞いたことがあるが、詳しくは説明できないIT用語がたくさん出てきました。 ノート取りながら1周するのに1ヶ月以上かかりました。 まとめ 私はHTML→CSS→JavaScript→Swiftの順で学習をしました。 本の力は偉大ですが、実際何かアプリを作ろうとすると、書籍の知識だけでは絶対に不可能でした。 QiitaやStackOverflow等でネット上から求める情報を調べ出す力が重要ということも学びました。 これがいわゆる"Google力"というやつなのですね。 以上、これからプログラミングを始める方の参考になればと思い、Qitta初投稿でした。 購入総金額:22592円
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Swift初学者が自動販売機ロジックを書いてみたら…(その1)

今回は自動販売機ロジックについて学習したのでアウトプットします。 ちなみに私はMac触り始めて半年の者です。(ガチガチの初心者です) 初学者のアウトプット記事ですので、間違っている点など多々あるかと思いますが、暖かい目で見守っていただけますと幸いです。 はじめに 現在、私はMENTAで、ヤマタクメンターにご指導いただいております。 ヤマタクメンターのURL https://menta.work/user/1840 今回は課題として、自動販売機のロジックをXcodeのPlaygroundでコーディングしてみました。 簡単に言うと自動販売機の仕組みをコードで表現しましたみたいなことですね。 サンプルコードは一切見ずに、あーだこーだ言いながら作りました(笑) 初学者が作ったものなので、ツッコミどころ満載だと思いますがご容赦ください? ちなみに例外ケースは考えずに、とりあえず一発目のコードを掲載してます。 今後、改善するつもりです。 仕様 ・お金を入力する(0以上) ・飲み物は3種類(水100円、コーヒー150円、エナジードリンク200円)から選べる ・入力したお金が飲み物の値段より高ければ(お釣りが0以上だったら)、選択した飲み物とお釣りが出力される ・各飲み物の初期在庫数を5本とし、飲み物が買われたら、その飲み物の在庫数を-1する ・飲み物を選択しなかったら、”飲み物を選んでね”と出力 ・飲み物が買えなかったら(お金が足りなかったら)、”お金が足りません”と出力 ・在庫数に変化がなければ(減らなかったら)、”在庫変動はありません”と出力 今後は ・在庫がなかった場合の挙動 ・釣り銭切れの挙動 ・使えないお金が入力された場合の挙動    以上を追加していこうと思っています。 実装コード qiita.swift import UIKit //入れるお金 var inputedYen:Int = 200 //飲み物を選択 ※Drink.water,Drink.coffee or Drink.energyDrinkで選択する var wantedDrink:Drink? = nil //飲み物の種類 enum Drink{ case water case coffee case energyDrink } //飲み物の値段 var drinkPrice = 0 //お釣り var change = 0 //各飲み物の初期在庫数 var stockOfWater = 5 var stockOfCoffee = 5 var stockOfEnergyDrink = 5 //飲み物の在庫を減らす関数 func reduceStock () { switch wantedDrink { case .water: stockOfWater = stockOfWater - 1 case .coffee: stockOfCoffee = stockOfCoffee - 1 case .energyDrink: stockOfEnergyDrink = stockOfEnergyDrink - 1 case .none: break } } //飲み物の在庫数を確認する関数 func checkStock() { print("waterの在庫は\(stockOfWater)個です") print("coffeeの在庫は\(stockOfCoffee)個です") print("energyDrinkの在庫は\(stockOfEnergyDrink)個です") } //飲み物を選択した時に、その値段を決める関数。選択しなければ選ぶよう出力する switch wantedDrink { case .water: drinkPrice = 100 case .coffee: drinkPrice = 150 case .energyDrink: drinkPrice = 200 case nil: print("飲み物を選んでね") checkStock() print("在庫変動はありません") } //お釣りを計算する関数 func calculatedChange () { change = inputedYen - drinkPrice } //入れたお金が0円以上かつ、飲み物を選択した場合、お釣りと飲み物を出力 if inputedYen >= 0 && wantedDrink != nil{ calculatedChange() //お釣りが0円以上であれば飲み物とお釣りを出し、お金が足りなければエラーメッセージを出力 if change >= 0 { print(wantedDrink!) print("お釣りは\(change)です") reduceStock() checkStock() }else{ print("お金が足りません") checkStock() print("在庫変動はありません") } } おわりに 最初は自動販売機ロジックってなんですか??から始まり、ほんとに作れるのかと不安でしたが、やってみるとすごく楽しいんです! 自分の期待している挙動になるよう、もっとまとまったコードになるようにと、今まで読んだ参考書をめくりながら、試行錯誤しながら作りました。 まだ改善の余地はいっぱいあると思うので、頑張りたいと思います!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

viewDidLoad内で、viewのwidthなどが0になってしまい取得できない(Swift. の画面レイアウト周りの備忘録)

Swiftのレイアウトで詰まったポイント swiftのレイアウト周りで詰まったので、備忘録もかねてまとめておこうと思います。swift5、Xcode12です。storyboardやswiftUIではなく、swift(コード)で書いています。(storyboardやswiftUI使えば凄い楽なんですけどね…レイアウトにこだわるとコードで書くのが一番良いらしいです。) viewDidLoad内で、viewのwidthなどが0になってしまい取得できない 上記の通りですが、下記のコードのようにviewDidLoad内でview1のwidthとheightの値が0になってしまい、view2が描画できていません。 view.backgroundColor = UIColor.lightGray let view1 = UIView() let view2 = UIView() self.view.addSubview(view1) self.view.addSubview(view2) view1.translatesAutoresizingMaskIntoConstraints = false view1.backgroundColor = UIColor.green view1.widthAnchor.constraint(equalTo: self.view.widthAnchor, multiplier: 0.5).isActive = true view1.heightAnchor.constraint(equalTo: self.view.heightAnchor, multiplier: 0.5).isActive = true view1.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true view1.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true view2.translatesAutoresizingMaskIntoConstraints = false view2.backgroundColor = UIColor.blue view2.frame.size.width = view1.frame.size.width / 2 view2.frame.size.height = view1.frame.size.height / 2 //view1のwidthとheightは0になってしまう view2.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true view2.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true 上記コードの実行結果: 期待される結果: viewDidLoad内でview1に対して、view1.widthAnchor.constraint()を用いてオートレイアウトを使った。そして、そのviewのwidthの値を用いて、下記の方法でview2の長さを決めようとした。 原因  原因は、制約やレイアウトが反映されていないため。viewDicLoad内でviewに制約を加えても、その場では更新されない。 解決策 1. レイアウトでのサイズ指定(widthやheight)をせずに、制約としてサイズの指定をする。 AnchorやNSLayoutConstraintで制約を指定する。 //widthやheightでレイアウトの指定をしない view2.widthAnchor.constraint(equalTo: self.view.widthAnchor, multiplier: 0.4).isActive = true view2.heightAnchor.constraint(equalTo: self.view.heightAnchor, multiplier: 0.4).isActive = true 2. 制約やレイアウトを更新する。 レイアウトが更新されないなら、そのタイミングで更新するだけ。 下記コードではうまく更新できない。タイミングの問題かと思い、色々なタイミングで実行したけどできない。なんでだろ? self.view.updateConstraintsIfNeeded() self.view.layoutIfNeeded() まとめ 奥が深いなぁ… 下記引用です。 Viewの読み込み 制約の追加(AutoLayout) 制約を元にViewのframeを計算(レイアウト) frameの位置に描画(レンダリング) という順番で読み込むので、オートレイアウトがされ、frameの値が決められた後にframeを直接編集するとオートレイアウトで決定した制約が死んでしまうのかな…なるほど? 今後の課題 基本的にはオートレイアウトですべて決定するのが良さそう…? もしくは、他のオートレイアウトの方法との組み合わせはいける? widthAnchorやheightAnchorのAnchorについても今度調べようかなと思いました。 参考文献 viewのライフサイクル viewのライフサイクル2 オートレイアウト(Anchor) オートレイアウト(NSLayoutConstraint) レイアウトと制約の更新 レイアウト・制約・描画のタイミングなど
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

viewDidLoad内で、viewのwidthなどが0になってしまい取得できない(Swiftの画面レイアウト周りの備忘録)

Swiftのレイアウトで詰まったポイント swiftのレイアウト周りで詰まったので、備忘録もかねてまとめておこうと思います。swift5、Xcode12です。storyboardやswiftUIではなく、swift(コード)で書いています。(storyboardやswiftUI使えば凄い楽なんですけどね…レイアウトにこだわるとコードで書くのが一番良いらしいです。) viewDidLoad内で、viewのwidthなどが0になってしまい取得できない 上記の通りですが、下記のコードのようにviewDidLoad内でview1のwidthとheightの値が0になってしまい、view2が描画できていません。 view.backgroundColor = UIColor.lightGray let view1 = UIView() let view2 = UIView() self.view.addSubview(view1) self.view.addSubview(view2) view1.translatesAutoresizingMaskIntoConstraints = false view1.backgroundColor = UIColor.green view1.widthAnchor.constraint(equalTo: self.view.widthAnchor, multiplier: 0.5).isActive = true view1.heightAnchor.constraint(equalTo: self.view.heightAnchor, multiplier: 0.5).isActive = true view1.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true view1.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true view2.translatesAutoresizingMaskIntoConstraints = false view2.backgroundColor = UIColor.blue view2.frame.size.width = view1.frame.size.width / 2 view2.frame.size.height = view1.frame.size.height / 2 //view1のwidthとheightは0になってしまう view2.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true view2.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true 上記コードの実行結果: 期待される結果: viewDidLoad内でview1に対して、view1.widthAnchor.constraint()を用いてオートレイアウトを使った。そして、そのviewのwidthの値を用いて、下記の方法でview2の長さを決めようとした。 原因  原因は、制約やレイアウトが反映されていないため。viewDicLoad内でviewに制約を加えても、その場では更新されない。 解決策 1. レイアウトでのサイズ指定(widthやheight)をせずに、制約としてサイズの指定をする。 AnchorやNSLayoutConstraintで制約を指定する。 //widthやheightでレイアウトの指定をしない view2.widthAnchor.constraint(equalTo: self.view.widthAnchor, multiplier: 0.4).isActive = true view2.heightAnchor.constraint(equalTo: self.view.heightAnchor, multiplier: 0.4).isActive = true 2. 制約やレイアウトを更新する。 レイアウトが更新されないなら、そのタイミングで更新するだけ。 下記コードではうまく更新できない。タイミングの問題かと思い、色々なタイミングで実行したけどできない。なんでだろ? self.view.updateConstraintsIfNeeded() self.view.layoutIfNeeded() まとめ 奥が深いなぁ… 下記引用です。 Viewの読み込み 制約の追加(AutoLayout) 制約を元にViewのframeを計算(レイアウト) frameの位置に描画(レンダリング) という順番で読み込むので、オートレイアウトがされ、frameの値が決められた後にframeを直接編集するとオートレイアウトで決定した制約が死んでしまうのかな…なるほど? 今後の課題 基本的にはオートレイアウトですべて決定するのが良さそう…? もしくは、他のオートレイアウトの方法との組み合わせはいける? widthAnchorやheightAnchorのAnchorについても今度調べようかなと思いました。 参考文献 viewのライフサイクル viewのライフサイクル2 オートレイアウト(Anchor) オートレイアウト(NSLayoutConstraint) レイアウトと制約の更新 レイアウト・制約・描画のタイミングなど
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

swift(storyboard)のViewControllerで扱う変数とメソッドをMain.storyboardと紐付ける

[変数の扱い方] https://saisai-weblink.com/2021/07/23/swiftstoryboardのmain-storyboardとviewcontorollerを紐付けた変数を作る/ [メソッドの扱い方] https://saisai-weblink.com/2021/07/23/【画像付き】swiftstoryboardでボタンを作成してメソッドと/
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ジェネリクス(備忘録)

・ジェネリクスとは? ・ジェネリクスとは、Swift実践入門(p.236)によると、型をパラメータとして受け取ることで汎用的なプログラムを記述するための機能である。これにより、関数や型を汎用的かつ型安全に記述できる。 ・定義方法 ・ジェネリック関数の場合 func 関数名<型引数>(引数: 型引数) -> 戻り値の型 { 関数呼び出し時に実行される文 } ・ジェネリック型(型引数をもつクラス、構造体、列挙型)の場合 struct 構造体名<型引数> { 構造体の定義 } 型引数として宣言された方は、ジェネリック関数やジェネリック型の内部で通常の型と同等に扱うことができる。 ・特殊化 実際にジェネリック関数を呼び出したり、ジェネリック型をインスタンス化するときには、型引数に具体的な型を指定する必要があり、具体的な型引数を与えて型を確定させることを特殊化という。 配列にString型やInt型を当てはめて使用することができるのも特殊化のひとつである。 var intArray = [1,2,3,] var stringArray = ["a","b","c"] 特殊化の方法はジェネリック関数とジェネリック型それぞれについて大きく分けて二つある。 ・ジェネリック関数の場合 ①引数からの型推論を用いる方法 func someFunc<T>(_ argument: T) -> T { return argument } let someString = someFunc("Hello") //String型 let someInt = someFunc(100) //Int型 ②戻り値からの型推論を用いる方法 戻り値からの型推論で特殊化を行う際には、ジェネリック関数の戻り値の型が型引数となっていて、かつ、戻り値の代入先の型が決まっている必要がある。 func someFunc<T>(_ argument: Any) -> T? { return argument as? T } let someString: String? = someFunc("Hello") //TはString型となる let error = someFunc("Error") //エラー ・ジェネリック型の場合 ①<>内に型引数を明示する方法 struct SomeStringStruct<Some> { let name : Some } let someString = SomeStringStruct<String>(name: "okadai") //String型 ②型推論によって型引数を推論する方法 struct SomeIntStruct<Some> { let num : Some } let someInt = SomeIntStruct(num: 100) //Int型 ・型制約 型制約により、準拠すべきプロトコルやスーパークラスなど、型引数にはさまざまな制約を設けることができる。これにより、型の性質を利用でき、ジェネリック関数やジェネリック型をより詳細に扱うことができる。 ・ジェネリック関数の場合 設けることができる制約 ①スーパークラスや準拠するプロトコルに対する制約 func 関数名<型引数: プロトコル名やスーパークラス名>(引数) { 関数呼び出し時に実行される文 } func isEqual<T: Equatable>(_ x: T, _ y: T) -> Bool { return x == y } isEqual("abc", "def") // false ②連想型のスーパークラスや準拠するプロトコルに対する制約 func 関数名<型引数: プロトコル>(引数) -> 戻り値の型 where 連想型: プロトコルやスーパークラス { 関数呼び出し時に実行される文 } func sorted<T: Collection>(_ argument: T) -> [T.Element] where T.Element: Comparable { return argument.sorted() } sorted([3,2,1]) ③型どうしの一致を要求する制約 func 関数名<型引数1: プロトコル1, 型引数2: プロトコル2>(引数) -> 戻り値の型 where プロトコル1の連想型 == プロトコル2の連想型 { 関数呼び出し時に実行される文 } //この場合は、要素の型の一致を要求している func concat<T: Collection, U: Collection>(_ argument1: T, _ argument2: U) -> [T.Element] where T.Element == U.Element { return Array(argument1) + Array(argument2) } let array = [1,2,3] let set = Set([1,2,3]) let result = concat(array, set) //[1,2,3,1,2,3] ・ジェネリック型の場合 設けることができる制約 ①スーパークラスや準拠するプロトコルに対する制約 struct 型名<型引数: プロトコル名やスーパークラス名> { 構造体の定義 } ・ジェネリック型の型制約付きエクステンション 型の定義では、①スーパークラスや準拠するプロトコルに対する制約しか使用できなかったが、エクステンションでは、②連想型のスーパークラスや準拠するプロトコルに対する制約、③型どうしの一致を要求する制約も使えるようになる。 extension ジェネリック型名 where 型制約 { 制約を満たす場合に有効となるエクステンション } struct Pair<Element> { let first: Element let second: Element } extension Pair where Element == String { func hasElement(containing character: Character) -> Bool { return first.contains(character) || second.contains(character) } } let stringPair = Pair(first: "abc", second: "def") stringPair.hasElement(containing: "e") // true ・プロトコルへの条件付き準拠 ・ジェネリック型の型制約付きエクステンションでは、プロトコルへの準拠も可能。 extension ジェネリック型名: 条件付き準拠するプロトコル名 where 型制約 { 制約を満たす場合に有効となるエクステンション } struct Pair<Element> { let first: Element let second: Element } extension Pair: Equatable where Element: Equatable { static func == (_ lhs: Pair, _ rhs: Pair) -> Bool { return lhs.first == rhs.first && lhs.first == rhs.second } } let stringPair1 = Pair(first: "abc", second: "def") let strinfPair2 = Pair(first: "def", second: "ghi") let stringPair3 = Pair(first: "abc", second: "def") stringPair1 == strinfPair2 // false stringPair1 == stringPair3 // true
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Swift】Sign in with Appleの実装について

概要 この記事は僕が学習時に挑戦するときに詰まってしまったので記事におこして自分のメモ用にと初学者の手助けになればと思い記事にしました。 実装できたらログインが簡単なのでユーザーにも優しいアプリ作成ができます。 Appleログインを実装するにたりApple developerの有償の登録が必要です。 手順1 Apple developerでアプリの登録 先ずは、下記の画像の赤枠をクリックします。 ログインして頂き①を選択して下さい。 下記の画像の様に移動しますので「Identifiers」を選択して下さい。 移動すると登録しているアプリが表示されます。 そこで赤枠のプラスボタンをクリックして下さい。 「App IDs」が選択されているのを確認して赤枠の「Continue」をクリックして下さい。 「App」が選択されていることを確認して赤枠の「Continue」をクリックして下さい。 下記の画像の様な画面になりますので画像2の様に「Description」には分かりやすい様にご自身で作成したプロジェクト名をつけて頂き「Bundle ID」は作成したプロジェクトからコピペで貼り付けて下さい。 そして下にスクロールして頂き画像3の赤枠の部分にチェックを入れ右上の「Continue」をクリックして下さい。 [画像1] [画像2] [画像3] 下記の画像の画面になりますので右上の「Register」をクリックして登録完了です。 そうすると「Identifiers」に先ほど作成したものが追加されていれば成功です。 これでApple developerにアプリの登録は完了です。 手順2 Firebaseの設定 今回はFirebaseでのプロジェクト作成の方法については割愛させて頂き「Sign in with Apple」の方法だけ説明させて頂きます。 ご自身で作成したプロジェクトを選択して頂き「Authentication」を選択し「Sigan-in method」を選択します。 そこでログイン方法を選択するのですが「Apple」を選択して頂くと画像2の様な画面になりますので無効になっているのを有効に変更し保存して下さい。 これでFirebaseの設定は終わりです。 [画像1] [画像2] 手順3 Appleログイン機能の実装 今回cocoaPodsに下記の4つをインストールします。 pod 'Firebase' pod 'Firebase/Auth' pod 'Firebase/Firestore' pod 'PKHUD', '~> 5.0' 続いてXcodeでプロジェクトを開いて頂き下記の画像の①を選択し②をクリックして下さい。 そして画像2のような画面になりますので③で「Apple」と検索すると④の様に「Sign in with Apple」が出てきますのでそちらをダブルクリックして追加して下さい。 そうすると画像3の赤枠の様に追加されます。 [画像1] [画像2] [画像3] 設定は全て終わったのでそれではコードを書いていきます 今回僕はViewControllerに実装しました。 先ずはコードで「Sign in with Apple」ボタンを実装します。 調べた感じだとStoryboardにボタンを配置してやっても上手くいかないらしくコードで実装します。 ViewController.swift //先ずは下記をimportして下さい。 import AuthenticationServices import CryptoKit import Firebase import PKHUD import FirebaseAuth override func viewDidLoad() { super.viewDidLoad() downloadFirebase.downloadTweet() if #available(iOS 13.0, *) { // ここでインスタンス(ボタン)を生成 // authorizationButtonTypeでボタンのデザインを変更できる // authorizationButtonStyleでボタンの配色を変更できる let appleLoginButton = ASAuthorizationAppleIDButton( authorizationButtonType: .default, authorizationButtonStyle: .whiteOutline ) // ボタン押した時にhandleTappedAppleLoginButtonの関数を呼ぶようにセット appleLoginButton.addTarget( self, action: #selector(handleTappedAppleLoginButton(_:)), for: .touchUpInside ) // ↓はレイアウトの設定 // これを入れないと下の方で設定したAutoLayoutが崩れる appleLoginButton.translatesAutoresizingMaskIntoConstraints = false // Viewに追加 view.addSubview(appleLoginButton) // ↓はAutoLayoutの設定 // appleLoginButtonの中心を画面の中心にセットする appleLoginButton.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true appleLoginButton.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true // appleLoginButtonの幅は、親ビューの幅の0.7倍 appleLoginButton.widthAnchor.constraint(equalTo: self.view.widthAnchor, multiplier: 0.7).isActive = true // appleLoginButtonの高さは40 appleLoginButton.heightAnchor.constraint(equalToConstant: 40.0).isActive = true } } 続いて上記のコードの下に下記のコードも記述して下さい。 Firebaseのドキュメントによると、最初に実行する必要があるのは、暗号で保護されたナンスを生成し、Appleから認証リクエストを行うときにそれを使用します。 ナンスを生成するコードはFirebaseから提供してくれているのでそれを活用します。 ViewController.swift @available(iOS 13.0, *) //上記で作成した#selector(handleTappedAppleLoginButton(_:))のobjctの作成をします。 @objc func handleTappedAppleLoginButton(_ sender: ASAuthorizationAppleIDButton) { // ランダムの文字列を生成 let nonce = randomNonceString() // delegateで使用するため代入 currentNonce = nonce // requestを作成 let request = ASAuthorizationAppleIDProvider().createRequest() //これで初回の1回だけemailと名前を取得する事ができます。 request.requestedScopes = [.email, .fullName] // sha256で変換したnonceをrequestのnonceにセット request.nonce = sha256(nonce) // controllerをインスタンス化する(delegateで使用するcontroller) let controller = ASAuthorizationController(authorizationRequests: [request]) controller.delegate = self controller.presentationContextProvider = self controller.performRequests() } func randomNonceString(length: Int = 32) -> String { precondition(length > 0) let charset: Array<Character> = Array("0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._") var result = "" var remainingLength = length while remainingLength > 0 { let randoms: [UInt8] = (0 ..< 16).map { _ in var random: UInt8 = 0 let errorCode = SecRandomCopyBytes(kSecRandomDefault, 1, &random) if errorCode != errSecSuccess { fatalError("Unable to generate nonce. SecRandomCopyBytes failed with OSStatus \(errorCode)") } return random } randoms.forEach { random in if length == 0 { return } if random < charset.count { result.append(charset[Int(random)]) remainingLength -= 1 } } } return result } // ⑤SHA256を使用してハッシュ変換する関数を用意 @available(iOS 13, *) private func sha256(_ input: String) -> String { let inputData = Data(input.utf8) let hashedData = SHA256.hash(data: inputData) let hashString = hashedData.compactMap { return String(format: "%02x", $0) }.joined() return hashString } 下記のコードは認証が成功したときと失敗した時のコードを記述しています。 ViewController.swift // ⑥extensionでdelegate関数に追記していく extension ViewController: ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding { // 認証が成功した時に呼ばれる関数 func authorizationController(controller _: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) { // credentialが存在するかチェック guard let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential else { return } UserDefaults.standard.setValue(appleIDCredential.user, forKey: "appleAuthorizedUserIdKey") // nonceがセットされているかチェック guard let nonce = currentNonce else { fatalError("Invalid state: A login callback was received, but no login request was sent.") } // credentialからtokenが取得できるかチェック guard let appleIDToken = appleIDCredential.identityToken else { print("Unable to fetch identity token") return } // tokenのエンコードを失敗 guard let idTokenString = String(data: appleIDToken, encoding: .utf8) else { print("Unable to serialize token string from data: \(appleIDToken.debugDescription)") return } // 認証に必要なcredentialをセット let credential = OAuthProvider.credential( withProviderID: "apple.com", idToken: idTokenString, rawNonce: nonce ) // Firebaseへのログインを実行 Auth.auth().signIn(with: credential) { (authResult, error) in if let error = error { print(error) // 必要に応じて HUD.flash(.labeledError(title: "予期せぬエラー", subtitle: "再度お試しください。"), delay: 1) return } if let authResult = authResult { print(authResult) // 必要に応じて HUD.flash(.labeledSuccess(title: "ログイン完了", subtitle: nil), onView: self.view, delay: 1) { _ in //ここにログインが完了した時に画面遷移のコードを記述して下さい。 } } } } // delegateのプロトコルに設定されているため、書いておく func presentationAnchor(for _: ASAuthorizationController) -> ASPresentationAnchor { return view.window! } // Appleのログイン側でエラーがあった時に呼ばれる func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) { // Handle error. print("Sign in with Apple errored: \(error)") } } 上記のコードで全て実装完了です。 下に全てまとめたコードをを載せておきますので使用する際はコピペで使用して下さい。 ViewController.swift // // ViewController.swift // Sample // // // import UIKit import AuthenticationServices import CryptoKit import Firebase import PKHUD import FirebaseAuth class ViewController: UIViewController { fileprivate var currentNonce: String? fileprivate var downloadFirebase = DownloadFirebase() override func viewDidLoad() { super.viewDidLoad() downloadFirebase.downloadTweet() if #available(iOS 13.0, *) { // ここでインスタンス(ボタン)を生成 let appleLoginButton = ASAuthorizationAppleIDButton( authorizationButtonType: .default, authorizationButtonStyle: .whiteOutline ) // ボタン押した時にhandleTappedAppleLoginButtonの関数を呼ぶようにセット appleLoginButton.addTarget( self, action: #selector(handleTappedAppleLoginButton(_:)), for: .touchUpInside ) // ↓はレイアウトの設定 // これを入れないと下の方で設定したAutoLayoutが崩れる appleLoginButton.translatesAutoresizingMaskIntoConstraints = false // Viewに追加 view.addSubview(appleLoginButton) // ↓はAutoLayoutの設定 // appleLoginButtonの中心を画面の中心にセットする appleLoginButton.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true appleLoginButton.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true // appleLoginButtonの幅は、親ビューの幅の0.7倍 appleLoginButton.widthAnchor.constraint(equalTo: self.view.widthAnchor, multiplier: 0.7).isActive = true // appleLoginButtonの高さは40 appleLoginButton.heightAnchor.constraint(equalToConstant: 40.0).isActive = true } } @available(iOS 13.0, *) @objc func handleTappedAppleLoginButton(_ sender: ASAuthorizationAppleIDButton) { // ランダムの文字列を生成 let nonce = randomNonceString() // delegateで使用するため代入 currentNonce = nonce // requestを作成 let request = ASAuthorizationAppleIDProvider().createRequest() // sha256で変換したnonceをrequestのnonceにセット request.requestedScopes = [.email, .fullName] request.nonce = sha256(nonce) // controllerをインスタンス化する(delegateで使用するcontroller) let controller = ASAuthorizationController(authorizationRequests: [request]) controller.delegate = self controller.presentationContextProvider = self controller.performRequests() } func randomNonceString(length: Int = 32) -> String { precondition(length > 0) let charset: Array<Character> = Array("0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._") var result = "" var remainingLength = length while remainingLength > 0 { let randoms: [UInt8] = (0 ..< 16).map { _ in var random: UInt8 = 0 let errorCode = SecRandomCopyBytes(kSecRandomDefault, 1, &random) if errorCode != errSecSuccess { fatalError("Unable to generate nonce. SecRandomCopyBytes failed with OSStatus \(errorCode)") } return random } randoms.forEach { random in if length == 0 { return } if random < charset.count { result.append(charset[Int(random)]) remainingLength -= 1 } } } return result } // ⑤SHA256を使用してハッシュ変換する関数を用意 @available(iOS 13, *) private func sha256(_ input: String) -> String { let inputData = Data(input.utf8) let hashedData = SHA256.hash(data: inputData) let hashString = hashedData.compactMap { return String(format: "%02x", $0) }.joined() return hashString } } // ⑥extensionでdelegate関数に追記していく extension ViewController: ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding { // 認証が成功した時に呼ばれる関数 func authorizationController(controller _: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) { // credentialが存在するかチェック guard let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential else { return } UserDefaults.standard.setValue(appleIDCredential.user, forKey: "appleAuthorizedUserIdKey") // nonceがセットされているかチェック guard let nonce = currentNonce else { fatalError("Invalid state: A login callback was received, but no login request was sent.") } // credentialからtokenが取得できるかチェック guard let appleIDToken = appleIDCredential.identityToken else { print("Unable to fetch identity token") return } // tokenのエンコードを失敗 guard let idTokenString = String(data: appleIDToken, encoding: .utf8) else { print("Unable to serialize token string from data: \(appleIDToken.debugDescription)") return } // 認証に必要なcredentialをセット let credential = OAuthProvider.credential( withProviderID: "apple.com", idToken: idTokenString, rawNonce: nonce ) // Firebaseへのログインを実行 Auth.auth().signIn(with: credential) { (authResult, error) in if let error = error { print(error) // 必要に応じて HUD.flash(.labeledError(title: "予期せぬエラー", subtitle: "再度お試しください。"), delay: 1) return } if let authResult = authResult { print(authResult) // 必要に応じて HUD.flash(.labeledSuccess(title: "ログイン完了", subtitle: nil), onView: self.view, delay: 1) { _ in //ここにログインが完了した時に画面遷移などのコードを記述して下さい。 } } } } // delegateのプロトコルに設定されているため、書いておく func presentationAnchor(for _: ASAuthorizationController) -> ASPresentationAnchor { return view.window! } // Appleのログイン側でエラーがあった時に呼ばれる func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) { // Handle error. print("Sign in with Apple errored: \(error)") } } 終わり 中々、調べてもこのての記事が出てこず少しでも誰かの手助けになればと思っています。 説明もあまりできない箇所もあるのでもっと自分自身でわかれば随時更新していくつもりです。 ユーザー側に立った時にこのログイン方法を実装すると簡単にログインできるので覚えておくと良いのかなと思っています。 ただ、ユーザ名やプロフィール画像を使用した機能を実装する場合は別で用意して登録してもらう様にしないといけないのでそこは注意が必要です。 最後にもし指摘などがあれば教えていただけると幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

URLSession Swift

APIとは アプリケーションプログラミングインターフェイスの略称。 他のプログラムと簡単に接続できる 殆どのプログラムではデータの送受信等にAPIが使用されている. SwiftでAPIを取ってくるにはどうすればよいか ① URLの作成 ② URLセッションの作成 ③ URLセッションにタスクを与える。 ④ タスクを始める この順番でコードを書いていく api.swift //例 //ネットワーキングする場所 func performRequest(urlString:String) { //① URLの作成 if let url = URL(string: urlString) {//optionalURL型で帰ってくる //② URLセッションの作成(基本デフォルト設定) let session = URLSession(configuration: .default) //③ URLセッションにタスクを与える。//completionHandlerとは値として関数を取ります。 let task = session.dataTask(with: url, completionHandler: handle(data:response:error:)) //④ タスクを始める(データを取ってくる) task.resume() } } func handle(data:Data?,response:URLResponse?,error:Error?){ //errorがわたってくるのでエラーがあったかどうか確かめられる。 if error != nil { print(error!) return } if let safeData = data { let dataString = String(data: safeData, encoding: .utf8) print(dataString!) } } クロージャーを使うと簡潔にかけるので通常は下記の様に書く closure.swift func performRequest(urlString:String) { //① URLの作成 if let url = URL(string: urlString) { //② URLセッションの作成(基本デフォルト設定) let session = URLSession(configuration: .default) //③ URLセッションにタスクを与える。//completionHandlerとは値として関数を取ります。 let task = session.dataTask(with: url) { data, response, error in if error != nil { print(error) return } if let SafeData = data { let dataString = String(data: safeData, encoding: .utf8) print(dataString!) } } //④ タスクを始める task.resume() } } これだけだとString型のものしかとってこれないのでこれをParseJsonをしてswiftでも使えるようにしなくてはいけない。 補足情報 URLSessionとは ネットワーキングする際にネットワーク上のデータをまとめてダウンロードしてくれたりアップロードしてくれたりする。 URLSessionTaskとは URLSessionがなにをするのかをきめる。ダウンロードなのかアップロードなのか等(URLSessionDataTask URLSessionUploadTask URLSessionDownloadTask) URLSessionConfigurationとは URLSessionの設定。.defaultでデフォルト設定が多いが、キャッシュやクッキーの扱い方によってbackground ephemeralなどに設定する必要がある。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

タプル型について

・タプル型とは? ・タプル型とは、複数の型をまとめて1つの型として扱うことができる型である。 ・タプル型の値をタプルという。 ・タプル型の定義とタプルの生成 // タプル型の定義 var sampleTuple = (Int, String) // タプルの生成 sampleTuple = (1,"Hello") ・要素へのアクセス ・要素へのアクセス方法は3つ ①インデックスによるアクセス ②要素名によるアクセス ③代入によるアクセス ・①インデックスによるアクセス let tuple = (100, "Hello") print(tuple.0) // 100 print(tuple.1) // Hello ・②要素名によるアクセス let tuple = (int: 100, string: "Hello") print(tuple.int) // 100 print(tuple.string) // Hello ・③代入によるアクセス let int: Int let string: String (int, string) = (100, "Hello") print(int) // 100 print(string) // Hello // この方法でも宣言できる let (int, string) = (100, "Hello") print(int) print(string)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む