NSAlertの使い方

## 概要
- SwiftUIでアラートといえば[alert\(\_:isPresented:actions:message:\)](https://developer.apple.com/documentation/swiftui/view/alert(_:ispresented:actions:message:)-6awwp)を使用しますが、細かい調整のためNSAlertを使いたい場面があり、そのときの忘備録です。

## OK

![](https://i.imgur.com/kXvaLhz.jpg)


```swift
Button {
    let alert = NSAlert()
    alert.messageText = "Title"
    alert.informativeText = "message"
    alert.addButton(withTitle: "OK")
    _ = alert.runModal()
} label: {
    Text("OK")
}
```

## OK / Cancel

![](https://i.imgur.com/ibbRGsA.jpg)
```swift
Button {
    let alert = NSAlert()
    alert.messageText = "Title"
    alert.informativeText = "message"
    alert.addButton(withTitle: "OK")
    alert.buttons[0].tag = NSApplication.ModalResponse.OK.rawValue
    alert.addButton(withTitle: "Cancel")
    alert.buttons[1].tag = NSApplication.ModalResponse.cancel.rawValue
    let result = alert.runModal()
    if result == .OK {
        print("tapped: OK")
    } else if result == .cancel {
        print("tapped: Cancel")
    } else {
        print("tapped: Unknown")
    }
} label: {
    Text("Cancel / OK")
}
```

## Delete / Cancel

![](https://i.imgur.com/qjZS03b.jpg)

- Deleteボタンには[hasDestructiveAction](https://developer.apple.com/documentation/appkit/nsbutton/3622469-hasdestructiveaction)を使用する

```swift
Button {
    let alert = NSAlert()
    alert.messageText = "Title"
    alert.informativeText = "message"
                    
    alert.addButton(withTitle: "Cancel")
    alert.buttons[0].tag = NSApplication.ModalResponse.cancel.rawValue
    
    alert.addButton(withTitle: "Delete")
    alert.buttons[1].hasDestructiveAction = true
    
    let result = alert.runModal()
    if result == .cancel {
        print("tapped: Cancel")
    } else if result == .alertSecondButtonReturn {
        print("tapped: Delete")
    } else {
        print("tapped: Unknown")
    }
} label: {
    Text("Cancel / Delete")
}
```

## 3つ以上のボタン

![](https://i.imgur.com/BXMxYpt.jpg)
```swift
Button {
    let alert = NSAlert()
    alert.messageText = "Title"
    alert.informativeText = "message"
    alert.addButton(withTitle: "First")
    alert.addButton(withTitle: "Second")
    alert.addButton(withTitle: "Third")
    let result = alert.runModal()
    
    switch result {
    case .alertFirstButtonReturn:
        print("tapped: First")
    case .alertSecondButtonReturn:
        print("tapped: Second")
    case .alertThirdButtonReturn:
        print("tapped: Third")
    default:
        print("tapped: Unknown")
    }
} label: {
    Text("First / Second / Third")
}
```

- またボタンが4つ以上ある場合は下記の見た目になる(macOS 13)

![](https://i.imgur.com/qyKHzcY.jpg)

## ボタンのキー設定
- ボタンが2つあるとき、デフォルトでは左のボタンにEsc、右のボタンにEnterが割り当てられているよう。
- またキーは以下のように設定できる。

```swift
alert.buttons[0].keyEquivalent = "\r"  // Enter
alert.buttons[0].keyEquivalent = "\u{1b}"  // Esc
```

## ボタン押下時にアラートを表示したままにする
- NSAlertダイアログのボタンを押下すると、ダイアログは終了してしまう。
- そこでtargetを設定すると表示したままにできる。
    - 参考: [Don't dismiss NSAlert when button is clicked](https://stackoverflow.com/questions/11786504/dont-dismiss-nsalert-when-button-is-clicked)
- またSwiftUIのViewはtargetに指定できないため、NSObjectを継承させたAlertManagerを定義してそこで処理を行っている。

![May-22-2023 12-36-58](https://i.imgur.com/YSn2rh3.gif)

```swift
struct ContentView: View {
    
    let alertManager = AlertManager()
    
    var body: some View {
        VStack {
            Button {
                alertManager.handleAlertButtonTapped()
            } label: {
                Text("First / Second / OK")
            }
        }
        .padding()
    }
}
```

```swift
class AlertManager: NSObject {
    
    func handleAlertButtonTapped() {
        let alert = NSAlert()
        alert.messageText = "Title"
        alert.informativeText = "message"
        alert.addButton(withTitle: "First")
        alert.buttons[0].target = self
        alert.buttons[0].action = #selector(self.handleFirstButtonTapped)
        alert.buttons[0].keyEquivalent = ""
        
        alert.addButton(withTitle: "Second")
        alert.buttons[1].target = self
        alert.buttons[1].action = #selector(self.handleSecondButtonTapped)
        
        alert.addButton(withTitle: "OK")
        alert.buttons[2].keyEquivalent = "\r"
        let result = alert.runModal()
        
        switch result {
        case .alertThirdButtonReturn:
            print("tapped: OK")
        default:
            print("tapped: Unknown")
        }
    }
    
    @objc func handleFirstButtonTapped() {
        print("handleFirstButtonTapped")
    }
    
    @objc func handleSecondButtonTapped() {
        print("handleSecondButtonTapped")
    }
}
```