2017年12月15日金曜日

コンテキストメニューを置き換えてみた

作成中のアプリで、UITextViewのコンテキストメニューに機能を追加したいと思いまして。

こういうの

調べたところ、touchesBeganとcanPerformActionをオーバーライドすればカスタマイズできそうとのことなのでこんな感じにしてみました。
protocol CustomedMenuTextViewDelegate: class {
    func customedMenu1(selectedText: String)
    func customedMenu2(selectedText: String)
    func customedMenu3(selectedText: String)
}

class CustomedMenuTextView: UITextView {
    
    weak var customedMenuDelegate: CustomedMenuTextViewDelegate?

    override func touchesBegan(_ touches: Set, with event: UIEvent?) {
        let menuController = UIMenuController.shared
        menuController.setTargetRect(CGRect.zero, in: self)
        menuController.arrowDirection = .down
        
        let menuItems = [
            UIMenuItem.init(title: "Menu1", action: #selector(menu1Selected(sender: ))),
            UIMenuItem.init(title: "Menu2", action: #selector(menu2Selected(sender: ))),
            UIMenuItem.init(title: "Menu3", action: #selector(menu3Selected(sender: ))),
            ]
        menuController.menuItems = menuItems
        menuController.setMenuVisible(true, animated: true)
    }
    
    override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
        if action == #selector(menu1Selected(sender: )) ||
           action == #selector(menu2Selected(sender: )) ||
           action == #selector(menu3Selected(sender: )) {
            return true
        }
        
        return false
    }
    
    // MARK: - ACTION EVENT METHOD
    
    @objc private func menu1Selected(sender: Any) {
        guard let _ = sender as? UIMenuController else {
            print("error1")
            return
        }
        
        let selectedText = self.text(in: self.selectedTextRange!)!
        if let d = customedMenuDelegate {
            d.customedMenu1(selectedText: selectedText) //選択された文字列を返す
        }
    }
    
    @objc private func menu2Selected(sender: Any) {
        guard let _ = sender as? UIMenuController else {
            print("error2")
            return
        }
        
        let selectedText = self.text(in: self.selectedTextRange!)!
        if let d = customedMenuDelegate {
            d.customedMenu2(selectedText: selectedText) //選択された文字列を返す
        }
    }
    
    @objc private func menu3Selected(sender: Any) {
        guard let _ = sender as? UIMenuController else {
            print("error3")
            return
        }
        
        let selectedText = self.text(in: self.selectedTextRange!)!
        if let d = customedMenuDelegate {
            d.customedMenu3(selectedText: selectedText) //選択された文字列を返す
        }
    }
}

呼び出す側はこうします。

class ViewController: UIViewController, CustomedMenuTextViewDelegate {

    @IBOutlet weak var textView: CustomedMenuTextView!
    
    // MARK: - UIViewController: RESPONDING TO VIEW EVENTS
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        textView.isEditable = false
        textView.customedMenuDelegate = self
    }

    // MARK: - UIViewController: HANDLING MEMORY WARNINGS
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    // MARK: - METHODS FOR CustomedMenuTextViewDelegate
    
    func customedMenu1(selectedText: String) {
        print("Menu1: selected text = " + selectedText)
    }
    
    func customedMenu2(selectedText: String) {
        print("Menu2: selected text = " + selectedText)
    }
    
    func customedMenu3(selectedText: String) {
        print("Menu3: selected text = " + selectedText)
    }
}

もう少し汎用性を持たせたいところですが、とりあえず動作するようなので我慢します。


こうなりました

Appleによる正規の方法ではなさそうです。

WKWebView(deprecatedだけどUIWebViewでもいいけど)でも、同じことをしたかったのですが、この方法ではダメでした。残念。

良い方法ないですかね?


「使ってみた(App Extension編) その1」に続きます。

iOS 11 Programming
  • 著者:堤 修一,吉田 悠一,池田 翔,坂田 晃一,加藤 尋樹,川邉 雄介,岸川克己,所 友太,永野 哲久,加藤 寛人,
  • 発行日:2017年11月16日
  • 対応フォーマット:製本版,PDF
  • PEAKSで購入する


2017年12月1日金曜日

チューニングなどという(貴族編)

「思うところあって」と、ギターのチューニングについて書いてきました。

チューニングなどという(まずは基本ですよ編)
チューニングなどという(これはアカン編)
チューニングなどという(そしてリベンジ編)
チューニングなどという(だからチューナー編)


まぁ、自分のアプリのPRのために書いたわけですけど。最後は、その「思うところ」をふたつばかりちょっと。


ひとつめは、「これはアカン編」で書いた、ハーモニクスを使ったチューニング方法はだめだということ。

今どきチューナーも安いですし、みんな使ってるだろと思っていたのですが、YouTubeを見ると、「ちょっと通っぽいチューニング方法」みたいな体でハーモニクスでのチューニングを披露していたりするわけです。何人も。中には、ギタースクールの講師と称する人すらいたりするのです。そして再生回数もかなり稼いでいます。


あかんでしょ、これ(笑)。


ま、それがこの一連のシリーズを書くきっかけだったわけです。


ふたつめは、初心者がチューニングメーターを使うことに反対する方がいることです。

自分の耳でチューニングすることで耳を鍛えることができるんだ、という主張です。もちろん考えは人それぞれですが、それはちょっとどうでしょうか。

そもそも、チューニングで耳を鍛える(音感を鍛える)、ってなに?

今まで書いてきたように、ギターのチューニングなんて二つの音が同じになるように調節することです。わんわん鳴る唸りが出ないようにユニゾンでぴったり合わせるだけのことです。

ギターの音に耳をすまして集中すること自体は良いことですし、大切なことだとは思いますが、それってチューニングでやる必要があるでしょうか。

僕は、初心者こそチューニングメーターを使うべきだと思ってます。

そもそも、音感を鍛えると言ったって、正しくチューニングされていないギターを、初心者がああでもないこうでもないとペグを回したところで、耳が鍛えられるなんてことは考えにくいです。時間の無駄ですよ。さっさとチューナーで正しく合わせて、少しでも多く練習した方が、よほどマシです。

例えば、ピアノを学ぶ人は、まず、ピアノの調律を学ばなければならないなんてことはないでしょう?それともピアニストは音感が悪いのでしょうか?

チューニングは、楽器を演奏するための準備であって、練習でもトレーニングでもありません。

ギターに限らず、初心者にとって最も大切なことのひとつは、正しくチューニングされた楽器を弾くことだと思います。普段から正しくチューニングされた楽器を弾くことで、正しい状態が身について、そしてもし合っていないときに「あれ、おかしいな?」ってわかるようになると思うのです。

つまり、正しくチューニングされた楽器によって音感が磨かれるのであって、音感を磨くのにチューニングをするのではないです。結果と過程をすり替えてはいけないと思います。


”そう、チューニングなどという雑事は、使用人 チューナー に任せておけば良いんですよ。” 
by 貴族探偵