6.

Xcode でボタンを作りコードと連携する|IBAction と SwiftUI の Button

編集
この記事の要点
  • Storyboard ではボタンを配置 → Control キーを押しながらコードへドラッグして @IBAction を作る
  • 接続の種類は 2 つ。操作を受け取るなら @IBAction、見た目を変えるなら @IBOutlet
  • SwiftUI なら Button("ラベル") { 処理 } の 1 行で済む
  • ボタンが反応しないときは接続が切れている(丸が白い)か、制約が無くて画面外にある
  • UIKit の UIButton は iOS 15 以降 UIButton.Configuration で見た目を設定する

Storyboard で作る(UIKit)

1. ボタンを配置する

1. Main.storyboard を開く
2. 右上の「+」(Library / Cmd + Shift + L)から Button を探す
3. View Controller の画面上へドラッグする
4. ダブルクリックしてラベルを「送信」に変える
5. 画面下の Add New Constraints で位置の制約を付ける

制約(Auto Layout)を付けないと、実機の画面サイズによってはボタンが画面外に出ます。シミュレーターでは見えているのに実機で押せない、という症状の原因になります。

2. コードとつなぐ

1. 右上の「Adjust Editor Options」→ Assistant を選び、Storyboard と
   ViewController.swift を左右に並べる
2. ボタンを Control キーを押しながらコード側へドラッグする
3. 出てきたダイアログで
     Connection: Action     … タップされたときの処理
     Name:       tapSend
     Type:       UIButton   … sender をボタンとして受け取れる
     Event:      Touch Up Inside
4. Connect を押す
import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var messageLabel: UILabel!   // 表示を変えたい部品
    @IBOutlet weak var sendButton: UIButton!    // ボタン自身を操作したいとき

    @IBAction func tapSend(_ sender: UIButton) {
        messageLabel.text = "送信しました"
        sender.isEnabled = false                // 押したボタンを無効にする
    }
}
役割作るとき
@IBOutlet部品をコードから操作するConnection: Outlet
@IBAction部品の操作を受け取るConnection: Action

コードだけで作る

override func viewDidLoad() {
    super.viewDidLoad()

    let button = UIButton(type: .system)
    button.setTitle("送信", for: .normal)
    button.translatesAutoresizingMaskIntoConstraints = false   // 制約を自分で付ける
    view.addSubview(button)

    NSLayoutConstraint.activate([
        button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
        button.centerYAnchor.constraint(equalTo: view.centerYAnchor),
        button.heightAnchor.constraint(equalToConstant: 44),
    ])

    // 押されたときの処理をつなぐ
    button.addTarget(self, action: #selector(tapSend), for: .touchUpInside)

    // クロージャで書く(iOS 14 以降)
    button.addAction(UIAction { _ in print("押された") }, for: .touchUpInside)
}

@objc private func tapSend() {
    print("押された")
}

#selector で指定するメソッドには @objc が必要です。付け忘れるとビルドは通り、実行時に unrecognized selector sent to instance でクラッシュします。

見た目を整える

// iOS 15 以降: Configuration で指定する
var config = UIButton.Configuration.filled()
config.title = "送信"
config.image = UIImage(systemName: "paperplane")
config.imagePadding = 8
config.baseBackgroundColor = .systemBlue
config.cornerStyle = .medium
config.contentInsets = NSDirectionalEdgeInsets(top: 12, leading: 20,
                                               bottom: 12, trailing: 20)
let button = UIButton(configuration: config)

// 従来の書き方(iOS 14 以前)
button.setTitle("送信", for: .normal)
button.setTitleColor(.white, for: .normal)
button.backgroundColor = .systemBlue
button.layer.cornerRadius = 8

// 状態ごとに変える
button.setTitle("送信中…", for: .disabled)
button.isEnabled = false

タップできる領域は 44×44 ポイント以上にするのが Apple のヒューマンインターフェースガイドラインの推奨です。小さいと押しづらく、審査で指摘されることもあります。

SwiftUI の場合

import SwiftUI

struct ContentView: View {
    @State private var message = ""
    @State private var isSending = false

    var body: some View {
        VStack(spacing: 16) {
            Text(message)

            Button("送信") {
                message = "送信しました"
            }
            .buttonStyle(.borderedProminent)
            .disabled(isSending)

            // アイコン付き
            Button {
                isSending = true
            } label: {
                Label("送信", systemImage: "paperplane")
            }

            // 削除など取り消せない操作
            Button("削除", role: .destructive) { }
        }
        .padding()
    }
}

SwiftUI では接続の作業そのものが不要です。Storyboard のような「線がつながっていない」問題が起きません。

反応しないときの確認順

症状原因
押しても何も起きない接続が切れている。コード左端の丸が白い(黒く塗られていれば接続済み)
unrecognized selector sent to instanceメソッド名を変えたのに Storyboard 側の接続が古いまま。接続を消して繋ぎ直す
Unexpectedly found nil while unwrapping@IBOutlet が繋がっていない/viewDidLoad より前に触っている
ボタンが見えない・押せない制約が無い/他のビューに隠れている/親の isUserInteractionEnabled が false
2 回反応する同じメソッドに接続が二重に張られている

接続の状態は、Storyboard でボタンを右クリックすると一覧で確認できます。不要な接続は x を押して外してください。

関連

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. マップの追加方法
  2. テンプレートの種類一覧
  3. ファイルの役割一覧
  4. シミュレーターの画面の拡大/縮小をする方法
  5. スライダーの作成とカスタマイズ
  6. ボタンの作成とプログラムと連携
  7. ラベルの作成とプログラムと連携
  8. 【Xcode/Swift】ImageViewのContentMode一覧
  9. エラー一覧
  10. アプリを実機で起動させる方法
  11. ツールバーの設置とボタンの追加
  12. 画像の追加

最近更新/作成されたページ