月別アーカイブ: 2020年4月

【Swift5】ディクショナリ等に入れるDate型の初期値の設定が面倒。

タイトルのままですが、[String:Any]等で、AnyにはString型Bool型Int型等ダイレクトに入力できる物が多いのですが、Date型も入れれないのかと考えてみました。
結局よくわからず、String型で日時を作ってDateFormatterを設定してDate型に変換してから出ないと無理っぽい感じでした。

なのでやったことの忘備録です。

import UIKit

let dateNow = Date() //現在の時刻
var dateDic: [String:Any] = [:] //ディクショナリ型

//①ディクショナリ型に直接date型を入れたいが無理っぽい。
dateDic["dateNow"] = dateNow
dateDic["dateString"] = "2020-04-20 11:15:17 +0000"

print(dateDic["dateNow"] as! Date)
//print(dateDic["dateString"] as! Date )  //String型なので当然エラーになる


//②なのでDate型を作るためにDateFormatterをセット
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_US_POSIX")//USなので時差に注意。
dateFormatter.dateFormat = "yyyy'-'MM'-'dd' 'HH':'mm':'ss'"
dateFormatter.timeZone = TimeZone(secondsFromGMT: 0)

//文字列をDateFormatterでDate型にして辞書に収納
let string = "2020-04-20 11:15:17"
let dateString = dateFormatter.date(from: string)
dateDic["dateString"] = dateString

print(dateDic["dateString"] as! Date) //Date型として出せる。

//③おまけDate型を日本用に出力。
let f = DateFormatter()
f.timeStyle = .full
f.dateStyle = .full
f.locale = Locale(identifier: "ja_JP")//日本なので時差に注意。

print(f.string(from: dateDic["dateString"] as! Date))

こんなんでます。

2020-04-20 23:38:46 +0000
2020-04-20 11:15:17 +0000
2020年4月20日 月曜日 20時15分17秒 日本標準時

ちょいとめんどいですね。

【Swift5】アプリがバックグラウンドに入った時の処理。

なんか以前はappDelegateを使ってたよーな気がしたんだけど調べ直してみた。

import UIKit

class ViewController: UIViewController, UIApplicationDelegate{

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
     
        NotificationCenter.default.addObserver(self,
                                               selector: #selector(ViewController.background),//通知来た時の処理先
                                               name: UIApplication.willResignActiveNotification,
                                               object: nil)
    }
    
    @objc func background() {
        print("ばっくぐらうんど通知来た")
    }
}

抜粋なんで動かんかったらすまん。
こんなんで動くと思う。/(^o^)\

【Swift5】pickerViewを2つ(以上)とdatePickerを設置して下から出るようにする。

定期的にpickerViewのこんな記事を書いている気がします。(^_^;)
pickerViewはよく使うけどアプリをたまにしか作らないのでその間にSwiftのバージョンも上がるし作り方も忘れるよね。

同じViewContorollerに2つ以上のPickerViewを設置し、datePickerも使って、しかも普通のキーボードのように下から出るようにしたい。
それに少し前の記事を参考に閉じるボタンも付けます。それは下記参照。
【Swift5】textFieldのキーボードに閉じるボタンを付ける。 | iPhoneアプリ備忘録

ではまず、3つのtextfieldを作成します。
autoLayoutは適当に。
スクリーンショット 2020-04-11 11.06.09

textfieldはそれぞれあうとれっとせつぞくします。
pickerViewのプロパティを2つと、datePickerのプロパティを作り、pickerViewに表示する配列も作成します。

UIPickerViewDelegate, UIPickerViewDataSourceを設定するとプロトコルに従いメソッドが作成されます。
UITextFieldDelegateも書いておきましょう。
とりあえずここまでのコード。

import UIKit

class ViewController: UIViewController,UITextFieldDelegate,UIPickerViewDelegate, UIPickerViewDataSource {

    //textFieldとのアウトレット接続
    @IBOutlet weak var kanaTextField: UITextField!
    @IBOutlet weak var numberTextField: UITextField!
    @IBOutlet weak var dateTextField: UITextField!

    //pickerViewのプロパティを作る
    var kanaPickerView : UIPickerView = UIPickerView()
    var numberPickerView  : UIPickerView = UIPickerView()
    //datePickerのプロパティを作る
    var datePickerView  :UIDatePicker = UIDatePicker()
    
    //pickerViewに表示する配列を作成
    let list1:[String] = ["いち","に","さん","よん","ご",]
    let list2:[String] = ["1","2","3","4","5","6","7"]
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
    }
    
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        <#code#>
    }
    
    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        <#code#>
    }
}

そして、プロトコルは置いといてキーボードをpickerViewに置き換える辺りの設定します。
基本的にはPickerViewにtagを設定し、textfieldのinputViewに設定します。
datePickerも同様に。
viewDidLoadから呼びます。

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        makePickerKeybord()
    }
    
    //キーボードの設定
    func makePickerKeybord(){
        //かな数字のPickerviewをキーボードにする設定
        kanaPickerView.tag = 1
        kanaPickerView.delegate = self
        kanaTextField.inputView = kanaPickerView
        kanaTextField.delegate = self
        
        //numberのPickerviewをキーボードにする設定
        numberPickerView.tag = 2
        numberPickerView.delegate = self
        kanaTextField.inputView = numberPickerView
        kanaTextField.delegate = self
        
        // 日付ピッカーをキーボードにする設定
        datePickerView.datePickerMode = UIDatePicker.Mode.dateAndTime
        datePickerView.timeZone = TimeZone(identifier: "Asia/Tokyo")
        datePickerView.locale = Locale(identifier: "ja_JP")
        datePickerView.minuteInterval = 15
        datePickerView.addTarget(self, action: Selector(("dateChange")), for: .valueChanged)
        dateTextField.inputView = datePickerView
        dateTextField.delegate = self
    }

pickerViewのプロトコルの設定。
コンポーネントの数は1でコンポーネントに入る配列の数はtagで切り分けます。
コンポーネントの中身、配列もtagで切り分け。

    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 1
    }
    
    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        switch pickerView.tag {
        case 1:
            return list1.count
        case 2:
            return list2.count
        default:
            return 0
        }
    }
    
    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
        print("たぐ",pickerView.tag)
        switch pickerView.tag {
            case 1:
                return list1[row]
            case 2:
                return list2[row]
        default:
            return "error"
        }
    }

pickerViewとdatePickerViewに変更があった時に呼ばれてtextFieldのテキストが書き換えられるメソッド。

     //pickerが選択された際に呼ばれるデリゲートメソッド.
     func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
         switch pickerView.tag {
             case 1:
                 return kanaTextField.text = list1[row]
             case 2:
                 return numberTextField.text = list2[row]
         default:
             return
         }
     }
     
     //datePickerが変化すると呼ばれる
    @objc func dateChange(){
         let formatter = DateFormatter()
         formatter.dateFormat = "yyyy年MM月dd日 HH:mm"
         dateTextField.text = "\(formatter.string(from: datePickerView.date))"
     }
}

いちおこれで完成。
IMG_6322

一応全コード

import UIKit

class ViewController: UIViewController,UITextFieldDelegate,UIPickerViewDelegate, UIPickerViewDataSource {

    //textFieldとのアウトレット接続
    @IBOutlet weak var kanaTextField: UITextField!
    @IBOutlet weak var numberTextField: UITextField!
    @IBOutlet weak var dateTextField: UITextField!

    //pickerViewのプロパティを作る
    var kanaPickerView : UIPickerView = UIPickerView()
    var numberPickerView  : UIPickerView = UIPickerView()
    //datePickerのプロパティを作る
    var datePickerView  :UIDatePicker = UIDatePicker()
    
    //pickerViewに表示する配列を作成
    let list1:[String] = ["いち","に","さん","よん","ご",]
    let list2:[String] = ["1","2","3","4","5","6","7"]
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        makePickerKeybord()
    }
    
    //キーボードの設定
    func makePickerKeybord(){
        //かな数字のPickerviewをキーボードにする設定
        kanaPickerView.tag = 1
        kanaPickerView.delegate = self
        kanaTextField.inputView = kanaPickerView
        kanaTextField.delegate = self
        
        //numberのPickerviewをキーボードにする設定
        numberPickerView.tag = 2
        numberPickerView.delegate = self
        numberTextField.inputView = numberPickerView
        numberTextField.delegate = self
        
        // 日付ピッカーをキーボードにする設定
        datePickerView.datePickerMode = UIDatePicker.Mode.dateAndTime
        datePickerView.timeZone = TimeZone(identifier: "Asia/Tokyo")
        datePickerView.locale = Locale(identifier: "ja_JP")
        datePickerView.minuteInterval = 15
        datePickerView.addTarget(self, action: Selector(("dateChange")), for: .valueChanged)
        dateTextField.inputView = datePickerView
        dateTextField.delegate = self
    }
    
    
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 1
    }
    
    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        switch pickerView.tag {
        case 1:
            return list1.count
        case 2:
            return list2.count
        default:
            return 0
        }
    }
    
    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
        switch pickerView.tag {
            case 1:
                return list1[row]
            case 2:
                return list2[row]
        default:
            return "error"
        }
    }
    
     //pickerが選択された際に呼ばれるデリゲートメソッド.
     func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
         switch pickerView.tag {
             case 1:
                 return kanaTextField.text = list1[row]
             case 2:
                 return numberTextField.text = list2[row]
         default:
             return
         }
     }
     
     //datePickerが変化すると呼ばれる
    @objc func dateChange(){
         let formatter = DateFormatter()
         formatter.dateFormat = "yyyy年MM月dd日 HH:mm"
         dateTextField.text = "\(formatter.string(from: datePickerView.date))"
     }
}

// MARK: - キーボードにと閉じるボタンを付ける
//storybordで該当テキストフィールドを選択し、identity Inspectorでclassを DoneTextFierdに切り替える
class DoneTextFierd: UITextField{

    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        commonInit()
    }

    private func commonInit(){
        let tools = UIToolbar()
        tools.frame = CGRect(x: 0, y: 0, width: frame.width, height: 40)
        let spacer = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: self, action: nil)
        let closeButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(self.closeButtonTapped))
        tools.items = [spacer, closeButton]
        self.inputAccessoryView = tools
    }

    @objc func closeButtonTapped(){
        self.endEditing(true)
        self.resignFirstResponder()
    }
}

【Swift5】scrollViewを使って画面下にあるtextfieldがキーボードで隠れないようにした。

入力フォームなどで縦に長くなった時、下の方のtextFieldがキーボードに隠れて困ることがよくあります。
それを解消しました。
前回の記事のようにscrollViewとstackViewを使っている場合に便利です。

参考サイト
Swift:TextFieldが隠れちゃうのをなんとかする!|プログラミング初心者のプロへの道
UITextFieldを使用する時に必要なあれこれ – Qiita
[Swift4]複数のTextFieldがキーボードに隠れて入力できない時の対策 – あなたにフィット

上記2つのサイトを参考にSwift5で簡単に書いてみます。

まずは前々回の記事を参考にscrollView – stackView – view – textfieldという階層を作ります。
【Swift5】縦長のスクロール画面をscrollViewとstackViewを使って作ると便利だった。改訂版 | iPhoneアプリ備忘録
わかりやすくViewの色を塗り分けておきます。
スクリーンショット 2020-04-10 21.03.20

0vz6x-pfv2f

あと、前回の記事を参考にキーボードを閉じるボタンも付けておきます。
【Swift5】textFieldのキーボードに閉じるボタンを付ける。 | iPhoneアプリ備忘録

このままだとスクロールはしますが一番下の青のtextfieldに入力しようとしても隠れて見えません。
キーボードが出た時にキーボードの高さ分だけスクロールビューを動かします。
以下コード

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var scrollView: UIScrollView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        configureObserver()
    }

    override func viewWillDisappear(_ animated: Bool) {
        removeObserver()
        super.viewWillDisappear(animated)
    }
    
    // MARK: キーボードでスクロール変更
    //キーボードの出現でスクロールビューを変更するのを監視用オブザーバー
    func configureObserver() {
        NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil)
        //ここでUIKeyboardWillShowという名前の通知のイベントをオブザーバー登録をしている
        NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillHide(_:)), name: UIResponder.keyboardDidHideNotification, object: nil)
        //ここでUIKeyboardWillHideという名前の通知のイベントをオブザーバー登録をしている
    }
    
    func removeObserver() {
        NotificationCenter.default.removeObserver(self)
    }

        //UIKeyboardWillShow通知を受けて、実行される関数
    @objc func keyboardWillShow(_ notification: NSNotification){
        guard let userInfo = notification.userInfo else { return }
        let keyboardSize = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue.height
        scrollView.contentInset.bottom = keyboardSize
    }
       
       
       //UIKeyboardWillShow通知を受けて、実行される関数
    @objc func keyboardWillHide(_ notification: NSNotification){
        scrollView.contentInset = .zero
        scrollView.scrollIndicatorInsets = .zero
       }
}

// MARK: - キーボードにと閉じるボタンを付ける
//storybordで該当テキストフィールドを選択し、identity Inspectorでclassを DoneTextFierdに切り替える
class DoneTextFierd: UITextField{

    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        commonInit()
    }

    private func commonInit(){
        let tools = UIToolbar()
        tools.frame = CGRect(x: 0, y: 0, width: frame.width, height: 40)
        let spacer = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: self, action: nil)
        let closeButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(self.closeButtonTapped))
        tools.items = [spacer, closeButton]
        self.inputAccessoryView = tools
    }

    @objc func closeButtonTapped(){
        self.endEditing(true)
        self.resignFirstResponder()
    }
}

こんな感じになります。
2hafl-4l5jh

【Swift5】textFieldのキーボードに閉じるボタンを付ける。

textFieldのキーボードに閉じるボタンを付けたいときがあります。

参考サイト
swiftで閉じるボタンのついたキーボードを表示するUITextFieldクラスの作成 – Qiita

ほぼ劣化コピペです。
元サイトがなくなったら困るので自分用です。
不都合があれは削除します。(-人-)

UITextFieldのカスタムクラスになります。
以下のコードをコードの最後に追記。

// MARK: - キーボードにと閉じるボタンを付ける
//storybordで該当テキストフィールドを選択し、identity Inspectorでclassを DoneTextFierdに切り替える
class DoneTextFierd: UITextField{

    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        commonInit()
    }

    private func commonInit(){
        let tools = UIToolbar()
        tools.frame = CGRect(x: 0, y: 0, width: frame.width, height: 40)
        let spacer = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: self, action: nil)
        let closeButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(self.closeButtonTapped))
        tools.items = [spacer, closeButton]
        self.inputAccessoryView = tools
    }

    @objc func closeButtonTapped(){
        self.endEditing(true)
        self.resignFirstResponder()
    }
}

そして適用したいtextFieldのidentity Inspectorでcustom classをDoneTextFierdに変更する。
スクリーンショット 2020-04-10 21.23.40

これでDone付きのキーボードが出るようになります。
スクリーンショット 2020-04-10 21.28.12

【Swift5】縦長のスクロール画面をscrollViewとstackViewを使って作ると便利だった。改訂版

【Swift4】縦長のスクロール画面をscrollViewとstackViewを使って作ると便利だった。 | iPhoneアプリ備忘録

上記ページのやり方でスクロールビューを作ってるとエラーが出た。
Xcode11でInterface Builderの仕様が変わったらしい。
参考サイト
Xcode11のIBに追加されたContentLayoutGuideとFrameLayoutGuideについて – Qiita

なので改訂版です。
旧ページの画像を使ったりするので整合性がなかったらご指摘ください。/(^o^)\

まず、Safe AreaにScroll Viewを配置します。
Add New Constraintsで上下左右全てのマージンを0とします。
スクリーンショット 2019-01-06 20.41.39

このようなエラーが出ますが続けます。
スクリーンショット 2020-04-03 12.29.31

次いで、そのScroll Viewの中にVertical Stack Viewを配置し、Add New Constraintsで上と左右のマージンを0とします。
スクリーンショット 2020-04-03 13.19.10

ここでXcode11から変更された「Content Layout Guide」を使用します。
現状ではScrollViewの幅と高さが決まっていないためエラーになっています。
なのでStack ViewからContent Layout Guideへ右ドラックで接続しEqual Widthsを選択します。スクリーンショット 2020-04-03 12.30.10

スクリーンショット 2020-04-03 12.31.06

同様にEqual hightsも接続します。
スクリーンショット 2020-04-03 12.31.21

幅は画面幅で設定されるのですが高さが決まらないためエラーが出てますが、Stack Viewに子ビューを入れて高さを決定するとエラーが無くなります。
スクリーンショット 2020-04-03 13.51.43

スクリーンショット 2020-04-03 14.01.41

縦長な画面でテストします。
操作するViewを選んで、simulated sizeを「freeform」にして、heightを設定します。
とりあえず1200にしてみました。
スクリーンショット 2019-01-06 20.51.30

StackViewに高さが200の子Viewを6個入れて分かりやすように色を付けてみました。
スクリーンショット 2019-01-06 21.04.34

これでアプリを起動すると綺麗にスクロールしました。\(^o^)/
zuhqs-9xva4

これだけのことがコードを一行も書かずにできるってすごいなーと思います。\(^o^)/