Swift TagListViewでタグ入力UI実装
はじめに
今回作りたかったのはこんなありがちなUI
https://github.com/ElaWorkshop/TagListView
このライブラリを使ったら簡単に実装できた。使い方簡単にメモしておく
ハマりポイントはあまりなかったが、タグを追加していったときに高さの取得方法がわからなかったがソース見てたらそれっぽい値が定義されてた。
tagListView.intrinsicContentSize
実装
import UIKit import TagListView class ViewController: UIViewController, TagListViewDelegate, UITextFieldDelegate { let MARGIN: CGFloat = 10 let tagListView = TagListView() let textField = UITextField() override func viewDidLoad() { super.viewDidLoad() self.setView() } func setView() { view.addSubview(tagListView) view.addSubview(textField) tagListView.frame = CGRect(x: MARGIN, y: 50, width: view.frame.width-MARGIN*2, height: 0) // タグの削除ボタンを有効に tagListView.enableRemoveButton = true // 今回は削除ボタン押された時の処理を行う tagListView.delegate = self // タグの見た目を設定 tagListView.alignment = .left tagListView.cornerRadius = 3 tagListView.textColor = UIColor.black tagListView.borderColor = UIColor.lightGray tagListView.borderWidth = 1 tagListView.paddingX = 10 tagListView.paddingY = 5 tagListView.textFont = UIFont.systemFont(ofSize: 16) tagListView.tagBackgroundColor = UIColor.white // タグ削除ボタンの見た目を設定 tagListView.removeButtonIconSize = 10 tagListView.removeIconLineColor = UIColor.black // テキストフィールドは適当にセット textField.delegate = self textField.placeholder = "タグを入力してください" textField.returnKeyType = UIReturnKeyType.done // レイアウト調整 updateLayout() } // テキストフィールドの完了ボタンが押されたら func textFieldShouldReturn(_ textField: UITextField) -> Bool { if 0 < textField.text!.count { // タグを追加 tagListView.addTag(textField.text!) // テキストフィールドをクリアしてレイアウト調整 textField.text = nil updateLayout() } return true } // タグ削除ボタンが押された func tagRemoveButtonPressed(_ title: String, tagView: TagView, sender: TagListView) { // リストからタグ削除 sender.removeTagView(tagView) updateLayout() } func updateLayout() { // タグ全体の高さを取得 tagListView.frame.size = tagListView.intrinsicContentSize textField.frame = CGRect(x: MARGIN, y: tagListView.frame.origin.y + tagListView.frame.height + 5, width: view.frame.width-MARGIN*2, height: 40) } }
以上です
Swift Carthage 使い方メモ
carthage自体のインストールは済んでる前提で、その後のライブラリインストール手順についてメモしておく。
手順はざっくり以下
1. CartfileにレポジトリURL追加
2. carthage update コマンドでライブラリインストール
3. xcodeからライブラリ追加
4. Run Scriptにパスを追加してビルド
1. Cartfileにレポジトリ追加
プロジェクト直下のCartfileに追記するだけ。だいたい以下のような感じ
github "Alamofire/Alamofire"
2. carthage update コマンドでライブラリインストール
自分の場合、iPhoneアプリしかやったときないので毎回以下を実行
$ carthage update --platform iOS
Swift codableなclass(object)のプロパティに変数で動的にアクセスする
あまり応用シーンはなさそうだけど今回やりたかったことは
user["id"]、user["name"]みたいなかんじでプロパティにアクセスしたかった。
やりたかった理由はObjectから特定のプロパティを表示する画面を作りたかった。
こんなクラスがあってプロパティがたくさんあるイメージ
class User { var id: Int var name: String ・・・ }
テーブルビューに表示するのだが、各行で if や switch でハードコードするのも何となく無駄な気がして別の方法を検索。
たまたま、今回はAPIから取得した値をCodableなClassとして扱っていてStackOverflowにいい方法がのってた。
・StackOverflow
https://stackoverflow.com/questions/45209743/how-can-i-use-swift-s-codable-to-encode-into-a-dictionary
こんな感じのextensionを作る
extension Encodable { subscript(key: String) -> Any? { return dictionary[key] } var dictionary: [String: Any] { return (try? JSONSerialization.jsonObject(with: JSONEncoder().encode(self))) as? [String: Any] ?? [:] } }
利用方法
// Object var user = User(id: 1, name: "user1", ・・・) // 表示する順番を定義 var fields = [ "id", "name", ・・・ ] // テーブル表示 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { // user[fields[indexPath.row]] でプロパティにアクセスできる }
プロパティの値を更新したい場合はこれだと上手くいかないけど今回は表示するだけの目的だったので事足りた。以上です。
Swift LINEぽいメッセージアプリの入力UIをつくったのでメモ
はじめに
かなり前にこちらでメッセージを表示する部分の吹き出しの作り方をメモした。
http://kimagureneet.hatenablog.com/entry/2015/09/19/005407
今回はメッセージ入力部分のUIの作り込みの方法をメモ。こんな感じのものを作りたかった。
やりたいことは
・入力フォームを画面下に固定
・フォーカスを当てたらキーボードの上に入力フォームを移動
・複数行にも対応したいのでUITextFieldでなくUITextViewを使用
上記を満たすためにやらなくてはならないこと(上と重複するけど)
・キーボードを開いたとき
入力フォームの位置をキーボードの上に移動
UITextViewの高さを複数行見えるように変更
・キーボードを閉じたとき
入力フォームの位置を画面下に移動
UITextViewの高さを1行見えるように変更
ざっくりこんな感じ。あとはキーボードの上に完了ボタンを追加したり細々とした内容。
実装
import UIKit class ViewController: UIViewController, UITextViewDelegate { let MARGIN_MSG: CGFloat = 1 let HEIGHT_BOX: CGFloat = 50 let WIDTH_SUBMIT: CGFloat = 70 let MIN_HEIGHT_MSG: CGFloat = 50 let MAX_HEIGHT_MSG: CGFloat = 100 // メッセージを表示する領域 var tableView: UITableView! // メッセージBOX(入力フォームと送信ボタン) var box: UIView! var msg: UITextView! var submit: UIButton! // テーブルビューのサイズ情報 var sizeTable: CGSize! // メッセージBOXのフレーム情報(キーボードが閉じられた時の情報) var frBox: CGRect! // キーボードのフレーム情報 var frKeyboard: CGRect! override func viewDidLoad() { super.viewDidLoad() setView() } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) let notificationCenter = NotificationCenter.default notificationCenter.addObserver(self, selector: #selector(ViewController.handleKeyboardWillShowNotification(_:)), name: .UIKeyboardWillShow, object: nil) notificationCenter.addObserver(self, selector: #selector(ViewController.handleKeyboardWillHideNotification(_:)), name: .UIKeyboardWillHide, object: nil) } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillShow, object: self.view.window) NotificationCenter.default.removeObserver(self, name: .UIKeyboardDidHide, object: self.view.window) } // キーボードが表示されるときに行う処理 @objc func handleKeyboardWillShowNotification(_ notification: Notification) { let userInfo = notification.userInfo! frKeyboard = (userInfo[UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue drawBox() } // キーボードが非表示になるときに行う処理 @objc func handleKeyboardWillHideNotification(_ notification: Notification) { box.frame = frBox tableView.frame.size = sizeTable } // メッセージBOX描画処理 func drawBox() { let statusBarHeight = UIApplication.shared.statusBarFrame.height let bound: CGSize = UIScreen.main.bounds.size var height = msg.sizeThatFits(CGSize(width: msg.frame.size.width, height: CGFloat.greatestFiniteMagnitude)).height if height < MIN_HEIGHT_MSG { height = MIN_HEIGHT_MSG } else if MAX_HEIGHT_MSG < height { height = MAX_HEIGHT_MSG } // 入力フォームの位置調整 msg.frame = CGRect(x: msg.frame.origin.x, y: msg.frame.origin.y, width: msg.frame.width, height: height) box.frame = CGRect(x: box.frame.origin.x, y: bound.height - frKeyboard.size.height - box.frame.height, width: msg.frame.width, height: height) // テーブルビューの高さ調整 tableView.frame.size = CGSize(width: tableView.frame.width, height: bound.height - (statusBarHeight + frKeyboard.height + height)) } func textViewDidChange(_ textView: UITextView) { drawBox() } func setView() { let statusBarHeight = UIApplication.shared.statusBarFrame.height let widthMax = view.frame.width let heightMax = view.frame.height // メッセージを表示する領域 tableView = UITableView(frame: CGRect(x:0, y:statusBarHeight, width:widthMax, height:heightMax - (statusBarHeight + HEIGHT_BOX))) tableView.separatorStyle = .none view.addSubview(tableView) // 入力フォームと送信ボタンを表示する領域 box = UIView(frame: CGRect(x: 0, y: tableView.frame.origin.y + tableView.frame.height, width: widthMax, height: HEIGHT_BOX)) view.addSubview(box) // 送信ボタン submit = UIButton(frame: CGRect(x: widthMax-(WIDTH_SUBMIT+MARGIN_MSG), y: MARGIN_MSG, width: WIDTH_SUBMIT, height: HEIGHT_BOX-MARGIN_MSG)) submit.setTitle("送信", for: UIControlState.normal) submit.setTitleColor(UIColor.white, for: UIControlState.normal) submit.backgroundColor = UIColor.black submit.layer.cornerRadius = 2.0 submit.layer.borderColor = UIColor.lightGray.cgColor submit.layer.borderWidth = 3 box.addSubview(submit) // 入力フォーム msg = UITextView(frame: CGRect(x: MARGIN_MSG, y: MARGIN_MSG, width: widthMax-(MARGIN_MSG*3+WIDTH_SUBMIT), height: HEIGHT_BOX-MARGIN_MSG)) msg.font = UIFont.systemFont(ofSize: 18) msg.layer.borderColor = UIColor.lightGray.cgColor msg.layer.borderWidth = 3 msg.delegate = self box.addSubview(msg) // キーボード完了ボタン let keyboard = UIView(frame: CGRect(x: 0, y: 0, width: widthMax, height: 40)) keyboard.backgroundColor = UIColor.lightGray let button = UIButton(frame: CGRect(x: widthMax - 50, y: 5, width: 50, height: 30)) button.setTitle("完了", for: .normal) button.layer.cornerRadius = 2.0 keyboard.addSubview(button) msg.inputAccessoryView = keyboard button.addTarget(self, action: #selector(onClose(sender:)), for: .touchUpInside) // メッセージBOXの位置を保持しておく frBox = box.frame // テーブルビューのサイズを保持しておく sizeTable = tableView.frame.size } // 完了ボタンでキーボードを閉じる @objc func onClose(sender: UIBarButtonItem){ msg.endEditing(true) } }
ずらずら書いたけど以上です。ちなみにSwift4で確認した。
Swift ImageSlideshowのPagerの色、位置を変更する
https://github.com/zvonicek/ImageSlideshow
こちらのライブラリの話
デフォルトだと画像とページャーガ重なっていて見づらいので、色を変えて画像の下にくるようにする
var slideShow = ImageSlideshow() let pager = UIPageControl() pager.pageIndicatorTintColor = UIColor.lightGray pager.currentPageIndicatorTintColor = UIColor.black slideShow.pageIndicator = pager slideShow.pageIndicatorPosition = PageIndicatorPosition(horizontal: .center, vertical: .under) let imageSources = [ImageSource(imageString: "img1")!, ImageSource(imageString: "img2")!, ImageSource(imageString: "img3")!, ImageSource(imageString: "img4")!] slideShow.setImageInputs(imageSources)
デフォルトこっちの方がいい気が。。以上です
Swift UITableViewですぐ忘れる細かいことまとめておく
セルの罫線を消す
tableView.separatorStyle = .none
セルの罫線の隙間をなくす
UITableView.appearance().separatorInset = UIEdgeInsets.zero
空のセルは表示しない
tableView.tableFooterView = UIView(frame: .zero)
セルをタップした時の色がかわらないように
cell.selectionStyle = .none
随時追加する予定。以上です
ionic async awaitでAPIへ同期的にリクエスト
やりたいことはタイトルのとおり。初めてでハマったのでコードをメモしておく。
import { Component } from '@angular/core'; import { Http, Headers,RequestOptions } from '@angular/http' import 'rxjs/add/operator/map' import 'rxjs/add/operator/catch' @Component({ selector: 'page-home', templateUrl: 'home.html' }) export class HomePage { constructor( public http: Http ) { } ionViewDidEnter() { this.requestApiAsync() } async requestApiAsync() { for (var i = 1; i <= 3; i++) { console.log(i + '回目') let result = await this.requestApi() console.log(result) } } requestApi() { return new Promise((resolve, reject) => { let url = 'http://example.com/path/to' let body = 'key=value' let headers = new Headers({ "Content-Type": "application/x-www-form-urlencoded" }) let options = new RequestOptions({ headers: headers }) this.http.post(url, body, options) .map(response => response).catch(error => error) .subscribe( result => resolve(result.json()), error => reject(error) ) }) } }
以上です