

先日回答を頂きましたNSCalendar.Unit「dateToStringメソッドはこんな感じ」のコードを実行するとテキストの初期値にある曜日の表示が（GTM+9）となります。「さらに修正するとこんなコード」を実行すると曜日の表示も適正になりました。どちらの場合もシミュレーターは立ち上がりますがデートピッカーを動かして日付を変えたり、完了ボタンや今日のボタンをタップするとエラーになりシミュレーターが終了しました。  
ストリーボードのテキストフィールドを作り直してOutletし直したり、connections
inspectorで削除してつけなおしたりしました。メソッド名なども見直し、自分なりにやってみましたが改善しません。恐縮ですがご教示いただけますか。感謝しています。  
デバッグエリアの表示  
2016-12.... path is ･･･Reading from private effective user settings.  
2016-…-[…App.ViewController tappedToolBarBtn:]: unrecognized selector sent to
instance ...  
*** Terminating app due to uncaught exception 'NSInvalidArgumentException',
reason: '-[…App.ViewController tappedToolBarBtn:]: unrecognized selector sent
to instance ...  
*** First throw call stack:)  
libc++abi.dylib: terminating with uncaught exception of type NSException

[code]

    //  ViewController.swiftのコード
    
    import UIKit
    class ViewController: UIViewController, UIToolbarDelegate {    
    @IBOutlet weak var textField: UITextField!
    //var textField: UITextField!
    var toolBar:UIToolbar!
    var myDatePicker: UIDatePicker!
    
    override func viewDidLoad() {
        super.viewDidLoad()        
        // 入力欄の設定
        //textField = UITextField(frame: CGRectMake(self.view.frame.size.width/3, 100, 0, 0))
        textField.placeholder = dateToString(date: (NSDate() as Date as Date as NSDate) as Date)
        textField.text        = dateToString(date: (NSDate() as Date as Date as NSDate) as Date)
        //textField.sizeToFit()
        self.view.addSubview(textField)
    
        // UIDatePickerの設定
        myDatePicker = UIDatePicker()
        myDatePicker.addTarget(self, action: Selector(("changedDateEvent:")), for: UIControlEvents.valueChanged)
        myDatePicker.datePickerMode = UIDatePickerMode.date
        textField.inputView = myDatePicker
    
        // UIToolBarの設定     
        toolBar = UIToolbar(frame: CGRect(x: 0, y: self.view.frame.size.height/6, width: self.view.frame.size.width, height: 40.0))
    
        toolBar.layer.position = CGPoint(x: self.view.frame.size.width/2, y: self.view.frame.size.height-20.0)
        toolBar.barStyle = .blackTranslucent
        toolBar.tintColor = UIColor.white
        toolBar.backgroundColor = UIColor.black
    
        let toolBarBtn      = UIBarButtonItem(title: "完了", style: .plain, target: self, action: Selector(("tappedToolBarBtn:")))
        let toolBarBtnToday = UIBarButtonItem(title: "今日", style: .plain, target: self, action: Selector(("tappedToolBarBtnToday:")))        
        toolBarBtn.tag = 1
        toolBar.items = [toolBarBtn, toolBarBtnToday]        
        textField.inputAccessoryView = toolBar
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    
    // 「完了」を押すと閉じる
    func tappedToolBarBtn(sender: UIBarButtonItem) {
        textField.resignFirstResponder()
    }    
    // 「今日」を押すと今日の日付をセットする
    func tappedToolBarBtnToday(sender: UIBarButtonItem) {
        myDatePicker.date = NSDate() as Date
        changeLabelDate(date: NSDate())
    }
    func changedDateEvent(sender:AnyObject?){        
    func dateToString(date: Date) -> String {
        //DateFormatterは参照型なので、letが適切
        let date_formatter = DateFormatter()
        //曜日の1文字表記をしたいならweekdaysなんて配列はいらない
    
        date_formatter.locale     = Locale(identifier: "ja")
        date_formatter.dateFormat = "yyyy年MM月dd日（E） " //<-`E`は曜日出力用のフォーマット文字
        return date_formatter.string(from: date as Date)
    }
    
[/code]

}


CC BY-SA 3.0
2016-12-12T10:53:57.527
31100
2016-12-12T13:08:47.520
19879
question
xcode swift3
「Swiftで UITextField から UIDatePicker を呼び出す」をswift3に変換する際のエラー(2)


既出の質問との関連質問を書かれる場合は、既出の質問へのリンクを「補助的な情報」として含められた方が良いでしょう。(その場合でも、このご質問のようにリンク先を見なくても一つの質問として完結していることが望ましいです。)

また、今回のあなたのコード、コピペミスでしょうか、少し欠けていますね。

[code]

    func changedDateEvent(sender:AnyObject?){        
    func dateToString(date: Date) -> String {
    
[/code]

関連質問にあるリンク先の記事から以下のように復元させていただきました。

`func changedDateEvent(sender:AnyObject?){`

>
[code]

>         var dateSelecter: UIDatePicker = sender as! UIDatePicker
>         self.changeLabelDate(date: myDatePicker.date as NSDate)
>     }
>  
>     func changeLabelDate(date:NSDate) {
>         textField.text = self.dateToString(date: date as Date)
>     }
>  
[/code]

`func dateToString(date: Date) -> String {`

では **本題** に入りましょう。

* * *

## エラーの原因

あなたのコードで問題があるのは、この部分:

`myDatePicker.addTarget(self, action:` **`Selector(("changedDateEvent:"))`**`,
for: UIControlEvents.valueChanged)`  
`let toolBarBtn = UIBarButtonItem(title: "完了", style: .plain, target: self,
action: `**`Selector(("tappedToolBarBtn:"))`**`)`  
`let toolBarBtnToday = UIBarButtonItem(title: "今日", style: .plain, target:
self, action: `**`Selector(("tappedToolBarBtnToday:"))`**`)`

と、この部分:

`func `**`tappedToolBarBtn(sender:`**` UIBarButtonItem) {`  
`func `**`tappedToolBarBtnToday(sender:`**` UIBarButtonItem) {`  
`func `**`changedDateEvent(sender:`**`AnyObject?){`

とで **セレクターとメソッドの整合性が取れていない** ことに起因しています。

整合性が取れれば動くようにできるので、どちらか一方を修正しても良いのですが、どちらの書き方もSwift3では標準的とは言えないものなので、両方修正することにしましょう。

* * *

## アクションメソッドの修正

アクションメソッドを記載するときは、呼び出し側がSwift2の時と互換になる形の方が良いでしょう。

`func `**`tappedToolBarBtn(_`**` sender: UIBarButtonItem) {`  
`func `**`tappedToolBarBtnToday(_`**` sender: UIBarButtonItem) {`  
`func `**`changedDateEvent(_`**`sender:` **`UIDatePicker`**`){`

3つのメソッドとも`sender`の前に「アンダーライン」「スペース」が入っているのにご注意ください。Swift
3ではメソッドの全ての引数がデフォルトで外部名(パラメータラベル)を持つように言語仕様の修正がありました。そのため、Swift
2以前と呼び出し側が同じになるようにする(セレクターを使う場合を含む)ためには先頭の引数の前に`_
`を付加して、「この先頭引数には外部名はありません」と宣言してやる必要があります。

ちなみに3つ目のメソッドについては引数型も修正させてもらっています。これは「Swift2の時代でもこの書き方はないだろう」の部分で、`UIDatePicker`からのアクションメソッドでは必ず`sender`は`UIDatePicker`インスタンスになるので、それを一旦`AnyObject`で受けてキャストで`UIDatePicker`に変換する、なんてことは必要ありません。ましてやなぜかOptionalにしている(`AnyObject?`の`?`)点に至っては意味不明です。

* * *

## セレクター表記の修正

順序が前後しましたが、後は上のメソッドに合わせてセレクター表記の方を修正しなければいけません。最初に一つ覚えておいて欲しいのは、

**Swift
2.2以降では`Selector`のイニシャライザを使ったセレクター表記(`Selector(("changedDateEvent:"))`)は可能なかぎり使わないこと**

です。 **必ず`#selector`構文を使ってください**。

※Swiftがサジェスチョンで出してくる「カッコを二重にする」候補は、`Selector(...)`を使わざるをえないことを理解してやっているエキスパート用のものだと思ってください。

同じクラスの中から、上記3つのアクションメソッド(もちろん修正後のもの)を示す`#selector`構文は以下のようになります。

`#selector(tappedToolBarBtn(_:))`  
`#selector(tappedToolBarBtnToday(_:))`  
`#selector(changedDateEvent(_:))`

あなたのコードの場合、各メソッドには曖昧さはないので、次のように短縮形で書くこともできます。

`#selector(tappedToolBarBtn)`  
`#selector(tappedToolBarBtnToday)`  
`#selector(changedDateEvent)`

※逆にもっと長くなっている場合を見かけることもあるでしょうが、無理に長くする必要はありません。

ちなみに`#selector`構文を使う最大のメリットは、 **存在しないメソッドを指定した場合、コンパイル時のエラーとなる**
ことです。今回のあなたの事象のように、実行してみて初めて **unrecognized selector sent to instance**
(インスタンスには存在しないセレクターを使ってるよ…意訳です)なんてエラーに遭遇する可能性を極めて低くしてくれます。

* * *

## 修正後のコード

その他にも無駄なキャストを使っている部分などを修正すると、あなたのコードは以下のような感じになります。

[code]

    class ViewController: UIViewController, UIToolbarDelegate {
        @IBOutlet weak var textField: UITextField!
        var toolBar:UIToolbar!
        var myDatePicker: UIDatePicker!
        
        override func viewDidLoad() {
            super.viewDidLoad()
            // 入力欄の設定
            textField.placeholder = dateToString(date: Date()) //<-`dateToString`のパラメータは`Date`型なので最初から`Date()`を渡す
            textField.text        = dateToString(date: Date()) //<-同上
            self.view.addSubview(textField)
            
            // UIDatePickerの設定
            myDatePicker = UIDatePicker()
            myDatePicker.addTarget(self, action: #selector(changedDateEvent), for: UIControlEvents.valueChanged)
            myDatePicker.datePickerMode = UIDatePickerMode.date
            textField.inputView = myDatePicker
            
            // UIToolBarの設定
            toolBar = UIToolbar(frame: CGRect(x: 0, y: self.view.frame.size.height/6, width: self.view.frame.size.width, height: 40.0))
            
            toolBar.layer.position = CGPoint(x: self.view.frame.size.width/2, y: self.view.frame.size.height-20.0)
            toolBar.barStyle = .blackTranslucent
            toolBar.tintColor = UIColor.white
            toolBar.backgroundColor = UIColor.black
            
            let toolBarBtn      = UIBarButtonItem(title: "完了", style: .plain, target: self, action: #selector(tappedToolBarBtn))
            let toolBarBtnToday = UIBarButtonItem(title: "今日", style: .plain, target: self, action: #selector(tappedToolBarBtnToday))
            toolBarBtn.tag = 1
            toolBar.items = [toolBarBtn, toolBarBtnToday]
            textField.inputAccessoryView = toolBar
        }
        
        override func didReceiveMemoryWarning() {
            super.didReceiveMemoryWarning()
        }
        
        // 「完了」を押すと閉じる
        func tappedToolBarBtn(_ sender: UIBarButtonItem) {
            textField.resignFirstResponder()
        }
        // 「今日」を押すと今日の日付をセットする
        func tappedToolBarBtnToday(_ sender: UIBarButtonItem) {
            myDatePicker.date = Date()  //<-Date型のプロパティに現在時刻を入れるなら`Date()`を渡すだけでOK
            changeLabelDate(date: Date())  //<-Date型の引数に現在時刻を渡すときも同じく`Date()`だけでOK
        }
        func changedDateEvent(_ sender: UIDatePicker){ //<- `UIDatePicker`からのactionの`sender`は必ず`UIDatePicker`になる
            //`sender`を直接`UIDatePicker`として使えばいいのでキャストは不要
            self.changeLabelDate(date: sender.date)
        }
    
        func changeLabelDate(date: Date) { //<- `NSDate`を使っているところは全て`Date`に置き換える
            textField.text = self.dateToString(date: date)
        }
        
        func dateToString(date: Date) -> String {
            //DateFormatterは参照型なので、letが適切
            let date_formatter = DateFormatter()
            //曜日の1文字表記をしたいならweekdaysなんて配列はいらない
            
            date_formatter.locale     = Locale(identifier: "ja")
            date_formatter.dateFormat = "yyyy年MM月dd日（E） " //<-`E`は曜日出力用のフォーマット文字
            return date_formatter.string(from: date as Date)
        }
    }
    
[/code]

細かいことを言えば、まだあれこれ修正の余地はありますが、とりあえず「Swift3で UITextField から UIDatePicker
を呼び出す」のを試すにはこれで十分のはずです。お試しください。


CC BY-SA 3.0
2016-12-12T13:08:47.520
31101
2016-12-12T13:08:47.520
2020-06-17T08:14:45.997
-1
13972
31100
answer