Swiftで音楽再生を止めないで、効果音を出す

どんなアプリでもボタンを押したらバックグラウンドの音楽プレイヤーが止まってしまったら不快だ。
ということで、以下のコードを音楽を再生する前に実行しておけば、効果音を流す際に音楽プレーヤーの音を止めずに、 効果音を再生することができる。

do {
    let audioSession = AVAudioSession.sharedInstance()
    try audioSession.setCategory(AVAudioSessionCategoryAmbient)
    try audioSession.setActive(true)
} catch let error {
            print(error)
}

しかし、このコードを実行したとしても、
音声ファイルは「.caf(Core Audio File)」でないとダメなことには注意しなければならない。

Swiftでランダムな文字列を取得する

Swiftでランダムな文字列を取得したいことがあったので調べてみた。 すると以下の様な方法が見つかった。 参考:

【Swift2.x】指定した長さのランダムな文字列を出力する - Qiita

func getRandomStringWithLength(length: Int) -> String {

        let alphabet = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
        let upperBound = UInt32(alphabet.characters.count)

        return String((0..<length).map { _ -> Character in
            return alphabet[alphabet.startIndex.advancedBy(Int(arc4random_uniform(upperBound)))]
  })
}

学び

Range型もmapを実装している。 なんとなはなしに、配列だけの関数だと思っていた。

iOS, Swiftでのアニメーションについて(2)

今回は、以下のUIViewのクラスメソッドを試してみました。 iOS4の頃からあるものの、僕は使ったことがありませんでした。

class func transitionWithView(_ view: UIView,
                     duration duration: NSTimeInterval,
                      options options: UIViewAnimationOptions,
                   animations animations: (() -> Void)?,
                   completion completion: ((Bool) -> Void)?)

公式ドキュメントに、

Creates a transition animation for the specified container view.

とあるように、「指定のコンテナビュー(UIView)の遷移・変化アニメーションを作る」とある。

optionsに指定できるアニメーションはなかなか豊富で以下の種類がある。

public static var TransitionNone: UIViewAnimationOptions { get } // default
public static var TransitionFlipFromLeft: UIViewAnimationOptions { get } // 左回転
public static var TransitionFlipFromRight: UIViewAnimationOptions { get } //右回転
public static var TransitionCurlUp: UIViewAnimationOptions { get }  //上にめくる
public static var TransitionCurlDown: UIViewAnimationOptions { get } // 下にめくる
public static var TransitionCrossDissolve: UIViewAnimationOptions { get }  //  クロスフェード
public static var TransitionFlipFromTop: UIViewAnimationOptions { get } // 上回転
public static var TransitionFlipFromBottom: UIViewAnimationOptions { get } // 下回転

最初は、animationsのブロックの中になにを書いたらいいのか分からなかったが、 アニメーションの過程で行われる、 ビューの追加、削除などを書く。 公式のドキュメントのサンプルにも下のように書かれている。

// toViewは、アニメーションの過程でContainerViewに追加されるUIVIew
// fromViewは、アニメーションの過程でContainerViewから削除されるUIView
[UIView transitionWithView:containerView
           duration:0.2
           options:UIViewAnimationOptionTransitionFlipFromLeft
           animations:^{ [fromView removeFromSuperview]; [containerView addSubview:toView]; }
           completion:NULL];

これを応用すると、以下の具合にUINavigationControllerの遷移アニメーションも変更することができる。 参考: [iOS] ナビゲーションコントローラのトランジションを手軽に差し替える方法 | Developers.IO

class MyNavigationController: UINavigationController {

    override func pushViewController(viewController: UIViewController, animated: Bool) {
        if animated {
            UIView.transitionWithView(self.view, duration: 0.4, options: .TransitionCurlDown, animations: { 
    // viewの追加処理
              super.pushViewController(viewController, animated: false) 
            }, completion: nil)
        }
        else {
            super.pushViewController(viewController, animated: false)
        }
    }
}

このように書くとUINavigationControllerでpushViewController関数を使った時に、 指定のUIViewAnimationOptionsでアニメーションさせることができる。

UIViewをUIImageにするExtension

UIViewを直接動かすのではなく、ダミー的にUIImageViewを動かすというようなことがやりたかったので、 UIViewをUIImageに変換するExtensionを探しました。

extension UIView {
    
    func toImage() -> UIImage? {

        UIGraphicsBeginImageContextWithOptions(self.frame.size, false, 0)
        if let context = UIGraphicsGetCurrentContext() {
            CGContextTranslateCTM(context, -self.frame.origin.x, -self.frame.origin.y)
            self.layer.renderInContext(context)
            let image = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()
            return image
        }
        return nil
    }
}

これで何でも、UIImageに変換できる・・・・はず。

iOS, Swiftでのアニメーションについて(1)

表現力をアップさせたいので、標準で用意されているアニメーション関数を見なおし。 初回は、お馴染みのUIViewの以下クラスメソッドを調べてみた。

extension UIView {
    public class func animateWithDuration(duration: NSTimeInterval, delay: NSTimeInterval, options: UIViewAnimationOptions, animations: () -> Void, completion: ((Bool) -> Void)?)
    public class func animateWithDuration(duration: NSTimeInterval, animations: () -> Void, completion: ((Bool) -> Void)?) 
    public class func animateWithDuration(duration: NSTimeInterval, animations: () -> Void) // delay = 0.0, options = 0, completion = NULL
    public class func animateWithDuration(duration: NSTimeInterval, delay: NSTimeInterval, usingSpringWithDamping dampingRatio: CGFloat, initialSpringVelocity velocity: CGFloat, options: UIViewAnimationOptions, animations: () -> Void, completion: ((Bool) -> Void)?)

お手軽にアニメーションさせるなら、このメソッドという印象。 設定できるoptionsは4種類とやや表現力には欠ける。

CurveEaseInOut // 最初と最後がゆっくり
CurveEaseIn // 最初がゆっくり
CurveEaseOut //最後がゆっくり
CurveLinear // 一定

この中でも、iOS7から使用できるようになった以下のメソッドは他とは毛並みが違う。 こちらは引数名からも予測できるように、簡単にバネの動きをつけることができる。

animateWithDuration(duration: NSTimeInterval, delay: NSTimeInterval, usingSpringWithDamping dampingRatio: CGFloat, initialSpringVelocity velocity: CGFloat, options: UIViewAnimationOptions, animations: () -> Void, completion: ((Bool) -> Void)?)

dampingRatioには0〜1の数字を渡す。 1に近づくほどバネらしさが失われ、0に近づくほどバネらしく弾むといったあんばい。 initialSpringVelocityは、 UIView.animateWithDurationのinitialSpringVelocityについて - Qiita にも書いてあるが、いまいちわからない。 しかし、0を代入しておいても期待の動きにはなるので、僕は盲目的に0を設定しています。

UIViewのアニメーションの基準点を変更する

UIViewのアニメーションの基準点はデフォルトでは中心だが、 アニメーションによっては基準点を端に寄せたりする必要が出てくる。 そんなときはUIViewのlayerプロパティ(CALayer)のanchorPointプロパティを変更する。

view.layer.anchorPoint=CGPoint(0, 0) // 左上
view.layer.anchorPoint=CGPoint(0, 1) // 左下
view.layer.anchorPoint=CGPoint(0.5, 0.5) // 中央(デフォルト)
view.layer.anchorPoint=CGPoint(1, 0) // 右上
view.layer.anchorPoint=CGPoint(1, 1) // 右下

しかし、アンカーポイントを変更すると元の位置とは異なった場所に配置されてしまう。 そのため、アンカーポイントを変更する際は、変更前のframeを保存しておくと、 変更前の位置と同じ位置にレイアウトできる。

let frame = view.frame
view.layer.anchorPoint = CGPoint(0.5, 1) // 中央下端
view.frame = frame

// 以下にアニメーション処理

UIAlertControllerで設定画面を開く

UIAlertControllerでアプリの設定画面を開きます。
こうすることで、ユーザーの手間を少しは省けます。 欲を言うと、「Facebookアプリの設定画面」や「設定アプリのトップ」へ移動させるURLSchemeが欲しかった。

let alertController = UIAlertController(
            title: "title",
            message: "message",
            preferredStyle: UIAlertControllerStyle.Alert
        )
        alertController.addAction(UIAlertAction(title: NSLocalizedString("キャンセル", comment: "キャンセル"), style: .Cancel, handler: nil))
        alertController.addAction(UIAlertAction(title: NSLocalizedString("設定画面へ", comment: ""), style: .Default, handler: { (action:UIAlertAction) -> Void in
            // 設定画面を開く
            if let url = NSURL(string:UIApplicationOpenSettingsURLString) {
                UIApplication.sharedApplication().openURL(url)
            }
        }))
        self.presentViewController(alertController, animated: true, completion: nil)