- 投稿日:2021-09-01T22:15:06+09:00
UITextFieldDelegateのメソッド
はじめに 今回はUITextFieldDelegateのメソッドについてまとめます.自分用のメモ目的です. 環境 Xcode 12.5.1 Swift 5.4.2 UITextFieldDelegateのメソッド 1. textFieldShouldBeginEditing func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool { return true } 呼ばれるタイミング できること タップされたとき,入力可能になる直前 trueでテキスト入力可能,falseで入力不可 2. textFieldDidBeginEditing func textFieldDidBeginEditing(_ textField: UITextField) { print(textField.text)//textFieldに入力された値をOptional型で出力 } 呼ばれるタイミング できること タップされて入力可能になったあと テキストの値を取得 3. textFieldShouldEndEditing func textFieldShouldEndEditing(_ textField: UITextField) -> Bool { return true } 呼ばれるタイミング できること キーボードのReturnキーが押されて入力が完了する直前 trueで編集を停止,falseはそのまま 4. textFieldDidEndEditing func textFieldDidEndEditing(_ textField: UITextField, reason: UITextField.DidEndEditingReason) { print(textField.text)//textFieldに入力された値をOptional型で出力 } 呼ばれるタイミング できること キーボードが閉じたあと テキストの値を取得 5. textFieldShouldClear func textFieldShouldClear(_ textField: UITextField) -> Bool { return true } 呼ばれるタイミング できること キーボードのClearキーが押されたとき trueでクリア,falseは何もしない 6. textFieldShouldReturn func textFieldShouldReturn(_ textField: UITextField) -> Bool { textField.resignFirstResponder()//キーボードをを閉じる return true } 呼ばれるタイミング できること キーボードのReturnキーが押されたとき キーボードを閉じるなど 7. shouldChangeCharactersIn func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { return true } 呼ばれるタイミング できること テキスト編集中 入力文字数の制限など 参考 [初心者向け]UITextFieldDelegate
- 投稿日:2021-09-01T21:56:57+09:00
Firebaseのセットアップとアプリの紐付け
はじめに Firebaseのセットアップからアプリの紐付けまでのやり方の忘備録 環境 Xcode Version 12.5.1 (12E507) 手順 1.Firebaseのにプロジェクトを登録 ・FireBaseにアクセス https://firebase.google.com ・「使ってみる」を押しコンソールを表示 ・「プロジェクトを追加」を押す ・アプリ名を入力 ・「続行」を押す ・Googleアナリティクスアカウントに「Default Account for Firebase」を選択 ・以上でプロジェクトの準備は完了。続行を押してコンソールに戻る 2.アプリにFirebaseを登録する ・コンソールからセットアップしたアプリを開く ・iOSを選択 ・Xcodeで設定したバンドルIDを入力して「アプリを登録」を押す ※バンドルIDはXcodeのBundle Identifierと同じもの。 ・指示に従い、設定ファイルのダウンロードとプロジェクトへ追加 ※追加時のXcodeのメッセージは何も変更せずFinish 3.Firebase SDKの追加 ・Profileの作成 ターミナルを開き、Xcodeプロジェクトフォルダにcdコマンドで移動して $pod initを実行する ・profileを開き追加するSDKを入力 ターミナルで$open podfileコマンドを実行 ・インストールするpodを追記 入力したらcommand + s で保存。 ・podのインストール ターミナルで$pod installを実行してSDKをインストール。 ・一旦Xcodeプロジェクトを閉じる(Xcode終了) ・podが追加されたプロジェクトを開く ターミナルでプロジェクトフォルダを開き白地アイコンの.xcworkspaceを開く ・AppDelegateに初期化コードを追加する 以下①②の追記 AppDelegate.swift import UIKit //①Firebaseをインポート(追記) import Firebase @main class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. //②初期化コード追記 FirebaseApp.configure() return true } //以下省略 command + r で実行できれば以上で終了です。 最後に podを追加する場合は、profileに追記して$pod updateを実行する。
- 投稿日:2021-09-01T21:26:21+09:00
SwiftでNews APIを使ってみた
はじめに SwiftでNews APIを使ってニュースアプリを作ってみたいと思います。 初心者にもわかりやすく、AutoLayoutの設定、デザインパターン、コードの可読性もしっかり守っているので、APIの入門記事としてはぴったりかなと。 では始めていきます。ぜひ最後までご覧ください。 UIの設計 このように配置していきます。 NewsListTableViewControllerからDetailViewControllerまでのsegueのidentifierに"toWeb"とつけてください。 ArticleTableViewCellを作り、IBOutlet接続します。 ArticleTableViewCell.swift import Foundation import UIKit class ArticleTableViewCell: UITableViewCell { @IBOutlet weak var titleLabel: UILabel! @IBOutlet weak var descriptionLabel: UILabel! @IBOutlet weak var urlImageView: UIImageView! } 全体設計 UIができた後に、今回のアプリの設計を行なっていく。 APIの取得 まず、APIの取得からやっていきたいと思います。 NewsAPIを使います。 操作は以下。 ログインをする、またアカウントがない場合は新規アカウント登録を行う。 それができたら、ここでAPIKeyを取得する。 そしてこのようにAPIを叩くと、JSONデータを変換してくれます。 これらのデータをうまく使い今回はアプリを作成していきます。 Webservice 今回のAPIにおいてのロジックを管理するWebserviceを書いていきます。 Webservice.swift import Foundation class Webservice { func getArticles(with urlString:String,completion:@escaping ([Article]?) -> ()){ if let url = URL(string: urlString) { let session = URLSession(configuration: .default) let task = session.dataTask(with: url) { data, response, error in if error != nil { print(error!) completion(nil) } if let safeData = data { // print(response) let decoder = JSONDecoder() do { let decodedData = try decoder.decode(ArticleList.self, from: safeData) completion(decodedData.articles) //プリントをしながら中身を確認する //print(decodedData.articles[0].description) } catch { print(String(describing: error)) } } } task.resume() } } } Article レスポンスしたデータをデコードするためのArticleを作成していきます。 Article.swift import Foundation struct ArticleList: Codable { let articles: [Article] } struct Article: Codable{ let title:String let description:String let urlToImage:String let url:String } NewsListTableViewController 最後に取得したデータをViewに反映させる、またUITableViewの操作のためにViewControllerを作っていきます。 その前に画像のキャッシュのために便利なSDWebImageというライブラリを使いたいと思います。 SDWebImageの詳しい説明、導入の仕方などはこれらの記事を見るとわかると思います。 NewsListTableViewController import Foundation import UIKit import SDWebImage class NewsListTableViewViewController: UITableViewController{ fileprivate var articles: [Article] = [] var urlArticle = "" override func viewDidLoad() { super.viewDidLoad() setup() } private func setup(){ //ここにAPIKeyを挿入する let urlString = "https://newsapi.org/v2/top-headlines?country=us&apiKey=[APIKey]" Webservice().getArticles(with: urlString,completion: { (articles) in guard let data = articles else{ return } self.articles = data print(articles![0].title) print(articles![0].description) DispatchQueue.main.async { self.tableView.reloadData() } }) } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { guard let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as? ArticleTableViewCell else { fatalError("ArticleTableViewCell not found") } cell.titleLabel.text = articles[indexPath.row].title cell.descriptionLabel.text = articles[indexPath.row].description cell.urlImageView.sd_setImage(with: URL(string: articles[indexPath.row].urlToImage), completed: nil) return cell } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return articles.count } override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { self.urlArticle = articles[indexPath.row].url self.performSegue(withIdentifier: "toWeb", sender: nil) } override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == "toWeb" { let detailVC = segue.destination as! DetailViewController detailVC.urlString = self.urlArticle } } } そして最後はクリックした記事を閲覧させるためにWKWebViewを使っていきたいと思います。 DetailViewController import UIKit import WebKit class DetailViewController: UIViewController , WKUIDelegate{ var webView: WKWebView! var urlString = "" override func loadView() { let webConfiguration = WKWebViewConfiguration() webView = WKWebView(frame: .zero, configuration: webConfiguration) webView.uiDelegate = self view = webView } override func viewDidLoad() { super.viewDidLoad() let myURL = URL(string:urlString) let myRequest = URLRequest(url: myURL!) webView.load(myRequest) } } WKWebViewの処理はWKWebViewのドキュメントを確認しながら学んでください。 完成形はこちらを参照してください。 指摘点がありましたら、コメントでもよろしくお願いします。
- 投稿日:2021-09-01T20:30:03+09:00
Swift×Firebaseでチャットアプリを作ってみた
はじめに SwiftでFirebaseを使ってチャットアプリを作ってみたいと思います。 Firebase初心者にもわかりやすく、デザインパターン、コードの可読性もしっかり守っているので、Firebaseの入門アプリとしてはぴったりかなと。 では始めていきます。ぜひ最後までご覧ください。 UIの設計 このように配置していきます。 また上記のように、それぞれの画面に対応する、WelcomeViewController,RegisterViewController,LoginViewController,ChatViewControllerを作成し、それぞれの画面に接続します。 RegisterButtonを押しながら、RegisterViewControllerにドラッグ&ドロップしてsegueで結びます。 またLoginButtonを押しながら、LoginViewControllerにドラッグ&ドロップしてsegueで結びます。 ChatViewControllerにはそれぞれの画面からsegueで結んでください。そしてそれぞれのsegueのidentifierを RegisterToChat,LoginToChatにしてください。 それぞれのViewControllerに、IBOutlet,IBAction接続します。 WelcomeViewController.swift class WelcomeViewController: UIViewController{ @IBOutlet weak var titleLabel: UILabel! } RegisterViewController.swift import UIKit class RegisterViewController: UIViewController { @IBOutlet weak var emailTextField: UITextField! @IBOutlet weak var passwordTextField: UITextField! @IBAction func registerPressed(_ sender: UIButton) { } } LoginViewController.swift import UIKit class LoginViewController: UIViewController { @IBOutlet weak var emailTextField: UITextField! @IBOutlet weak var passwordTextField: UITextField! @IBAction func loginPressed(_ sender: UIButton) { } } ChatViewController import UIKit class ChatViewController: UIViewController { @IBOutlet weak var tableView: UITableView! @IBOutlet weak var messageTextField: UITextField! override func viewDidLoad() { super.viewDidLoad() } @IBAction func sendPressed(_ sender: UIButton) { } @IBAction func logOutPressed(_ sender: UIBarButtonItem) { } } Firebaseの導入 Firebaseを使うために今回はCocoapodsを導入します。 Cocoapodsの導入の仕方は、このサイトなどを見ながら各自行ってください。 ここでPodfileを以下のように挿入します。 pod 'Firebase/Auth' pod 'Firebase/Firestore' pod installで導入していきます。 そして、Firebaseプロジェクトを追加 そのあとはプロジェクトの作成の手順に従って作業を進めていきます。 このプロジェクトに、Bundle identifierを登録しプロジェクトと接続します。 次にXcodeとの初期設定として、以下のコードを追加していきます。 ここでタイムライン機能の時に使うキーボード表示時に便利な機能の追加も行っていきます。 AppDelegate.swift import UIKit import Firebase import IQKeyboardManagerSwift @main class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. FirebaseApp.configure() IQKeyboardManager.shared.enable = true IQKeyboardManager.shared.enableAutoToolbar = false IQKeyboardManager.shared.shouldResignOnTouchOutside = true return true } // MARK: UISceneSession Lifecycle func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { // Called when a new scene session is being created. // Use this method to select a configuration to create the new scene with. return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) } func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) { // Called when the user discards a scene session. // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. // Use this method to release any resources that were specific to the discarded scenes, as they will not return. } } ようやく準備が終わりました。 ここからホーム画面、ログイン、新規アカウント登録、ログアウト,タイムラインを作っていきます。 まずは、ホーム画面から実装していきましょう。 ホーム画面 WelcomeViewController import UIKit class WelcomeViewController: UIViewController { @IBOutlet weak var titleLabel: UILabel! override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) navigationController?.isNavigationBarHidden = true } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) navigationController?.isNavigationBarHidden = false } } 新規アカウント登録機能 RegisterViewController import UIKit import FirebaseAuth class RegisterViewController: UIViewController { @IBOutlet weak var emailTextField: UITextField! @IBOutlet weak var passwordTextField: UITextField! @IBAction func registerPressed(_ sender: UIButton) { //emailTextFieldもpasswordTextFieldも値が存在していた場合 if let email = emailTextField.text,let password = passwordTextField.text{ //アカウント作成 Auth.auth().createUser(withEmail: email, password: password) { authResult, error in if error != nil{ print(error) }else{ //アカウント作成に成功したら、chatViewControllerにナビゲートされる self.performSegue(withIdentifier: "RegisterToChat", sender: self) } } } } } ログイン機能 LoginViewController import UIKit import FirebaseAuth class LoginViewController: UIViewController { @IBOutlet weak var emailTextField: UITextField! @IBOutlet weak var passwordTextField: UITextField! @IBAction func loginPressed(_ sender: UIButton) { //emailTextFieldもpasswordTextFieldも値が存在していた場合 if let email = emailTextField.text,let password = passwordTextField.text{ //サインイン Auth.auth().signIn(withEmail: email, password: password) { authResult, error in if error != nil { print(error) }else{ //ログインに成功したら、chatViewControllerにナビゲートされる self.performSegue(withIdentifier: K.loginSegue, sender: self) } } } } } ## タイムライン機能 次にタイムライン機能を実装していきたいのですが、少しタイムラインのUIが複雑になりそうなので、CustomCellを使っていきたいと思います。 New File -> Cocoatouchclass -> MessageCell(subclass:UITableViewCell)でAlso Create XIBfileにチェックを入れ MessageCell.swiftとMessageMessageCell.xibを作成します。 MessageCell.xibはこのように配置します。 CellのidentifierにReusableCellとつけてください。 MessageCell.swift import UIKit class MessageCell: UITableViewCell { @IBOutlet weak var messageBubble: UIView! @IBOutlet weak var label: UILabel! @IBOutlet weak var rightImageView: UIImageView! @IBOutlet weak var leftImageView: UIImageView! override func awakeFromNib() { super.awakeFromNib() messageBubble.layer.cornerRadius = messageBubble.frame.size.height / 5 } override func setSelected(_ selected: Bool, animated: Bool) { super.setSelected(selected, animated: animated) } } ここでメッセージを管理するためのモデル、データの変数を管理するためのモデルを作ります。 MessageCell.swift import Foundation struct Message { let sender:String let body:String } Constants.swift struct K { static let appName = "MessageChat" static let cellIdentifier = "ReusableCell" static let cellNibName = "MessageCell" static let registerSegue = "RegisterToChat" static let loginSegue = "LoginToChat" struct FStore { static let collectionName = "messages" static let senderField = "sender" static let bodyField = "body" static let dateField = "date" } } 最後にChatViewControllerを作成します。 ChatViewController import UIKit import Firebase class ChatViewController: UIViewController { @IBOutlet weak var tableView: UITableView! @IBOutlet weak var messageTextField: UITextField! let db = Firestore.firestore() var messages:[Message] = [ ] override func viewDidLoad() { super.viewDidLoad() tableView.delegate = self tableView.dataSource = self title = K.appName navigationItem.hidesBackButton = true tableView.register(UINib(nibName: K.cellNibName, bundle: nil), forCellReuseIdentifier: K.cellIdentifier) loadMessages() } func loadMessages(){ db.collection(K.FStore.collectionName) .order(by: K.FStore.dateField) .addSnapshotListener { querySnapshot, error in self.messages = [] if let e = error{ print("The was an issue retrieving data from Firestore.\(e)") }else{ if let snapshotDocuments = querySnapshot?.documents{ for doc in snapshotDocuments { let data = doc.data() if let messageSender = data[K.FStore.senderField] as? String,let messageBody = data[K.FStore.bodyField] as? String{ let newMessage = Message(sender: messageSender, body: messageBody) self.messages.append(newMessage) DispatchQueue.main.async { self.tableView.reloadData() let indexPath = IndexPath(row: self.messages.count-1, section: 0) self.tableView.scrollToRow(at: indexPath, at: .top, animated: true) } } } } } } } @IBAction func sendPressed(_ sender: UIButton) { if let messageBody = messageTextField.text ,let messageSender = Auth.auth().currentUser?.email{ db.collection(K.FStore.collectionName).addDocument(data: [ K.FStore.senderField:messageSender, K.FStore.bodyField:messageBody, K.FStore.dateField:Date().timeIntervalSince1970 ]) { (error) in if error != nil{ print(error) }else{ print("Success.") DispatchQueue.main.async { self.messageTextField.text = "" } } } } } @IBAction func logOutPressed(_ sender: UIBarButtonItem) { //ログアウト機能 do { try Auth.auth().signOut() navigationController?.popToRootViewController(animated: true) } catch let signOutError as NSError { print("Error signing out: %@", signOutError) } } } //MARK: - UITableViewDataSource extension ChatViewController:UITableViewDataSource{ func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return messages.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let message = messages[indexPath.row] let cell = tableView.dequeueReusableCell(withIdentifier: K.cellIdentifier, for: indexPath) as! MessageCell cell.label.text = message.body //This is a message from the current user. if message.sender == Auth.auth().currentUser?.email { cell.leftImageView.isHidden = true cell.rightImageView.isHidden = false cell.messageBubble.backgroundColor = UIColor(named: K.BrandColors.lightPurple) cell.label.textColor = UIColor(named: K.BrandColors.purple) } // This is a message frm the another sender else{ cell.leftImageView.isHidden = false cell.rightImageView.isHidden = true cell.messageBubble.backgroundColor = UIColor(named: K.BrandColors.purple) cell.label.textColor = UIColor(named: K.BrandColors.lightPurple) } return cell } } //MARK: - UITableViewDelegate extension ChatViewController: UITableViewDelegate { //receive index path func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { print(indexPath.row) } } 以上で完成しました。 コードはこちらに載せておきます。 指摘がありましたら、コメントでもよろしくお願いします。
- 投稿日:2021-09-01T17:56:27+09:00
UITableView HeaderViewを作成 (.tableHeaderView) 復習
今回の内容 コードと簡単解説 import UIKit class ViewController: UIViewController { @IBOutlet weak var tableView: UITableView! let sectionTitleArray = ["Colors","sports","Fruits","GrandTours"] //セクションのタイトルに表示 let cellContentsArray = [ ["red","blue","green","white","yellow"], ["Athletics","soccer","Cycling","basketball"], ["Orange","Pineapple","KiwiFruit"], ["Giro d'Italia","Tour de France","Buerta Espana"] ] //cellに表示する内容 override func viewDidLoad() { super.viewDidLoad() tableView.dataSource = self //tableHeaderView HeaderViewを設定 tableView.tableHeaderView = {() -> UIView in let headerView = UIView(frame: CGRect(x: view.frame.minX, y: view.frame.minY, width: view.frame.size.width, height: view.frame.size.height / 4)) headerView.backgroundColor = .systemIndigo headerView.layer.cornerRadius = 20.0 return headerView }() } } extension ViewController:UITableViewDataSource{ //セクション数を設定 func numberOfSections(in tableView: UITableView) -> Int { return 4 } //セクションの高さを設定 func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { return 25 } //セクションのタイトルを設定 func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { return sectionTitleArray[section] } //表示するセルの個数を設定 func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return cellContentsArray[section].count } //セルに表示する内容を設定 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) cell.textLabel?.text = cellContentsArray[indexPath.section][indexPath.row] return cell } } 終わり ご指摘、ご質問などありましたら、コメントまでお願い致します。
- 投稿日:2021-09-01T17:16:21+09:00
SwiftでBytecoinAPIを使ってみた
はじめに SwiftでBytecoinAPIを使ってビットコインアプリを作ってみたいと思います。 初心者にもわかりやすく、AutoLayoutの設定、デザインパターン、コードの可読性もしっかり守っているので、APIの入門記事としてはぴったりかなと。 まず完成形はこちら! では始めていきます。ぜひ最後までご覧ください。 UIの設計 このように配置していきます。 制約をつけていきます。 WeatherViewControllerを作り、IBOutlet,IBAction接続します。 ViewController.swift class ViewController: UIViewController{ @IBOutlet weak var bitcoinLabel: UILabel! @IBOutlet weak var currencyLabel: UILabel! @IBOutlet weak var currencyPicker: UIPickerView! } 全体設計 UIができた後に、今回のアプリの設計を行なっていく。 APIの取得 まず、APIの取得からやっていきたいと思います。 CoinAPI.ioを使います。 操作は以下。 メールに自分のAPIKeyが送られます。 そしてこのようにAPIを叩くと、JSONデータを変換してくれます。 これらのデータをうまく使い今回はアプリを作成していきます。 CoinManager 今回のAPIにおいてのロジックを管理するCoinrManagerを書いていきます。 CoinManager.swift import Foundation protocol CoinManagerDelegate { func didUpdateCoin(_ coinManager:CoinManager,coin:CoinModel) func didFailWithError(error:Error) } struct CoinManager { let baseURL = "https://rest.coinapi.io/v1/exchangerate/BTC" //APIKeyを入れる let apiKey = "[APIKey]" let currencyArray = ["AUD", "BRL","CAD","CNY","EUR","GBP","HKD","IDR","ILS","INR","JPY","MXN","NOK","NZD","PLN","RON","RUB","SEK","SGD","USD","ZAR"] var delegate:CoinManagerDelegate? func getCoinPrice(for currency:String){ //API取得 let urlString = "\(baseURL)/\(currency)?apikey=\(apiKey)" // print(urlString) self.performRequest(with: urlString) } func performRequest(with urlString: String){ //①URLを作る if let url = URL(string: urlString){ //②URLSessionを作る let session = URLSession(configuration: .default) //③SessionTaskを与える let task = session.dataTask(with: url) { data, response, error in if error != nil { // print(error!) self.delegate?.didFailWithError(error: error!) return } if let safeData = data { //途中でプリントで値を確認しながら進めると良い //let dataString = String(data:safeData,encoding:.utf8) // print(dataString) if let coin = self.parseJSON(safeData){ self.delegate?.didUpdateCoin(self, coin: coin) } } } //④タスクを始める task.resume() } } func parseJSON(_ coinData:Data) -> CoinModel?{ let decoder = JSONDecoder() do { let decodedData = try decoder.decode(CoinData.self, from: coinData) let rate = decodedData.rate let currency = decodedData.asset_id_quote print(rate) let coin = CoinModel(rate: rate, currencyName:currency) return coin } catch { delegate?.didFailWithError(error: error) return nil } } } CoinModel データをアプリが使いやすいような形に変換するためのCoinModelを作成していきます。 CoinModel.swift import Foundation struct CoinModel { let rate:Double let currencyName:String var rateString:String{ return String(format: "%.2f", rate) } } CoinData レスポンスしたデータをデコードするためCoinDataを作ります。 CoinData.swift import Foundation struct CoinData:Codable { let rate:Double let asset_id_quote:String } ViewController 最後に取得したデータをViewに反映させる、またPickerViewの操作のためにViewControllerを作っていきます。 ViewController import UIKit class ViewController: UIViewController{ @IBOutlet weak var bitcoinLabel: UILabel! @IBOutlet weak var currencyLabel: UILabel! @IBOutlet weak var currencyPicker: UIPickerView! var coinManager = CoinManager() override func viewDidLoad() { super.viewDidLoad() currencyPicker.dataSource = self currencyPicker.delegate = self coinManager.delegate = self } } //MARK: - UIPickerViewDelegate,UIPickerViewDataSource extension ViewController:UIPickerViewDelegate,UIPickerViewDataSource{ func numberOfComponents(in pickerView: UIPickerView) -> Int { return 1 } func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { return coinManager.currencyArray.count } func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { return coinManager.currencyArray[row] } func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { let selectedCurrency = coinManager.currencyArray[row] coinManager.getCoinPrice(for: selectedCurrency) } } //MARK: - CoinManagerDelegate extension ViewController:CoinManagerDelegate{ func didUpdateCoin(_ coinManager: CoinManager, coin: CoinModel) { DispatchQueue.main.async { self.bitcoinLabel.text = coin.rateString self.currencyLabel.text = coin.currencyName } } func didFailWithError(error: Error) { print(error) } } UIPickerViewの処理はUIPickerViewのドキュメントを確認しながら学んでください。 終わりに 以上でこのようなアプリが作成できました。 指摘点がありましたら、コメントでもよろしくお願いします。
- 投稿日:2021-09-01T17:04:23+09:00
[SwiftUI]Colorの整理とダークモード対応(超簡単)
投稿の経緯 SwiftUI × 個人開発でアプリ内で使う配色を管理する場合に使った方法を投稿します。 環境 Swift version 5.4.2 Xcode version 12.5.1 Colors.xcassetsの作成 File > New > File...でAsset Catalogを選択して今回はColorsという名前でファイルを新規作成します。 ファイルを作成したら下記画像のようにNew Color Setで色を追加します。 左側がライトモード対応で右側がダークモード対応です。 ここで設定した色が端末のライト or ダークで自動で切り替えられます。超便利ですね。 次はColorsファイルで作成した色をコードで使えるように設定します。 ColorManagerの作成 Colorsで作成した色をコードで扱うために、ファイルを新規作成し、構造体ColorManagerを定義します。 ColorManagerでは以下のように定義します。 struct ColorManager { static let 定数名 = Color("Colorsで設定したColor名") } 私の場合は以下のように設定しています。参考に。。 struct ColorManager { static let baseColor = Color("base_color") static let mainColor = Color("main_color") } 後は、コードで呼び出すだけです。 反映方法 反映方法は以下の通り。 ColorManager.baseColor .foregroundColor(ColorManager.baseColor) .background(ColorManager.baseColor) UIColorに設定する場合は以下の通り。 UIColor(ColorManager.baseColor) ポイントはColorとして扱われる点です。参考にしてください。 お知らせ 現在、iOS開発案件を業務委託で募集中です(副業)。TwitterDMでご依頼をお待ちしています
- 投稿日:2021-09-01T15:19:51+09:00
SwiftでOpenWeatherAPIを使ってみる
はじめに SwiftでOpenWeatherAPIを使って天気予報アプリを作ってみたいと思います。 初心者にもわかりやすく、AutoLayoutの設定、デザインパターン、コードの可読性もしっかり守っているので、APIの入門記事としてはぴったりかなと。 まず完成形はこちら! では始めていきます。ぜひ最後までご覧ください。 UIの設計 まずこのアプリでは以下の画像を使ったのでここからダウンロードしてください。 このように配置していきます。 制約をつけていきます。 WeatherViewControllerを作り、IBOutlet,IBAction接続します。 WeatherViewController.swift class WeatherViewController: UIViewController{ @IBOutlet weak var conditionImageView: UIImageView! @IBOutlet weak var temparatureLabel: UILabel! @IBOutlet weak var cityLabel: UILabel! @IBOutlet weak var searchTextField: UITextField! override func viewDidLoad() { super.viewDidLoad() } @IBAction func searchPressed(_ sender: UIButton) { } } 全体設計 UIができた後に、今回のアプリの設計を行なっていく。 APIの取得 まず、APIの取得からやっていきたいと思います。 OpenWeatherAPIを使います。 こちらでサインインして、APIKeyを取得します。 今回はCurrentWeatherDataを使います。 こちらでこのようにAPIを叩くと、JSONデータを変換してくれます。 これらのデータをうまく使い今回はアプリを作成していきます。 WeatherManager 今回のAPIにおいてのロジックを管理するWeatherManagerを書いていきます。 WeatherViewController.swift import Foundation //UI更新のためのプロトコル protocol WeatherManagerDelegate { func didUpdateWeather(_ weatherManager: WeatherManager, weather: WeatherModel) func didFailWithError(error: Error) } struct WeatherManager { //[APIKey]には自分のAPIKeyをかく let weatherURL = "https://api.openweathermap.org/data/2.5/weather?appid=[APIKey]&units=metric" var delegate: WeatherManagerDelegate? func fetchWeather(cityName: String) { let urlString = "\(weatherURL)&q=\(cityName)" performRequest(with: urlString) } func performRequest(with urlString: String) { func performRequest(with urlString:String){ //①URL型に変換 if let url = URL(string: urlString){ //②URLSessionを作る let session = URLSession(configuration: .default) //③Session taskを与える let task = session.dataTask(with: url) { data, response, error in if error != nil { self.delegate?.didFailWithError(error: error!) return } if let safeData = data { if let weather = self.parseJSON(safeData){ self.delegate?.didUpdateWeather(self, weather: weather) } } } //④タスクが始まる task.resume() } } func parseJSON(_ weatherData: Data) -> WeatherModel? { let decoder = JSONDecoder() do { //JSONを変換 let decodedData = try decoder.decode(WeatherData.self, from: weatherData) let id = decodedData.weather[0].id let temp = decodedData.main.temp let name = decodedData.name //データをアプリで使いやすいようにまとめる let weather = WeatherModel(conditionId: id, cityName: name, temperature: temp) return weather } catch { delegate?.didFailWithError(error: error) return nil } } } WeatherModel データをアプリが使いやすいような形に変換するためのWeatherModelを作成していきます。 WeatherModel.swift import Foundation struct WeatherModel { let conditionId:Int let cityName:String let temperature:Double var temperatureString:String { return String(format: "%.1f",temperature) } var conditinName:String{ switch conditionId { case 200...232: return "cloud.bolt" case 300...321: return "cloud.drizzle" case 500...531: return "cloud-rain" case 600...622: return "cloud-snow" case 701...781: return "cloud-fog" case 800: return "sun.max" case 801...804: return "cloud.bolt" default: return "cloud" } } } WeatherData レスポンスしたデータをデコードするためWeatherDataを作ります。 WeatherData.swift import Foundation //Codable = Decodable & Encodable struct WeatherData : Codable{ let name:String let main:Main let weather:[Weather] } struct Main:Codable { let temp:Double } struct Weather:Codable { let description:String let id:Int } WeatherViewController 最後に取得したデータをViewに反映させる、またTableViewの操作のためにWeatherViewControllerを作っていきます。 WeatherViewController import UIKit class WeatherViewController: UIViewController { @IBOutlet weak var conditionImageView: UIImageView! @IBOutlet weak var temperatureLabel: UILabel! @IBOutlet weak var cityLabel: UILabel! @IBOutlet weak var searchTextField: UITextField! var weatherManager = WeatherManager() override func viewDidLoad() { super.viewDidLoad() weatherManager.delegate = self searchTextField.delegate = self } } //MARK: - UITextFieldDelegate extension WeatherViewController: UITextFieldDelegate { @IBAction func searchPressed(_ sender: UIButton) { searchTextField.endEditing(true) } func textFieldShouldReturn(_ textField: UITextField) -> Bool { searchTextField.endEditing(true) return true } func textFieldShouldEndEditing(_ textField: UITextField) -> Bool { if textField.text != "" { return true } else { textField.placeholder = "何か入力してください" return false } } func textFieldDidEndEditing(_ textField: UITextField) { if let city = searchTextField.text { weatherManager.fetchWeather(cityName: city) } searchTextField.text = "" } } //MARK: - WeatherManagerDelegate extension WeatherViewController: WeatherManagerDelegate { func didUpdateWeather(_ weatherManager: WeatherManager, weather: WeatherModel) { DispatchQueue.main.async { //ここでUIの更新 self.temperatureLabel.text = weather.temperatureString self.conditionImageView.image = UIImage(systemName: weather.conditionName) self.cityLabel.text = weather.cityName } } func didFailWithError(error: Error) { print(error) } } UITextFieldの処理はUITextFieldのドキュメントを確認しながら学んでください。 終わりに 以上でこのようなアプリが作成できました。
- 投稿日:2021-09-01T10:09:51+09:00
classの配列を作る
標準のinit(repeating:count:) Array(repeating: HogeClass(), count: 3) これでできる配列の要素は全て同じ参照になります。 Arrayの拡張 extension Array { init(repeatingWith repeatedClosure: () -> Array.Element, count: Int) { self.init() for _ in 0 ..< count { self.append(repeatedClosure()) } } } Array(repeatingWith: { HogeClass() }, count: 3) こうすると配列の要素を全て別の参照にすることができます。
- 投稿日:2021-09-01T00:38:34+09:00
NeumorphismなTabBarを実装する
外部ライブラリを使用する 使用したライブラリ: NeumorphismTab CocoaPodsでインストール pod 'NeumorphismTab' コード ライブラリをimport import NeumorphismTab NeumorphismTabBarControllerを継承したMainTabBarControllerクラスを作る class MainTabBarController: NeumorphismTabBarController override func setupView()メソッドに下記を記述 colorを設定 view.backgroundColor = #colorLiteral(red: 0.9725490196, green: 0.9725490196, blue: 0.9725490196, alpha: 1) TabBarItemを生成 ここではiconにSFSymbolsの画像を入れている titleを空にするとテキストが表示されないボタンになる let first = NeumorphismTabBarItem(icon: UIImage(systemName: "newspaper")!, title: "") let second = NeumorphismTabBarItem(icon: UIImage(systemName: "person.crop.circle")!, title: "") let third = NeumorphismTabBarItem(icon: UIImage(systemName: "info.circle")!, title: "") 別ファイルで各タブの中身となるViewControllerを作りインスタンス化する let firstViewController = FirstViewController() let secondViewController = SecondViewController() let thirdViewController = ThirdViewController() tabBarItemとViewControllerをセットする setTabBar(items: [first, second, third]) setViewControllers([firstViewController, secondViewController, thirdViewController], animated: false) 完成形 全体のコード import UIKit import NeumorphismTab class MainTabBarController: NeumorphismTabBarController { override func setupView() { let first = NeumorphismTabBarItem(icon: UIImage(systemName: "newspaper")!, title: "") let second = NeumorphismTabBarItem(icon: UIImage(systemName: "person.crop.circle")!, title: "") let third = NeumorphismTabBarItem(icon: UIImage(systemName: "info.circle")!, title: "") view.backgroundColor = #colorLiteral(red: 0.9725490196, green: 0.9725490196, blue: 0.9725490196, alpha: 1) let firstViewController = FirstViewController() let secondViewController = SecondViewController() let thirdViewController = ThirdViewController() setTabBar(items: [first, second, third]) setViewControllers([firstViewController, secondViewController, thirdViewController], animated: false) } }