MILLEN BOX

音楽好きの組み込みソフトエンジニアによるプログラミング(主にiOSアプリ開発)の勉強の記録

3作目のアプリ Advent Canon 2015をリリースしています!

更新の間が開きまくりです。
いろいろ書きたいことが溜まってきています...。

そんな中、前回の記事(アプリ道場 Advent Calendar 2015 9日目を担当して初めてQiitaに投稿した)でお知らせしたアプリが(とっくに)リリースされております。
ご連絡が遅くなって申し訳ありません!!

アプリの説明を...。

音楽と一緒に2015年クリスマスを祝いましょう。

Advent Canon 2015は音楽でクリスマスを祝うアプリ2015年度版です。
クリスマスが近づくにつれて再生できる演奏が増えていきます。
伴奏を含め6つの演奏を組み合わせることで1曲が完成します。
使用した曲はパッヘルベルのカノンです。
以下の日程で再生できる演奏が増えていきます。

・2015年11月29日
・2015年12月06日
・2015年12月13日
・2015年12月20日
・2015年12月24日

クリスマスのアイコンをタップすることで演奏が再生されます。
再度タップすると再生した演奏はミュートされます。
あなたにとって一番心地よい組み合わせを探してみて下さい。

よかったらダウンロードしてみて下さい!楽しいですよ!

それでは皆様良いクリスマスを!

アプリ道場 Advent Calendar 2015 9日目を担当して初めてQiitaに投稿した

お久しぶりです。またまた間が空いてしました。
前回の投稿後、新作アプリを作成していました。
そして昨日無事サブミットが完了しました!
突然新作を作成したきっかけはこれです。

qiita.com

私、これの9日目を担当しました。
記事は以下です。

qiita.com

タイトルや内容を見て頂いたら分かる通り、この為に1本クリスマスアプリを作成しました。 記事中にある通り、Advent Calendarの投稿ネタを考えて街を散策している時にネタが降ってきた感じです。
音声の再生という私にとっては新しいことにも挑戦しました。
「短期間でアプリを作成提出まで持っていきたい」とよく思っていましたので、今回の内容はとても挑戦しがいがありました。
何より憧れのQiitaへの投稿が出来たことへの達成感がすごい!

作成したアプリのGithubは以下にあげておきます。(例によってオープンソースです。)

▶︎GitHub - anthrgrnwrld/adventCanon

f:id:anthrgrnwrld:20151209205533j:plain

またアプリについては詳しく書こうと思っています。
私の投稿とソースが少しでも誰かの役に立ったり、刺激になっていたりすれば嬉しいなー。

UIViewの一部をUIImageとして切り取る方法 [swift2.1] [Context] [CGAffineTransform]

先日はUIImageを切り取る方法について投稿しました。

anthrgrnwrld.hatenablog.com

この記事中の最後でも注意点としても書きましたが、この方法だと 切り取る範囲についてはあくまでUIImageを対象として考えないといけない です。
UIImageはUIImageViewに対し拡大・縮小して表示しているケースが多いので、見たまんまの感覚でrectを指定すると、想定と異なった範囲の切り取り画像になってしまいます。

この問題の解決の為、以下のような方法を考えました。

  1. 対象のViewと切り取り範囲(Rect)を元にコンテキストを作成
  2. 1をUIImageに保存

上記発想の元作成したものについて今回は書きたいと思います。
出来上がったもののgif画像とGithubは以下です。

f:id:anthrgrnwrld:20151120080245g:plain

▶︎GitHub - anthrgrnwrld/clipView

参考リンクは以下です。

▶︎ iPhone アプリ研究会 UIViewの一部をUIImageとして取得する方法

自分ポイント1

切り取り関数のソースを以下に示します。
parameterとしては対象のViewと切り取りRectを指定し、Returnは切り取り後のUIImageとなります。
中の処理のポイントについては 自分ポイント2 にて説明します。

    /**
     対象のViewを指定したrectで切り取りUIImageとして取得する

     - parameter view:切り取り対象のview
     - parameter rect:切り取る座標と大きさ
     - returns: 切り取り結果を返す
    */
    func clipView(view: UIView?, rect: CGRect?) -> UIImage? {
        
        guard let targetView = view else {
            return nil
        }
        
        guard let frameRect = rect else {
            return nil
        }
        
        // ビットマップ画像のcontextを作成.
        UIGraphicsBeginImageContextWithOptions(frameRect.size, false, 0.0)
        let context = UIGraphicsGetCurrentContext()!
        
        //Affine変換
        let affineMoveLeftTop = CGAffineTransformMakeTranslation(-frameRect.origin.x, -frameRect.origin.y)
        CGContextConcatCTM(context, affineMoveLeftTop)
        
        // 対象のview内の描画をcontextに複写する.
        targetView.layer.renderInContext(context)
        
        // 現在のcontextのビットマップをUIImageとして取得.
        let clippedImage = UIGraphicsGetImageFromCurrentImageContext()
        
        // contextを閉じる.
        UIGraphicsEndImageContext()
        
        return clippedImage
    }

自分ポイント2

Parameterの切り取りRectを基にコンテキストを作成します。
コンテキストについてはスクリーンショットを保存する方法の記事で少し出てきました。

anthrgrnwrld.hatenablog.com

スクリーンショットの保存の時にはコンテキストに写す範囲として画面全体としていましたが、今回は必要範囲が決まっています。
そのような場合には以下の様な手順が必要になります。

  1. コンテキスト(一時保存場所?)の作成 → 切り取りサイズの指定
  2. 切り取り座標に従いAffine変換する → 切り取り位置の指定
  3. コンテキストに対象Viewを複写する
// ビットマップ画像のcontextを作成.
UIGraphicsBeginImageContextWithOptions(frameRect.size, false, 0.0)
let context = UIGraphicsGetCurrentContext()!
        
//Affine変換
let affineMoveLeftTop = CGAffineTransformMakeTranslation(-frameRect.origin.x, -frameRect.origin.y)
CGContextConcatCTM(context, affineMoveLeftTop)
        
// 対象のview内の描画をcontextに複写する.
targetView.layer.renderInContext(context)

そしてコンテキストのビットマップをUIImageとして保存します。

let clippedImage = UIGraphicsGetImageFromCurrentImageContext()

ちなみに UIGraphicsBeginImageContextWithOptions を今回使用しましたが、 UIGraphicsBeginImageContext という関数もあります。
しかし今回使用した UIGraphicsBeginImageContextWithOptions の方がRetinaディスプレイを考慮した作りとなっているため、通常はこちらを使用した方が良いと思います。
(参考)
▶︎ 自前で描画した内容がUIImageで ぼやける時の処置 | 秋山ブログ

自分ポイント3

セグコントロールが押下された時の動作のソースを貼っときます。

    /**
     SegControlが押下された時に呼ばれる
    */
    @IBAction func pressClipSegControl(sender: AnyObject) {
        
        var rect: CGRect?       //切り取るrect値格納用
        
        //セグコントロールとclipTypeを紐付け。そしてその値がnilになる場合(= Non Clip)には元のイメージを表示する
        let clipValues : [clipType?] = [nil, .type100x100]
        
        guard let clipValue = clipValues[clipSegControl.selectedSegmentIndex] else {
            imageView.image = UIImage(named: "mountain.jpg")
            return
        }
        
        //セグコントロールが.type100x100(= Clip(100x100))の時、それに従ったrectの値を入れる
        switch clipValue {
        case .type100x100:
            rect = CGRectMake(137, 284, 100, 100)
        }
        
        //imageViewからframeRectで切り取り、結果をUIImageで取得する
        guard let clippedImage = clipView(imageView, rect: rect) else {
            imageView.image = UIImage(named: "mountain.jpg")
            return
        }
        
        //切り取り結果を拡大表示
        imageView.image = clippedImage
        
        
    }

clipTypeはViewController.swiftの頭で以下のように定義しています。

    //clipTypeをenumで定義しておく(一個だけだが練習)
    enum clipType :Int {
        case type100x100
    }

これでUIImageを切り取る方法と比べると直感的に切り取ることが出来るようになりました。

Status Barの表示を消す方法 [swift2.1]

Status Barの表示をしたくないアプリが出てきましてその設定方法について。
Status Barというのは、iOS機器の画面上部に日付やバッテリー残量、電波強度などの情報を表示しているBarのことです。

Xcodeでプロジェクトファイルの中を覗いていると、「Status Bar Style」という項目に「Hide status bar」というチェックボックスがありました。
ここの部分のチェックを付けたり外したりしながら設定を弄ってみましたが効果なし(シミュレーター上)。
んん〜?と思って調べたところ、Info.plistについても弄る必要がある模様。

▶︎iOS7でステータスバーを非表示に…奮闘記\(^o^)/|杏z 学習帳

自分ポイント1

XcodeでInfo.plistファイルを選択し、View controller-based status bar appearance を追加しましょう。
設定は NO にします。

f:id:anthrgrnwrld:20151116074633p:plain

自分ポイント2

本記事の頭で説明しましたプロジェクトファイルの「Status Bar Style」→「Hide status bar」のチェックボックスを入れます。

f:id:anthrgrnwrld:20151116074823p:plain

これでStatus Barの表示がされなくなりました。

UIImageの一部を切り取る方法 [swift2.1]

UIImageの一部を切り取る方法について。

表示している写真に対し、セグメントコントロールでクリップする/しないを切り替えるアプリを作成しました。
f:id:anthrgrnwrld:20151115103321g:plain

githubは以下です。

▶︎GitHub - anthrgrnwrld/clipImage

参考にしたページは以下です。

▶︎UIImageの一部を切り抜く方法 | 目くじら日記

▶︎画像をトリミングしてUIImageViewにセットする - Qiita

▶︎SwiftでUIImageを回転、リサイズ、クリッピング、塗りつぶし - Qiita

自分ポイント1

下の記載がクリッピング用のメソッドです。
parameterとしては対象のUIImageと切り取りRectを指定し、Returnは切りとり後のUIImageとなります。

    /**
     UIImageを切り取る
     
     - parameter image:切り取り対象(UIImage)
     - parameter rect:切り取る大きさと座標位置(CGRect)
     - returns: 切り取り結果(UIImage)
    */
    func clipImage(image: UIImage?, rect: CGRect?) -> UIImage? {
        
        guard let originalImage = image else {
            return nil
        }
        
        guard let rct = rect else {
            return image
        }
        
        // ソース画像からCGImageを取り出し、指定された範囲を切り抜いたCGImageを生成
        let cripImageRef = CGImageCreateWithImageInRect(originalImage.CGImage, rct)
        
        guard let imgrf = cripImageRef else {
            return image
        }

        //生成したCGImageをUIImageとする
        let crippedImage = UIImage(CGImage: imgrf)
        
        return crippedImage
        
    }

自分ポイント2

自分ポイント1で再生した関数をセグコントロールをタップした時に呼び出します。

    /**
     Clip用のSegControlを押下した時
     */
    @IBAction func pressClipSegControl(sender: AnyObject) {
        
        var rect :CGRect?       //切り取るrect値格納用
        
        //セグコントロールとclipTypeを紐付け。そしてその値がnilになる場合(= Non Clip)には元のイメージを表示する
        let clipValues : [clipType?] = [nil, .type100x100]
        guard let clipValue = clipValues[clipSegControl.selectedSegmentIndex] else {
            setImage()
            return
        }

        //セグコントロールが.type100x100(= Clip(100x100))の時、それに従ったrectの値を入れる
        switch clipValue {
        case .type100x100:
            let origin = CGPoint(x: 100, y: 250)        //座標位置(四隅の左上)は(100,100)
            let size = CGSize(width: 100, height: 100)  //切り取りサイズは100x100
            rect = CGRect(origin: origin, size: size)   //originとsizeからrectを割り出す
        }
        
        //上で作成したrectに従って指定したUIImageを切り取る。今回きりとるUIImageはimageViewFromLibrary.image
        let clippedImage = clipImage(imageViewFromLibrary.image, rect: rect)    //clippedImageには切り取り結果が入る

        //clippedImageがnilの場合には元のイメージを表示する
        guard let clpImg = clippedImage else {
            setImage()
            return
        }
        
        //きりとったUIImageを表示する
        imageViewFromLibrary.image = clpImg
    }

clipTyeについてはenumで以下のように定義しておきます。

    enum clipType :Int {
        case type100x100
    }

注意点

今回の方法の場合、切り取り座標と切り取りサイズの指定はあくまでUIImageに対して指定してあげないといけません。
スクリーンの座標で考えると「思ってたんと違う」ということになってしまうので注意してください。

(関連記事を書きました!)

anthrgrnwrld.hatenablog.com

実行中の実機がどのRetinaのタイプか判断する方法 [swift2.1]

備忘録代わりに小ネタ記事を投稿します。

2015/11/15現在の最新iOSがサポートしている実機の解像度はNon Retina, Retina(2x), Retina(3x)の3種類存在します。

アプリが動いている実機のRetinaのタイプを知りたい時には、以下の方法で知ることができます。

let scale = UIScreen.mainScreen().scale 

上記scale内に1.0, 2.0, 3.0いずれかの数値が入ります。
そしてその値によってどのRetinaのタイプかを判断可能です。

実機の判断方法については色んな方法がありますが、今回の方法についても手段の一つとして覚えておきます。

関数の説明文を書こう Xcode7 対応版 [Xcode7.1]

また間が空きましたが久々の更新です。
ちょっとハマってしまったことがありまして間隔が空いてしまっています。
ハマるとブログに書きたいネタは増えるんだけど、ハマりの解消作業もしたい。というようなジレンマ中です。

今日もちょっと小ネタです。
以前関数の説明文についてこんな記事を書きました。

anthrgrnwrld.hatenablog.com

ルールに沿って関数ヘッダの説明文を書けば、すごくおしゃれで便利だよ〜という内容です。

で、時は流れまして、、、
本日、この記事の通りに関数の説明文を書いたんですが、option+Clickで関数の説明が思ったのと違う風になっていました。
具体的にはparameterやreturnsなどが分かれて表示されていたのが、Description内に全ての記述が記載されるようになってしまっていたのです。
ん〜??と思って調べたところ、やっぱりXcode7(Swift2?)で記述ルールに変更があったんですね。

qiita.com

自分ポイント

Xcode6までは

/**
説明

:param: a: 引数a
:param: b: 引数b
:returns: 返り値
*/

というような記載をしていました。
それがXcode7からは以下のような書き方に変更になった模様です。

/**
説明

- parameter a: 引数a
- parameter b: 引数b
- returns: 返り値
*/

また何とMarkdown記法にも対応しています!

具体的な関数を例として、以下に使用例を書いておきます。

    /**
    sizeとcenter座標の指定からorigin座標を算出する(_=rectを算出する_)関数
     
    - parameter size:算出するrectのsize
    - parameter center:算出するrectのcenter座標
    - returns: sizeとcenterから算出されたorigin座標を含めたrectを返す
    */
    func makeRectFromSizeAndCenter(size :CGSize, center :CGPoint) -> CGRect {
        
        let origin = CGPoint(x: center.x - (size.width) / 2, y: center.y - (size.height) / 2)   //sizeとcenterから座標位置を特定
        let rect = CGRectMake(origin.x, origin.y, size.width, size.height) //blurView用Rect
        
        return rect
    }

この書き方で今まで通り、関数の呼び出しの部分で option + Click すると、自分で追加した説明文が参照されるようになりました!
Markdown対応なので"=rectを算出する"の部分が斜字体になっているのもポイントです。

f:id:anthrgrnwrld:20151112075009g:plain

やっぱり説明文をちゃんと書いていると落ち着きますね。