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 貴族探偵





2017年11月15日水曜日

Swift、フォー!

「フォー!」って叫んでたあの芸人さん、最近見ませんね。僕があまりテレビを見ないから、そう思うだけかもしれませんけど。


Xcode9になって、Swift4が使えるようになりました。

Swift3で書かれたプロジェクトを開くと、Swift4への変換のメッセージがでます。

なんだかframeworkやCocoaPodsも巻き込んでいきそうな感じです。以前のSwiftのアップデート時には、派手にソースが変更されて冷や汗をかきました。

今回ももしやと、びくびくしながら続けていくと、大した変更はありませんでした。

変換された部分は、#selector()で指定しているイベントのアクションの関数の定義に、@objcが付けられた程度です。

    //略
    let nc = NotificationCenter.default
    nc.addObserver(self,
                   selector: #selector(Foo.bar),
                   name: "piyo"
                   object: nil)
    //略
    @objc func bar() {
        //略
    }
    //略

しかし、なぜ@objcなんでしょうか。このプロジェクトはすべてSwiftで書かれていますし、何だか腑に落ちないです。どこから来たのobjc属性。

しかも追加された@objcの箇所にワーニングが出力されます。何か間違ってませんか?


Selector Expression
A selector expression lets you access the selector used to refer to a method or to a property’s getter or setter in Objective-C.

#selector(method name)
#selector(getter: property name)
#selector(setter: property name)

The method name and property name must be a reference to a method or a property that is available in the Objective-C runtime. The value of a selector expression is an instance of the Selector type.

(“The Swift Programming Language (Swift 3.1)”/Apple Inc.より)


ふむふむ。


セレクタはObjective-Cの概念であるため、Selector型を生成するにはメソッドがObjective-Cから参照可能である必要があります。メソッドをObjective-Cから参照可能にするには、objc属性を指定します。

(Swift実践入門/石川洋資,西山勇世 P289より)


なるほど。

どこかからObjctive-Cが湧いてきたわけではなく、そもそもセレクタはObjecive-Cのランタイムにおける概念なのですね。

これはSwift4の書式に変換してくれたというより、本来必要だった@objcを、厳しくなったコンパイラのために付けてくれたということなのでしょう。


試しに、@objcを削除してみるとエラーになります。

Argument of '#selector' refers to instance method 'bar()' that is not exposed to Objective-C
Add '@objc' to expose this instance method to Objective-C


ちなみに、objc属性の箇所のワーニングですが、

The use of Swift 3 @objc inference in Swift 4 mode is deprecated. Please address deprecated @objc inference warnings, test your code with “Use of deprecated Swift 3 @objc inference” logging enabled, and then disable inference by changing the "Swift 3 @objc Inference" build setting to "Default" for the "xxxxxx" target.

これは、TARGETSのBuild Settingsをswiftで検索して、Swift 3 @objc Inference(Onになっていると思います)を、Defaultにすると消えます。



これで安心してSwift4を使えますね。




2017年11月1日水曜日

チューニングなどという(だからチューナー編)

チューニングについて書いてきましたが、初心者にとって、ギターを正しくチューニングするのは、なかなか大変です。


そこでチューニングメーターですよ。奥さん。


市販されているものはだいたい大丈夫だと思うので、特別高価なものである必要はありません。利用する状況に応じて決めると良いのではないでしょうか。例えば、エレキギターならシールドを直接つなぐことができるタイプとか、アコースティックギターならクリップ式のものだとか。

クリップ式だと、ちょっと古いですがKORGのAW-1を持っています。アコースティックギターはもちろん、ソリッドボディーのエレキギターでも大丈夫です。ヘッドというかネックって、思っている以上に振動してるんですね。あと、メーターが機械式じゃないもの方が、少しくらい雑に扱っても故障しにくいかもしれません。

どれもちょっと古いですが。

普段のチューニングに加えて、エレキギターの場合はオクターブチューニングが必要です(あ、いや、アコースティックギターでも本来必要ですが、自分でやる人は少ないと思います。僕もやったことはないです)。つまりサドルの調整ですね。最低でも弦を交換したタイミングでは行うべきです。これはチューナーなしでは無理です。工夫すれば、できなくはなさそうですが、そんなこと普通はやらないでしょう。素直にチューナーを使いましょう。21世紀なんですから。

チューナーの使い方ですが、合わせたい音を設定する方法は機種によって違いますが、メーターの見方は同じです。針が真ん中が音があっている状態、左が低い(フラット)、右が高い(シャープ)です。

アプリのヘルプ用に作った動画があるので、よかったら見てみてください。(画質が良くないのは、スマホ用に少しでもパケットを節約して見てもらえるようにするためなので、許してください)

合わせたい弦を、びーんと鳴らしたとき、針が真ん中より左を指していたら、ゆっくりとペグを巻き上げて、中心を指すようにします。

もし、真ん中より右を指しているときは、ギターの音が高いので、ペグを回して緩めればいいわけです。でも、音を合わせるときには「低い方から巻き上げながら合わせる」というのがお約束でしたから、一度針が左側に来るまで緩めてから、ゆっくりと巻き上げながら針を真ん中に合わせるのがいいと思います。


こんな感じで、チューナーがあればチューニングを素早く簡単にできます。そして何より正確です。必携です。




「チューニングなどという(貴族編)」に続きます。

2017年10月15日日曜日

帰ってきた凛としてSwift(くるくる編)

泣きながらiPhoneX対応をしています。なんであんなにワーニングが出るんでしょうか、Xcode9のStroyboard。

ウェブページを表示する画面を作っていたのですが、また見慣れないSwiftLintのワーニングが出たので、シリーズ続行です。


UIWebViewがdeprecatedになったので、WKWebViewを使うことにしました。

ページのロード中を示すインジケータの表示ですが、UIWebViewならUIWebViewDelegateを実装すれば開始と終了のイベントが取得できるので、丸いアニメーションインジケータを表示するのが簡単だと思います。WKWebViewでも、WKNavigationDelegateを実装すれば同様にコントロールできます。

(※ 今回のソースは全てSwift4で、最小限動くところまで削るためにターゲットをiOS11.0以降にしました。ご注意を。)

import UIKit
import WebKit

class SomeWeb4ViewController: UIViewController, WKNavigationDelegate {

    @IBOutlet weak var webView: WKWebView!
    @IBOutlet weak var indicatorView: UIActivityIndicatorView! //くるくるインジケータ
    
    override func viewDidLoad() {
        super.viewDidLoad()

        indicatorView.hidesWhenStopped = true //くるくるしてないときは、非表示
        
        //webページのロード
        webView.navigationDelegate = self
        let myURL = URL(string: "https://www.apple.com/")
        let myRequest = URLRequest(url: myURL!)
        webView.load(myRequest)
    }
    
    func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
        indicatorView.startAnimating() //くるくる開始
    }
    
    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        indicatorView.stopAnimating() //くるくる停止
    }
}
まぁ、見たとおりです。WKNavigationDelegateで、Webページのロードの開始と停止のイベントを取得しています。処理もシンプルで分かりやすいですね。

ただ、WKWebViewはKVOに対応しているので、もう少し気の利いた制御をするのがお約束なのでしょうか。そんな情報が多いです。そこで、それらを真似てナビゲーションバーの下に、横に伸びるインジケータ表示にしました。

import UIKit
import WebKit

class SomeWeb2ViewController: UIViewController {

    @IBOutlet weak var webView: WKWebView!
    
    var progressView = UIProgressView()
    
    override func viewDidLoad() {
        super.viewDidLoad()

        //プログレスバー
        let progFrame = CGRect(x: 0.0,
                               y: self.navigationController!.navigationBar.frame.size.height - 2.0,
                               width: self.navigationController!.navigationBar.frame.size.width,
                               height: 10.0)
        progressView = UIProgressView(frame: progFrame)
        progressView.progressViewStyle = .bar
        self.navigationController?.navigationBar.addSubview(progressView)
        
        //ここで、webViewのobserverを設定して
        webView.addObserver(self, forKeyPath: "loading", options: .new, context: nil)
        webView.addObserver(self, forKeyPath: "estimatedProgress", options: .new, context: nil)

        //webページのロード
        let myURL = URL(string: "https://www.apple.com/")
        let myRequest = URLRequest(url: myURL!)
        webView.load(myRequest)
    }
    
    //ここで進捗を受信
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        if keyPath == "estimatedProgress"{
            progressView.setProgress(Float(webView.estimatedProgress), animated: true)
        } else if keyPath == "loading"{
            UIApplication.shared.isNetworkActivityIndicatorVisible = webView.isLoading
            if webView.isLoading {
                progressView.setProgress(0.1, animated: true) //プログレスの開始
            } else {
                progressView.setProgress(0.0, animated: false) //プログレスを消去
            }
        }
    }
    
    deinit {
        webView.removeObserver(self, forKeyPath: "estimatedProgress")
        webView.removeObserver(self, forKeyPath: "loading")
    }
}
webViewに、"loading"と"estimatedProgress"のobserverを付加して、ロードの開始、進捗、終了を取得しています。

ちなみにdeinitにある、observerの削除がないと落ちるとのことですが、11.0では落ちなかったです。だから不要、というわけでもなさそうですが。

もちろんこれでちゃんと動作するのですが、SwiftLintがワーニングを出力します。


・block_based_kvo
Block Based KVO Violation: Prefer the new block based KVO API with keypaths when using Swift 3.2 or later. (block_based_kvo)


new block based KVO API with keypaths ってなに?って感じですが、 どうやらWKWebViewに付加したobserverを func observeValue(){} で受信する方法は古いようです。

そこで、Adopting Cocoa Design Patternsの「Key-Value Observing」を参考にして、 NSKeyValueObservationを使った方法に書き換えてみました。
import UIKit
import WebKit

class SomeWeb3ViewController: UIViewController {
    
    @IBOutlet weak var webView: WKWebView!
    var progressView = UIProgressView()
    
    var obsLoading: NSKeyValueObservation? //←これ
    var obsEstimatedProgress: NSKeyValueObservation? //←これ
    
    override func viewDidLoad() {
        super.viewDidLoad()

        //プログレスバー
        let progFrame = CGRect(x: 0.0,
                               y: self.navigationController!.navigationBar.frame.size.height - 2.0,
                               width: UIScreen.main.bounds.size.width,
                               height: 10.0)
        progressView = UIProgressView(frame: progFrame)
        progressView.progressViewStyle = .bar
        self.navigationController?.navigationBar.addSubview(progressView)
        
        //ここから→
        obsLoading = observe(\.webView.loading) { _, _ in
            UIApplication.shared.isNetworkActivityIndicatorVisible = self.webView.isLoading
            if self.webView.isLoading {
                self.progressView.setProgress(0.1, animated: true) //プログレスの開始
            } else {
                self.progressView.setProgress(0.0, animated: false) //プログレスを消去
            }
        }
        obsEstimatedProgress = observe(\.webView.estimatedProgress) { _, _ in
            self.progressView.setProgress(Float(self.webView.estimatedProgress), animated: true)
        }
        //←ここまで

        let myURL = URL(string: "https://www.apple.com/")
        let myRequest = URLRequest(url: myURL!)
        webView.load(myRequest)
    }
}
Swift4におけるKVOのベストプラクティスがよくわかっていないので、本当にこれが正しいのか自信がないのですが、とりあえずワーニングが消えて、ちゃんと動きました。内心、ちょっと怪しいとは感じています。ま、そのときはそのときで随時修正します。

それにしても、なにこのバックスラッシュ。Swiftこわい。


♪♪♪


こちらもどうぞ。
凛としてSwift
凛としてSwift (血闘編ノ壱)
凛としてSwift (血闘編ノ弐)
凛としてSwift (血闘編ノ参)


2017年10月1日日曜日

チューニングなどという(そしてリベンジ編)

前回の「チューニングなどという(これはアカン編)」で、ハーモニクスを使ったチューニング方法はダメだという話を書きました。今回は、じゃあそれは何の役にも立たないかというかというと、「ちょっと待て、そこは考えどころじゃないか?」という話です。

また、5弦を基準に4弦を合わせる場合に限定して書きます。

正しいチューニング方法では、5弦の5フレットの実音に4弦の開放を合わせるというのは「チューニングなどという(まずは基本ですよ編)」で書きました。

そして、ハーモニクスを使ったチューニング方法では、5弦の5フレットのハーモニクスに4弦の7フレットのハーモニクスを合わせるのでした(でも実際は4弦が少しだけ低くなってしまう)。



そして、2つの音を同じにするよう合わせるには、音を同時に出して唸りがなくなるようにすれば良いことも書きました。

ここからは実際にチューニングをする時の動作についてです。

実音を使った正しいチューニングでは、5弦の5フレットを左手で押さえていないといけないので、ふたつの音を聞きながらペグを回すことはできません。右手でペグを回すという手もありますが、それでは両手が交差して大変ですし、5フレットを押さえている左手に変に力が入って音程も安定しないでしょう。そんなわけで、音をチェックしてはペグを回し、音をチェックして回し、の繰り返しになります。

うむ、これはそういうものなのでしょうがないですな。うんうん。

それに対して、ハーモニクスを使った方法は、音を出している間左手が自由になりますから、音を聞きながらペグを回すことができます。これはちょっと便利ですよね。

そこで、だいたい近いところまではハーモニクスを使ってから、仕上げに実音を使ったチューニングで確認するというのはどうでしょう。

ハーモニクスを使った方法は、基準となる5弦から高い方の4弦を合わせるとき、ピッタリ合わせても、4弦が正しい音程よりも少し低くなるのでした。そこで、そこからは実音でチェックしながら、少しずつ4弦を上げていって合わせるといった方法です。

ペグを回す時は低い方から巻き上げていって、正しい音に合わせるというのがお約束でしたが、これにも馴染みます。

基準となる弦から低い方の現に合わせるとき、つまり5弦の7フレットのハーモニクスに、6弦の5フレットのハーモニクスを合わせる時ですが、この場合はぴったり合わせてしまうと、6弦が少し高くなってしまうので、少し手前でやめて、そこからは、やっぱり実音で確認しながら少しづつ上げていくのがいいと思います。

ハーモニクスでざっくり合わせて、実音で正確にチェック。これですよ。

普段ギターの弦は、張りっぱなしにしている人が多いと思いますが、「ネックが弱いので、弾かないときは弦を緩めておかないとちょっと不安なんだよね」というような方は、この"ハーモニクスでざっくり、実音で正確に"というやり方は便利なのではないでしょうか。そうでなくても、弦の交換時など、全くチューニングがあっていない状態からチューニングする時には役に立ちそうです。

今回は、役に立たないチューニング方法を、なんとか使えないものかという視点で考えてみました。どうでしょうか。





「チューニングなどという(だからチューナー編)」に続きます。

2017年9月15日金曜日

Blender?

備忘録です。

Blenderの新しいバージョン2.79がリリースされたので、早速バージョンアップしてみました。

古いバージョンをアプリケーショフォルダから削除したら、ここから新しいバージョンをダウンロードして、解凍してアプリケーションフォルダに入れるだけです。フォルダ名はblenderに変更しました。まぁ、上書きでも大丈夫だとは思いますが。

アプリケーションの中にポンっ!


ちなみにBlenderフォルダの中

Blender.appをダブルクリックすると・・・

ちゃんとアップデートされています。

で、当然ドックにアイコンを作りますよね。


再起動しますよね。

あれ?

なにこのはてな。こっちが?ですわ。クリックしても反応しません。でも以前見たことがあります。

そういえば前にインストールした時に何か、ごにょごにょしたよなぁ。。。なんだっけ?
覚えていないので、グーグル先生に聞きました。そう、これこれ。

$ xattr -dr com.apple.quarantine /Applications/blender/

これで、ドックにアイコンを作り直せば、再起動しても大丈夫です。

OK!

これで次から、あたふたしなくて済みそうです。





2017年9月1日金曜日

チューニングなどという(これはアカン編)


前回、ギターのチューニング方法について書きましたが、今回はよく知られて「は」いる方法の、ハーモニクスを使ったチューニングについて書きます。

ただ最初に断っておきますが、この方法はダメです。ちゃんと音が合いません。

でもあえて説明します。

さて、音叉で5弦を合わせるところは同じです。正しく音があった5弦を基準に、順にチューニングしていきます。
  • 5弦の7フレットのハーモニクスに、6弦の5フレットのハーモニクスを合わせる。
  • 5弦の5フレットのハーモニクスに、4弦の7フレットのハーモニクスを合わせる。
  • 4弦の5フレットのハーモニクスに、3弦の7フレットのハーモニクスを合わせる。
  • 3弦の4フレットのハーモニクスに、2弦の5フレットのハーモニクスを合わせる。
  • 2弦の5フレットのハーモニクスに、1弦の7フレットのハーモニクスを合わせる。

3弦の4フレットのハーモニクスがちょっと苦しいですが、本題はそこではなく、各弦の7フレットの音がポイントです。

ここからは5弦と4弦に絞って説明していきます。

5弦の5フレットのハーモニクスはAです。そして4弦の7フレットのハーモニクスもAです。何の問題もなさそうですが、この4弦の7フレットのハーモニクスというのが曲者です。

まず大前提として、ギターはピアノやオルガンなどと同様、平均律の楽器です。フレットが均等に打たれてますからね(いうまでもなく、等間隔という意味ではなくて均等に幅が変化していきます)。

12フレットは開放弦の1オクターブ上の音になりますが、ちょうど真ん中です。正確には押さえた時に弦長が半分になります。

12フレットのハーモニクスは2倍振動なので、弦長が半分になるのと同じです。したがってこの2つの音は同じ音になります。逆に言うと、この2つの音が同じになるように調整するのが、いわゆるオクターブチューニングです。

開放弦
12フレット
12フレットのハーモニクス(開放の2倍振動)

5
フレットのハーモニクスは、5フレットがほぼ弦長の4分の3の位置にあるので4倍振動になり、正確に12フレットのさらに1オクターブ上の音になります。つまり5弦でいうと、開放弦の正確な2オクターブ上のAになるわけです。

5フレットのハーモニクス(開放の4倍振動)

さて問題の4弦の7フレットのハーモニクスです。

7フレットは、開放のほぼ3分の2の位置にあるので、そのハーモニクスは3倍振動になります。そうすると7フレットのAの1オクターブ上のA(2倍振動相当)が出そうなものなのですが。。。

平均律の正しい音程である7フレットのAは、開放の3分の2よりわずかに長いのです。その結果ハーモニクスは、7フレットの実音と比較して、わずかに高いということになります。

5フレットのハーモニクス(開放の3倍振動)
7フレット
※平均律の計算についてはまた改めて書くつもりです。

平均律の正しい音程である5弦の5フレットのハーモニクスに対して、このわずかに高い4弦の7フレットのハーモニクスが同じになるように、4弦を調整するとどうなるでしょうか。4弦の開放は本来合わせるべき、平均律のDより少し下げる(低い音にする)ことになってしまいます。

3弦のチューニングも、このわずかに低い4弦を基準にするので、さらに低くなります。
このわずかな差が、順に隣の弦、隣の弦、と積み重なってずれていって、1弦まで行った時には、随分と正確な音程から外れてしまいます。

まぁ4フレットのハーモニクスを使う時点でちょっとアレな感じではあるのですが。

前回、ギターというのはそれほど正確な音程が出せる楽器ではないとは書きましたが、限度があります。このチューニング方法はいけません。ダメですよ。






「チューニングなどという(そしてリベンジ編)」に続きます。

2017年8月15日火曜日

話題の書「アイデア大全」を斬るっ!

先日ブックオフをぶらぶらしてたら、アイデア大全を見つけました。

もったいねー。売っちゃったかー。こんなに実用的な本はないのに。

表紙に若干のスレはありますが、書き込みもなくキレイです。税込み1,260円。電子版よりちょっと安いですか。

とは言え、僕はすでに持っているので2冊目はいらんなぁ、と棚に戻そうとしたときヒラメキました。


手にとっただけで「!」が降りてくる恐ろしい本。。。


とりあえずブックオフから救出しました。

2冊のアイデア大全。

元々持っていた方は新品で購入して書店のカバーをかけてもらっていたのでとてもキレイです。


この中古のアイデア大全を、おもむろにカッターナイフで半分に。

著者さん、ごめんなさい。

さらに半分に

編集さん、ごめんなさい。

そして、さらに半分に。

本の神様、ごめんなさい。

そこでロータリーカッターの出番です。

慎重にカット。

これでバラバラになりました。

バラバラ。


あー、はいはい、スキャンして自炊ね。って思ったでしょ?
違いますよ。そんなの最初から電子版を買った方が楽チンですよ。

表紙と目次、あと、もったいないですけどアイデア史年表は捨てます。もう一冊ありますからね。
これは廃棄。


ここで2つ穴パンチの登場です。そして100均で買ったリング。

穴を開けます。


穴を開けて、リングを通せば出来上がりです。

出来上がり。


アイデア大全には42個の発想法が11章に分かれて紹介されていますが、内容的にはどこから読んでもいい作りになっています。

環状にしたことで始まりも終わりもなくなりました。ページがランダムになったわけではありませんが、ランダム「的」に開くことができますので、各発想法が順序に縛られないというか、各々独立した感が得られます。

この本は読むこと以上に、実践することこそ重要なので、いつでも手に取れるようにデスクの脇にばさっと置いて、気づいた時に数ページパラパラと読んで、ちょっと試して見るという感じで使うつもりです。

ま、それならわざわざ切る必要もないといえば、その通りなんですけどね(笑)。

ただ残念なことは、穴に対してリングが細いので、すぐに穴が傷みそうな点です。持ち運びには向いてませんね。穴が傷んだらその都度パッチで補強するというパワープレイでとりあえず我慢です。

もう少し太いリングが欲しいところ。


あと、いつまで公開されているかわかりませんが、こちらでクイズに答えると、読者特典として43個目の発想法「露伴のメトミニー」のPDFがダウンロードできます。もちろんプリントアウトして付け加えましたよ。

ちょっと手が空いたらクロヴィッツの関係アルゴリズムのアプリ化でもやってみようかなぁ。需要あるかなぁ。ってか訴えられたりしない?


と、まぁ、アイデア大全に対する鋭い批評、と思わせながらの自炊記事、に見せかけた上での、アイデア大全のカード化の話でした。ダメですか、そうですか。








2017年8月1日火曜日

チューニングなどという(まずは基本ですよ編)

ギターのチューニングについて、何回かに分けて書こうと思います。まぁ、結論としては僕の作ったアプリを使ってね(てへぺろ)、ってことではあるのですが、思うところもいろいろあるので。

まずは身もふたもないことを書きますが、ギターというのは構造的に、ピアノやオルガンなどと比べて、そんなに厳密に正確な音程を出せる楽器ではありません。オクターブチューニングをしっかり合わせても、各フレットの音ではどうしても誤差が生じます。フレットは指板に打ち込んでありますから、容易には調整できません。そもそも弦を押さえる強さの加減で音程が変わりますしね。クロマチックチューナーをお持ちなら、一度各音を測ってみてはいかがでしょうか。

とは言え、それでもチューニングはとても重要なわけで、ないがしろにはできません。

そこでまず今回は、基本的なギターのチューニング方法です。一応だらだらと言葉で書きますが、YouTubeなどでチューニング方法を検索すれば動画で説明されている方がすぐに見つかると思うので、そちらをご覧になった方がわかりやすいです。

使用するのは音叉だけです。A(ラ)の440HZのものです。音叉は楽器店で数百円で買えると思うので、一番リーズナブルで正確な方法だと思います。

音叉を鳴らす時は叩くわけですが、机のカドなど固い所に当てると狂う元になるので、太ももなんかにペシッと叩くのがいいのではないでしょうか。痛くない程度にね。

音叉はそのままでは音が小さいので、お尻の部分を何かに当てて共鳴させます。机やテーブルでもいいのですが、アコースティックギターなら、ギターが共鳴箱そのものですから、ボディに軽くあてるのがいいと思います。ただ、塗装面だと傷がついてしまうので、ピックガードやブリッジ、ブリッジピンの上など、固い部分がいいと思います。アンプをつないだエレキギターなら、音叉をピックアップに近づけるだけでアンプから音が出せます。

周りがうるさくて、直接耳で聞きたい場合は、音叉のお尻を耳の穴の1センチくらい手前(こめかみ方向)に当てればよく聞こえと思います。耳に穴に突っ込むのは危険ですからダメですよ。音叉のお尻の部分を歯で噛んでくわえてもよく聞こえます。骨伝導ってやつですね。

音叉の音とギターの5弦の12フレットのハーモニクスの音が同じになるように合わせます。

まずは2つの音をよく聞いて、なんとなく同じ音程まで近づけます。
2つの音を同時に鳴らすと、音がずれている場合はずれている分だけ唸りが発生します。びよよよよよ~んって感じで。これが近づいていくと、唸りが緩やかになります。びよ~うわぁ~んうわぁ~んうわぁ~んって感じですか。ぴったり合うと唸りはなくなって調和します。いわゆるユニゾンの調和ですね。そうなったら5弦はOKです。

これで5弦の音があったので、この5弦を基準にして他の弦を合わせていきます。この図はギターの入門書には必ず載っているものです。



  • 6弦の5フレットと5弦の開放が同じになるように。
  • 5弦の5フレットと4弦の開放が同じになるように。
  • 4弦の5フレットと3弦の開放が同じになるように。
  • 3弦の4フレットと2弦の開放が同じになるように。(ここだけ4フレットなので注意!)
  • 2弦の5フレットと1弦の開放が同じになるように。

と、なったら終了。と言いたいところですが、もう一度最初(音叉と5弦の12フレット)から確認します。ネックと弦の張力のバランスが変わると、当然音程も変わってきますので、再度確認が必要です。すべての音が合うまで何回か繰り返します。それが何回かは、こればっかりはそのギターのネックの強度にもよるので、何回とは言えませんが、何度やっても狂っているようなら、ナットやブリッジに何か問題があるかもしれません。身近に詳しい人がいたら見てもらうことをオススメします。

この音程が同じになるように合わせるというのが、言葉で言うと簡単なのですが、初心者にとってはなかなかハードルが高いと思います。慣れれば簡単なんですけどね。慣れないうちは、二つの音のどっちが高いのか低いのかすらよくわからないかもしれませんが、試行錯誤しながら慣れていってください。


ここでひとつペグを回す上での重要なポイントを。音を合わせる時には、低い状態からペグを巻き上げて、音があった状態にすることです。

弦はペグのポストに巻かれていると思いますが、弦の音を下げる時にこの巻かれた部分がわずかに緩みます。これがギターを弾いているうちにチューニングの狂いにつながっていきます。ギターを引くと言う行為は弦を弾くわけですが、言い換えるとほんの少し引っ張ることになるますから。

そんなわけで、音があったなと思ったら、あえてちょっとだけ弦の音を下げて、少しずつ音を確認しながら巻き上げて、音を合わせるようにするといいと思います。

唸りをチェック→ほんのちょっと巻き上げる→唸りをチェック→ほんのちょっと巻き上げる、の繰り返しですね。

とりあえず今回は以上です。何を当たり前のことをとお思いかもしれませんが、ギターのチューニングと言うのはこれが基本ですので、まずは押さえておいてください。






「チューニングなどという(これはアカン編)」に続きます。

2017年7月15日土曜日

ライセンスと謎アイコン

アプリを作るにあたって、いくつものオープンソースのライブラリなどを使わせてもらっています。ありがとうございます。

それらはBSDやMITなどのゆる~いライセンスですが、当然ライセンス条項に従った表記が必須です。そこで今回は、ライセンス条項の中の著作権表記とライセンス条項をアプリ内のどこで表示するかという話です。

Androidアプリでは専用のダイアログを用意してます。つまりライセンス表記用に画面をひとつ作っているわけです。

では、iOSではどうしているかというと、settings.bundleを使っています。本来はその名のとおり、そのアプリ固有の各種設定を操作する画面ですが、バージョンやライセンス表記に使うのにも都合がいいです。

まずプロジェクトの直下にSettings.bundleを追加します。




白いレゴブロックのようなパッケージが追加されます(どうでもいいことですけど、このアイコンどういう意味なんでしょうか)。中を見るとRoot.plistがありますので、Source Codeとして開きます。


上位のdictタグの下を変更します。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
 <!-- ここから -->
 <key>StringsTable</key>
 <string>Root</string>
 <key>PreferenceSpecifiers</key>
 <array>
  <dict>
   <key>Type</key>
   <string>PSGroupSpecifier</string>
   <key>Title</key>
 <!-- 中略 -->
   <key>MinimumValue</key>
   <integer>0</integer>
   <key>MaximumValue</key>
   <integer>1</integer>
   <key>MinimumValueImage</key>
   <string></string>
   <key>MaximumValueImage</key>
   <string></string>
  </dict>
 </array>
 <!-- ここまで -->
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
 <!-- ここから -->
 <key>PreferenceSpecifiers</key>
 <array>
        <dict>
            <key>Title</key>
            <string>About</string>
            <key>Type</key>
            <string>PSGroupSpecifier</string>
            <key>FooterText</key>
            <string>Copyright 20xx xxxxxx All Rights Reserved.
            </string>
        </dict>
        <dict>
            <key>Type</key>
            <string>PSChildPaneSpecifier</string>
            <key>Title</key>
            <string>Legal</string>
            <key>File</key>
            <string>Legal</string>
        </dict>
 </array>
 <key>StringsTable</key>
 <string>Root</string>
 <!-- ここまで -->
</dict>
</plist>
次にいったんXcodeを離れて、Finderでプロジェクトの下のSettings.bundleを右クリックして、「パッケージの内容を表示」で開きます。

ここにお好きな名前で、.plistファイルを作成します。わかりやすいようにLegal.plistとしました。内容は以下のとおりです。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>PreferenceSpecifiers</key>
    <array>
        <dict>
            <key>Title</key>
            <string></string>
            <key>Type</key>
            <string>PSGroupSpecifier</string>
            <key>FooterText</key>
            <string>Awsome Library
                
MIT License
        
Copyright (c) 20xx FooBar Co.Ltd
        
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
        
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
        
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
            </string>
        </dict>
    </array>
</dict>
</plist>
Settings.bundleのフッターの文字列を利用しているわけです。

Xcodeに戻って、Settings.bundleを開くとLegal.plistが作成されているのが確認できます。


これで実行すれば、設定アプリの中に今作ったアプリが追加されて、中に「Legal」が追加されています。


ラベルですが、僕は「Legal」としていますが、「Acknowledgements」などとしているアプリもありますね。複数の記述をするときは、Legal.plistarrayタグの下のdictタグを繰り返せば大丈夫です。

Legal.plistに記載するライセンス条項は、そのライブラリの中の文言を改変せず、そのままコピーします。通常はLICENSEというファイル名で置かれていることが多いですね。

ただ、正直なところ、改行位置だけは修正しています。改行位置がそのままだと非常に読みにくくなってしまうので、それでは本末転倒じゃないかと、そうしています。本当はよくないのかもしれませんが、改行位置ならセーフじゃないかなぁと思っています。法的な問題なので安易に大丈夫とは言えません。その辺り、詳しい方がいらっしゃったら意見を伺いたいところです。



2017年6月15日木曜日

凛としてSwift (血闘編ノ参)

凛としてSwift (血闘編ノ弐)」からの続きです。最終回です。


redundant_discardable_let

関数の返り値を読み捨てるのに、letを使うのは冗長だよ、です。
返り値を読み捨てていることの是非はともかく、letいらないの?知りませんでした。
let _ = daoFooDB.updateFooSortKey(foos)
_ = daoFooDB.updateFooSortKey(foos) //え、アンダースコアだけでいいの?まじか。


statement_position

ステートメントの開始位置がよくない、です。
このケースでは、そのステートメントの前に空白がないよ、というわけです。opening_braceと同じような意味合いというか、対になる感じでしょうか。
        if xMax <= 15.1 {
            //…省略
        }else if xMax <= 30.1 { //←elseの前にスペースがない
            //…省略
        }else if xMax <= 60.1 { //←elseの前にスペースがない
            //…省略
        }else { //←elseの前にスペースがない
            //…省略
        }
        if xMax <= 15.1 {
            //…省略
        } else if xMax <= 30.1 { //←elseの前にスペースを入れました
            //…省略
        } else if xMax <= 60.1 { //←elseの前にスペースを入れました
            //…省略
        } else {
            //…省略
        }


empty_count

配列が空かを判断するのには、isEmptyを使いましょう、ですね。
        if practices.count > 0 { //←配列が空か?
            //…省略
        }
        if !practices.isEmpty { //isEmptyを使用する
            //…省略
        }

※2018/12/29追記
countという変数名を使うと引っかかってしまうみたいなので注意です。
    func hoge(count: Int) {
        if count == 0 { //← empty_countエラー?
            //…省略
        } else {
            //…省略
        }
    }
    
    func hoge2(work: Int) {
        let count = work
        if count == 0 { //← empty_countエラー?
            //…省略
        } else {
            //…省略
        }
    }


variable_name

変数名,定数名にアンダースコアが入っているものがエラーになりました。
まぁ、アンダースコアはワイルドカードとして単体で意味があるので、変数には使用しない方がいいかもしれません。大文字のスネークケースの方が定数っぽいのに、と思うのはジジイだからでしょうか。
let SQL_PRACTICE_SELECT = "SELECT id, sort_key, name FROM practice WHERE id = ?;"
let kSqlPracticeSelect  = "SELECT id, sort_key, name FROM practice WHERE id = ?;"


force_cast

強制キャストは、失敗すると実行エラーになるので禁止ね、です。

そこでどうするかですが、そのキャストの失敗の重大性をどう見るかで変わってきそうです。起こりうる失敗として処理を続けるようなケースと、もう後続の処理を行う必要のないような重大なものです。

まず、後続の処理をハンドリングするために、キャストの成功時と失敗時の振り分けをif文で行う方法です。
ダウンキャストに失敗したとき、as!演算子だと実行時エラーになりますが、as?演算子を使ったときはnilを返します。これを利用してif let 文と組み合わせて、ダウンキャスト失敗時の処理をelseに落とします。
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "MenuTableViewCell", for: indexPath)
        self.updateCell(cell as! MenuTableViewCell, indexPath: indexPath) //←強制キャスト
     
        return cell
    }
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "MenuTableViewCell", for: indexPath)
        if let c = cell as? MenuTableViewCell {
            self.updateCell(c, indexPath: indexPath) //セルの表示項目の設定
            return c
        } //←この例ではelseがありませんが、もし失敗時の処理が必要ならここに。
     
        return cell
    }
これならキャストの成功/失敗で何かしらの処理を行って、後続の処理に流すことができますね。また、その値が複数の型のうちのどれかである可能性があるような場合など、if文でどの型かを判別して、それぞれ処理するのにも便利です。

※)2017/06/19追記
いくつかの型を判別してそれぞれ処理する時には、switch文でパターンマッチングを使った方が綺麗ですね。
    func fooBar(hoge: Any) {
        switch hoge {
        case let f as Foo:
            print("name is \(f.name)")
        case let f as Bar:
            print("name is \(f.name)")
        default:
            print("something wrong?")
        }
    }

次に、後続の処理をキャンセルしたい場合には、guard-let文でするりと抜けるのがスマートです。letで定義した定数が、guard文以降でも値を保持したまま使用可能という、まさにこのためにあるような便利機能です。
guard文の中にelseの処理は書けますが、thenの処理は書けません。また、elseの処理の中にはそのブロック内の後続処理に流れないようなreturn(もしくはbreakthrowなど)が必須です。if文のように分岐するというより、elseに入ったらもうブロック内の後続には流さない、以降の処理は条件が真であることを保証する、まさに「ガード」ですね。
    @IBAction func sliderVolumeValueChanged(_ sender: AnyObject) {
        guard let slider = sender as? UISlider else {
            //メッセージを出すとかね。
            return //←これは必須で、関数sliderVolumeValueChangedを抜けています
        }

        volume = slider.value //←guard-let文で定義されたsliderが使用できる!
        //…省略
    }
ちなみに、これをif letで書くと、ネストも深くなりますし、なんだか冗長です。
    @IBAction func sliderVolumeValueChanged(_ sender: AnyObject) {
        if let slider = sender as? UISlider {
            volume = slider.value
            //…省略
        } else {
            //メッセージを出すとかね。
        }
    }
ただ、このguard文の書式ですが言語的にはどうなんでしょうか。ここだけ定数のローカルスコープの例外っていうのは、なにやらモヤモヤします。規則性って大切です。例外ってのはできるだけ少ない方が人に優しいと思います。elseも、条件が偽である処理であることを示すためにあるのでしょうが、thenが書けないならelseも必要なさそうです。別の何かスマートな書式を定義しても良かったんじゃないかとは思います。言語を設計しているエラい人はそのあたりどう考えているんでしょうね?

それはさておき、ここで例に出したのはInterface Builderから生成されたアクションです。強制キャストに失敗することはありえなさそうです。
どうせキャストに失敗しないんだし、いっそのこと必要な型で受け取ってしまえば、とパラメータの型を変更してもちゃんと動きます。Lintのワーニングも出ません。でも、これは単に暗黙のうちに強制キャストをしているだけですし、むしろ強制キャストを隠してしまっているという点で、本末転倒で悪手じゃないかと思いますがどうでしょう?
    @IBAction func sliderVolumeValueChanged(_ sender: UISlider) { //← AnyObjectをUISliderに変更
        volume = sender.value //音量
        //…省略
    }
これをするくらいなら、// swiftlint:disableを使って、明示的にLintの例外とする方が健全だと思います。
    @IBAction func sliderVolumeValueChanged(_ sender: AnyObject) { //← AnyObjectをUISliderに変更
        // swiftlint:disable force_cast
        let slider = sender as! UISlider
        volume = sender.value //音量
        //…省略
    }
まぁ、問題なく動けば良いといえば、いいんですけどね。:P



force_try

エラーをthrowする文は、ちゃんとdo-catchでエラーをハンドリングしなさい、です。
    func playHoge(player: AVAudioPlayerNode, sourceFile: AVAudioFile, engine: AVAudioEngine) {
        player.scheduleFile(sourceFile, at: nil, completionHandler: nil)
        try! engine.start() //←エラーを投げる可能性あり
        player.play()
    }
    func playHoge(player: AVAudioPlayerNode, sourceFile: AVAudioFile, engine: AVAudioEngine) {
        player.scheduleFile(sourceFile, at: nil, completionHandler: nil)
        do {
            try engine.start()
            player.play()
        } catch {
            print("something wrong? \(error)")
        }
    }
まぁ、そうですよね。ただ、上記の例は別としても、try!を一律に制限するのもなぁ、という気もしないでもないです。でも、明示的にエラーを無視をしたいときには// swiftlint:disableを使って、ガツンと明示しなさいということなのでしょう。



unused_optional_binding

使わないならletでバインドする必要ないよね?、です。
    if let _ = h as? Hoge {
        //hはHoge型だよ
    } else {
       //hはHoge型じゃないよ
    }
    if h as? Hoge != nil {
        //hはHoge型だよ
    } else {
        //hはHoge型じゃないよ
    }
確かにそうなんですが、使わないことを明示するためのアンダースコアがあるんだから使ってもいいじゃんとは思います。個人的な好みだと上の方なんですが、Lintがそう言うならいたしかないです。

ただ上記の例のように、単に型を判断したいだけならtype(of: T)を使った方がいいのは間違いないです。
    if type(of: h) != Hoge.self {
        //hはHoge型だよ
    } else {
        //hはHoge型じゃないよ
    }


♪♪♪


以降のLintエラー、ワーニングは、// swiftlint:disableを使って明示的に回避しました。
ちなみに// swiftlint:disableに複数指定するときは空白を開けて、// swiftlint:disable cyclomatic_complexity function_body_lengthと書くようです。


function_parameter_count
関数のパラメータの数が多すぎ、です。

Objective-Cとの絡みもあり、ちょっと今は直せないと判断しました。
    @objc(calcIntonationGap:variety1:variety2:variety3:variety4:variety5:)
    static func calcIntonationGap(_ stimmung: Stimmung, variety1: StimmungVariety1, variety2: StimmungVariety2, variety3: StimmungVariety3, variety4: StimmungVariety4, variety5: StimmungVariety5) -> [Double] {

        //…省略
             
        return intonationGap
    }
    @objc(calcIntonationGap:variety1:variety2:variety3:variety4:variety5:)
    // swiftlint:disable function_parameter_count
    static func calcIntonationGap(_ stimmung: Stimmung, variety1: StimmungVariety1, variety2: StimmungVariety2, variety3: StimmungVariety3, variety4: StimmungVariety4, variety5: StimmungVariety5) -> [Double] {

        //…省略
     
        return intonationGap
    }


cyclomatic_complexity
これ、なんて訳せばいいのかよくわからなかったのでググったら、ウィキペディアの「循環的複雑度」が一番に出てきました。初めて聞いた単語(笑)。ざっくり言うと「分岐多すぎ」ですか。これも厄介で、五線譜を表示する処理ではどうしてもif文やswitch文の塊になるので避けられません。

type_body_length
構造体(enumも、かな?)の長さが、長すぎ。

function_body_length
関数の長さが、長すぎ。

file_length
ファイルの長さが、長すぎ。

この辺りは.swiftlint.ymlを修正して調整してもいいですが、少し頑張ればなんとかなりそうなので、あえて// swiftlint:disableで手が空くまでペンディングにしました。いずれにしても、// swiftlint:disableを使って回避した箇所は、定期的に棚卸しする必要があるでしょう。


これでLintのエラー、ワーニングが全て消えました。おつかれさまでした。

「帰ってきた凛としてSwift(くるくる編)」に続きます。


♪♪♪


こちらもどうぞ。
凛としてSwift
凛としてSwift (血闘編ノ壱)
凛としてSwift (血闘編ノ弐)
帰ってきた凛としてSwift(くるくる編)

2017年5月15日月曜日

凛としてSwift (血闘編ノ弐)

SwiftLintネタ「凛としてSwift (血闘編ノ壱)」からの続きです。


trailing_semicolon

いうまでもなく、文末のセミコロンですね。
気をつけていたんですが意外に多かったです。Objective-C混じりのプロジェクトなので、反射的に入力してしまっていたんですね。
self.imageFoo.alpha = 0.3; //むむ、セミコロンが!
self.imageBar.alpha = 0.3;
self.imageFoo.alpha = 0.3 //取りましたよ
self.imageBar.alpha = 0.3


vertical_whitespace

2行以上、空の行がつづく時に出ます。
意図的に2行以上空けることはないので、これもうっかりですね。気がついていませんでした。
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    } 


    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return notes.count
    }


trailing_comma

配列の最後の要素の後ろにカンマをつけるか否かです。
これは何というか、コーディングの流派というか、どっちもアリですし、だからこそSwiftの文法では両方OKになっているのだと思いますが、SwiftLintのデフォルトではカンマなしのようです。ここはSwiftLintに従うことにします。
        accidentials = [
            "picker_label_none",
            "picker_label_natural",
            "picker_label_sharp",
            "picker_label_flat", //←ここのカンマ
        ]
        accidentials = [
            "picker_label_none",
            "picker_label_natural",
            "picker_label_sharp",
            "picker_label_flat" //←取りましたよ

        ]


colon

コロンの後には空白を1個入れましょう、ですね。はい、その通りですね。
let hogeRequest:HogeRequest = HogeRequest()
let hogeRequest: HogeRequest = HogeRequest()


unused_closure_parameter

クロージャの使っていないパラメータをアンダースコアで省略すべきと。ふむ。
        UIView.animate(withDuration: TimeInterval(CGFloat(0.3)),
                       animations: { () -> Void in
                        pickerView?.center = offScreenCenter
        },
                       completion: { (Bool) -> Void in
                        pickerView?.removeFromSuperview()
        })
        UIView.animate(withDuration: TimeInt
erval(CGFloat(0.3)),
                       animations: { () -> Void in
                        pickerView?.center = offScreenCenter
        },
                       completion: { (_) -> Void in //←Boolをアンダースコアに変更
                        pickerView?.removeFromSuperview()
        })


weak_delegate

delegateパターンでは、循環参照によるメモリリークを避けるために弱参照にしなさいとのこと。
weakをつければいいのですが、それだけだと、"'weak' may only be applied to class and class-bound protocol types, not 'xxxDelegate'"のエラーが出るので、プロトコルにclassキーワードを追加して、クラス専用プロトコルにします。
protocol FooViewControllerDelegate {
    func fooViewDidDissmissed()
}

class FooViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {

    var delegate: FooViewControllerDelegate? //←強参照
    var selectedPractice: Practice? //選択されているPractice
protocol FooViewControllerDelegate: class { //←クラス専用プロトコル
    func fooViewDidDissmissed()
}

class FooViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {

    weak var delegate: FooViewControllerDelegate? //←弱参照にする
    var selectedPractice: Practice? //選択されているPractice


control_statement

これもまたObjective-C的うっかりです。
            switch (noteLanguage) { //←あれ、カッコが!
            case .english:
                name = "E♭"
            case .italian:
                name = "Mi♭"
            /* 省略 */
            case .japanese:
                name = "E♭"
            }
            switch noteLanguage { //←消しました
            case .english:
                name = "E♭"
            case .italian:
                name = "Mi♭"
            /* 省略 */
            case .japanese:
                name = "E♭"
            }


redundant_optional_initialization

オプショナル型の初期値はnilなので、わざわざnilで初期化する必要はないよ、です。
class HogeViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    var finishTime: Date? = nil //←nilで初期化は冗長
class HogeViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    var finishTime: Date? //←初期化を消しました


syntactic_sugar

いろんなケースがあるのかもしれませんが、ここではArray<hoge>()のシンタックス・シュガーである、[hoge]を使いなさいとのこと。

どうでもいいことですけど、syntactic_sugarなので「シンタクティック・シュガー」なんじゃないかなぁと思って辞書を引いたら、syntacticの訳に「シンタックスの」がありますね。syntaxの形容詞がsyntacticらしいです。ひょっとしてシンタックス・シュガーって和製英語だったりして?

    var transposingInstrumentKeys = Array<(TransposingInstrumentKey, String)>() //Arrayですね
    var transposingInstrumentKeys: [(TransposingInstrumentKey, String)] = [] //←配列っぽくね


unused_enumerated

enumeratedで変数が使われてないから、`.indices`とか使ってね、です。

こんな感じでenumeratedで回したけど、結局変数が使われないときにワーニングが出ます。これはコンパイラによるワーニングです。
"Immutable value 'h' was never used; consider replacing with '_' or removing it"
for (i, h) in hoge.enumerated() {
    /* 省略 */
    hoge[i] = piyo
    /* 省略 */
}

じゃあ、アンダースコアにすればいいかと修正すると、Lintがunused_enumeratedを検出します。
for (i, _) in hoge.enumerated() { // ←hをアンダースコアに
    /* 省略 */
    hoge[i] = piyo
    /* 省略 */
}

なるほどそりゃそうだ、です。
for i in hoge.indices { // ←ずぼらせずに、indicesを使いなさい
    /* 省略 */
    hoge[i] = piyo
    /* 省略 */
}


implicit_getter

コンピューテッドプロパティにおいて、セッタが不要なケースではgetキーワードが省略可能です。省略可能なものは省略してスッキリさせときや、です。
    var hoge: Int {
        get {
            return fuga + 10
        }
    }

もちろん、get{}を削除します。
    var hoge: Int {
        return fuga + 10
    }


まだまだ続くよ。「凛としてSwift (血闘編ノ参)」につづく。


♪♪♪


こちらもどうぞ。
凛としてSwift
凛としてSwift (血闘編ノ壱)
凛としてSwift (血闘編ノ参)
帰ってきた凛としてSwift(くるくる編)