録音/録画ボタンの作り方〜角の丸いビュー

ここでは iOS や iPadOS でよく使われている、赤い丸の録音・録画ボタンの作り方を説明します。

なお、録音や録画そのものの方法については、この記事では扱いません。

1. ここで作るボタンについて

iOS/iPadOS ではカメラやボイスメモなど、録音または録画を行うためのボタンは次のような赤い丸ボタンが使われています。

iOS の録音/録画ボタン iOS の録音/録画ボタン

そして、それをタップして録音(または録画)が始まると、四角い赤いボタンに切り替わります。

iOS の録音/録画ボタン

多くの人が慣れているユーザーインターフェイスで直感的にわかりやすいですね。

ここでは、この丸いボタンの作り方、丸いボタンから四角いボタンへのアニメーション方法を説明します。 さらについでに、ボイスメモの UI では四角いビューのうちで上側の2つの角だけ丸くなっています。

角の丸い View

このように、丸くする角の部位を指定する方法についても説明します。

2. 録画ボタンの作り方

さて、最終的にはこんなボタンにしたいと思っています。

iOS の録音/録画ボタン

でも、こんな程度のボタンでも、意外といろんな物が混ざって出来ていますから、もっと単純な例からはじめて順番にひとつひとつバラバラにして考えていきます。

まずは、赤い丸ボタンの作り方からはじめましょう。

2-1. 丸いボタンの作り方

まずはオブジェクトライブラリからストーリーボードにボタン (Button) をひとつ配置します。

オブジェクトライブラリを開くボタンは Xcode 11 から、プラスアイコンに変更されています。キーボードショートカットは ⇧ Shift + ⌘ command + L です。

iOS 丸いボタンの作り方

ボタンは適当な場所に配置して、サイズを幅 50、高さ 50 位にしておきます。

ボタンのラベルの文字をクリアします。

iOS 丸いボタンの作り方

背景色 Background は System Red Color を選択します。

iOS 丸いボタンの作り方

ここまでの設定で、次のように単純な四角の赤いボタンになるはずです。

iOS 丸いボタンの作り方

次に Xcode のアシスタントエディタを開いて、ボタンのアウトレットとアクションを作成します。

Xcode の使い方に慣れていない方は、次のビデオを参考にしてください。左側のウィンドウからアシスタントエディタ内にコネクションを設定する場合は、 右ボタンでドラッグします。

iOS 丸いボタンの作り方

import UIKit

class ViewController: UIViewController {
    @IBOutlet weak var recordButton: UIButton!
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }

    @IBAction func recordButtonTapped(_ sender: Any) {
    }    
}

これで準備は完了です。この赤い四角ボタンを丸ボタンにしましょう。

ボタンの場所を (x,y) 座標で (75, 75)、サイズを幅 (width) 50、高さ (height) 50 とするために、 ボタン (recordButton) の frame プロパティに次の値をセットします。

recordButton.frame = CGRect(x:75, y:75, width:50, height:50)

正方形のボタンの一辺の半分の長さを角の半径とすることで、丸ボタンにします。上で幅と高さを 50 にしたので、 角の半径は 25 にします。このためにボタンの layercornerRadiusプロパティを 25 にします。

recordButton.layer.cornerRadius = 25.0
recordButton.layer.masksToBounds = true

layer 属性はそれぞれのビューが持っている CALayer (Core Animation レイヤー) ですが、 今回のように境界線を変更したときに、その境界線でレイヤーをクリップするには masksToBounds プロパティを true にします。

上記コードは viewDidAppear に書き、次のようになります。

  override func viewDidLoad() {
    super.viewDidLoad()
  }

  override func viewDidAppear(_ animated: Bool) {      
    recordButton.frame = CGRect(x:75,y:75,width:50,height:50)
    recordButton.layer.masksToBounds = true
    recordButton.layer.cornerRadius = 25.0
  }

この結果、次のようになります。

iOS 丸いボタンの作り方

ちなみに、ストーリーボード上では見た目は変わりません。シミュレータか実機で実行して確認してください。

コードではなく、ストリーボードからも設定可能

ここでは、あとでプログラムから各種プロパティを変更するのと、 プロパティ間に関連性がある (例えば角の半径は一辺の長さの半分など) のでコードでプロパティを変更するようにしました。

しかし、動的に操作する必要のない場合には、ストーリーボードから layer プロパティを設定することも可能です。

iOS 丸いボタンの作り方

なお、今回のコードはデバイスのオリエンテーション (縦横) の変更に対応していないので、Info.plistSupported interface orientationsPortrait (bottom home button) のみを設定してください。

iOS 丸いボタンの作り方

2-2. 丸ボタンから四角ボタンへのアニメーション

UI コンポーネントの基底クラスである UIView には、アニメーション機能が実装されています。 UIView のスタティックメソッド animate メソッドを次の形で呼び出すことで、現在の状態から遷移先の状態に徐々に移り変わります。

UIView.animate(withDuration: /*変化に必要な時間(秒)*/) {
  //遷移する先の状態
}

丸ボタンの時にタップすると少し小さな四角ボタンに移り変わり、四角ボタンの状態の時にタップすると丸ボタンに戻るようにするには、 現在の形状を保持するフラグを1つ用意して、次のように書きます。

import UIKit

class ViewController: UIViewController {

  var isRecording = false
  @IBOutlet weak var recordButton: UIButton!
  
  override func viewDidLoad() {
    super.viewDidLoad()
  }

  override func viewDidAppear(_ animated: Bool) {
    recordButton.frame = CGRect(x:75,y:75,width:50,height:50)
    recordButton.layer.masksToBounds = true
    recordButton.layer.cornerRadius = 25
  }

  @IBAction func recordButtonTapped(_ sender: Any) {
    if isRecording {
      UIView.animate(withDuration: 0.2) {
        self.recordButton.frame = CGRect(x:75,y:75,width:50,height:50)
        self.recordButton.layer.cornerRadius = 25
      }
    } else {
      UIView.animate(withDuration: 0.2) {
        self.recordButton.frame = CGRect(x:75,y:75,width:30,height:30)
        self.recordButton.layer.cornerRadius = 3.0
      }
    }
    isRecording = !isRecording
  }
}

これを実行すると、次のようになります。

確かに丸ボタンから四角ボタン、四角ボタンから丸ボタンに移り変わっていることが確認できますね。

ただ、これだけですと、サイズが小さくなるに伴って、ボタンが左上側に動いてしまっていますので、少しだけ frame の座標を補正する必要があります。

図形の中心を変えないようにするには、下の図からわかるように四角ボタンは 10px だけ右下にずらせば良いです。

iOS 丸いボタンの作り方

そこで、四角いボタンの時の座標、x と y に 10 足します。

    ...
    } else {
      UIView.animate(withDuration: 0.2) {
        //次の x, y 座標を変更
        self.recordButton.frame = CGRect(x:75+10,y:75+10,width:30,height:30)
        self.recordButton.layer.cornerRadius = 3.0
      }
  }

再度動作確認すると、次のようになります。

確かにその場にとどまったままで、形が変わっています。

2-3. 画面下に移動して黒っぽいバーの中央に配置

さて、丸ボタンから四角ボタンへのアニメーションの仕組みができたところで、場所を移動しましょう。

もともとの目標は画面の下側に次のようなボタンを作ることでした。

iOS の録音/録画ボタン

そこで、画面下側にオブジェクトライブラリから新たに UI View を配置します。 背景色は黒っぽく設定します (ここでは Custom Color から Lead を選びました)。

Constraint は左右端から 0、下端から 0 とし、高さ 100 とします。

iOS 丸いボタンの作り方

そして、この View (名前を Base View とします) の参照として baseView という名前でアウトレットを作成しておきます。

@IBOutlet weak var baseView: UIView!

次に上で作成した赤いボタンを、この Base View のサブビューとしてぶら下げます。

iOS 丸いボタンの作り方

これによって、赤いボタンの x , y 座標は Base View の左上端からの相対座標で設定できるようになります。

このように準備した上で、上記のコードを一旦整理して、次のようにしておきます。

import UIKit

class ViewController: UIViewController {

    var isRecording = false
    var w: CGFloat = 0
    var h: CGFloat = 0
    let d: CGFloat = 50
    let l: CGFloat = 28
    @IBOutlet weak var recordButton: UIButton!
    @IBOutlet weak var baseView: UIView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func viewDidAppear(_ animated: Bool) {
        w = baseView.frame.size.width
        h = baseView.frame.size.height

        initRoundCorners()
        showStartButton()
    }

    @IBAction func recordButtonTapped(_ sender: Any) {
        if isRecording {
            UIView.animate(withDuration: 0.2) {
                self.showStartButton()
            }
        } else {
            UIView.animate(withDuration: 0.2) {
                self.showStopButton()
            }
        }
        isRecording = !isRecording
    }

    func initRoundCorners(){
        recordButton.layer.masksToBounds = true
    }
    
    func showStartButton() {
        recordButton.frame = CGRect(x:(w-d)/2,y:(h-d)/2,width:d,height:d)
        recordButton.layer.cornerRadius = d/2
    }
    
    func showStopButton() {
        recordButton.frame = CGRect(x:(w-l)/2,y:(h-l)/2,width:l,height:l)
        recordButton.layer.cornerRadius = 3.0
    }
    
}

だいぶ変わったように見えるかもしれませんが、定数をコードの上でまとめて書いて、 その他なるべくハードコードを減らすようにしただけです。考え方は変わりません。

この結果、次の画面のように画面下側の黒っぽいビューの中でボタンが、丸から四角にアニメーションします。

iOS 丸いボタンの作り方

だいぶ、完成に近づきました。

2-4. 二重丸ボタンの仕組み

二重丸ボタンにするために、赤いボタンの外側に白い枠線を描きます。

白い枠線を描くためにUIView をさらに2つ配置しました。1つはサイズ縦横 62 の白色の View。これを outerCircle という名前で参照します (アウトレットを作ります)。 そしてもう1つは縦横 58 で Base View と同じ色の黒っぽい View。これを innerCircle という名前で参照します。

iOS 丸いボタンの作り方

それぞれ、半径31、29 の円形のビューにして、赤いボタンの下側に配置します。これによって、少し大きい白色の枠線が見え、次に黒っぽい色、そして赤いボタンという風に重なります。

initRoundCorners 関数内で、マスク、色、角の半径を指定します。これらは動かないので、サイズと位置はストーリーボードの Constraint で Base View の中央に静的に配置しています。

  @IBOutlet weak var outerCircle: UIView!
  @IBOutlet weak var innerCircle: UIView!
  ...

  func initRoundCorners(){
    recordButton.layer.masksToBounds = true

    outerCircle.layer.masksToBounds = true
    outerCircle.layer.cornerRadius = 31
    outerCircle.backgroundColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)

    innerCircle.layer.masksToBounds = true
    innerCircle.layer.cornerRadius = 29
    innerCircle.backgroundColor = #colorLiteral(red: 0.1298420429, green: 0.1298461258, blue: 0.1298439503, alpha: 1)
  }

以上で、次のような動作が実現できました。

このような赤い丸いボタンがあれば、直感的に「何か録音でもしそうだな」とわかりますし、 四角い赤いボタンになれば「もう一度押せば停止するだろう」とわかります。UI の大事さが改めて感じられますね。

3. 部分的に角を丸くする方法

最後に Base View の左上の角と、右上の角を丸くしましょう。実際に iOS 付属の Voice Memo でも同じように丸みがついています。

ここまで角を丸くして、四角いビューを丸いビューとして利用する例をみてきました。そのときは layer プロパティの cornerRadius プロパティを設定しました。

部分的に丸みをつける角を指定するには、cornerRadius に加え、 maskedCorners プロパティを設定します。

baseView.layer.masksToBounds = true
baseView.layer.cornerRadius = 10
baseView.layer.maskedCorners = [.layerMaxXMinYCorner, .layerMinXMinYCorner]

以上で、最終的に次のようなビューが作成できました。

iOS の録音/録画ボタン

4. まとめ

おさらいすると、ポイントは次のようになります。

  • UIView の layer プロパティで cornerRadius = 半径masksToBounds = true を設定することで円形のビューが作れる。(ストーリーボードでも静的に設定可能)
  • 丸いボタンと四角いボタンの移り変わりは、同じボタンの角の半径とサイズをアニメーションで切り替えることで実現できる。
  • 二重丸はサイズ違いのビューを重ねて作っている。(今回は外側の白い部分は縦横 62、内側の黒い部分の縦横 58、赤いボタン縦横 50)
  • ビューの角の一部だけ丸くするには maskedCorners で丸くする角を指定。

もう一度コード全体を書いておきます。うまく動かない時に参考にしてください。

ストーリーボードは次のようになりました。Base View の下のビューの設定順番は大事です。

iOS 丸いボタンの作り方

import UIKit

class ViewController: UIViewController {

  var isRecording = false
  var w: CGFloat = 0
  var h: CGFloat = 0
  let d: CGFloat = 50
  let l: CGFloat = 28
  @IBOutlet weak var recordButton: UIButton!
  @IBOutlet weak var baseView: UIView!
  @IBOutlet weak var outerCircle: UIView!
  @IBOutlet weak var innerCircle: UIView!
  
  override func viewDidLoad() {
    super.viewDidLoad()
  }

  override func viewDidAppear(_ animated: Bool) {
        w = baseView.frame.size.width
        h = baseView.frame.size.height
        initRoundCorners()
        showStartButton()
  }

  @IBAction func recordButtonTapped(_ sender: Any) {
    if isRecording {
      UIView.animate(withDuration: 0.2) {
        self.showStartButton()
      }
    } else {
      UIView.animate(withDuration: 0.2) {
        self.showStopButton()
      }
    }
    isRecording = !isRecording
  }

  func initRoundCorners(){
    recordButton.layer.masksToBounds = true

    baseView.layer.masksToBounds = true
    baseView.layer.cornerRadius = 10
    baseView.layer.maskedCorners = [.layerMaxXMinYCorner, .layerMinXMinYCorner]
    
    outerCircle.layer.masksToBounds = true
    outerCircle.layer.cornerRadius = 31
    outerCircle.backgroundColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)

    innerCircle.layer.masksToBounds = true
    innerCircle.layer.cornerRadius = 29
    innerCircle.backgroundColor = #colorLiteral(red: 0.1298420429, green: 0.1298461258, blue: 0.1298439503, alpha: 1)
  }
  
  func showStartButton() {
    recordButton.frame = CGRect(x:(w-d)/2,y:(h-d)/2,width:d,height:d)
    recordButton.layer.cornerRadius = d/2
  }
  
  func showStopButton() {
    recordButton.frame = CGRect(x:(w-l)/2,y:(h-l)/2,width:l,height:l)
    recordButton.layer.cornerRadius = 3.0
  }
  
}

以上、iOS や iPadOS で録音や録画でよく使われる赤い丸ボタンの作り方を説明しました。

iOS/iPadOS では UI の重要さが初めから認識されていて、 CALayer (Core Animation レイヤー) というレイヤーが初めから全てのビューに組み込まれています。

これを最大限に利用することによって、UI の表現豊かな iPhone アプリを開発することが可能になります。

ここまでお読みいただき、誠にありがとうございます。SNS 等でこの記事をシェアしていただけますと、大変励みになります。どうぞよろしくお願いします。

© 2024 Swift による iOS 開発入門