はじめに
標準のUIAlertViewではなく、自分でカスタマイズしたAlertViewを表示したい時ってありますよね。ということで、実際に作ってみました。
方法1:UIViewを使って作る
UIViewに作成して、そこにLabelやButtonを設置する。
UIViewを保持しておいて、Buttonが押されたタイミングで親Viewから削除する。
func myAlertShow() {
let bound = self.view.bounds
let myView = UIView.init(frame: CGRect.init(x: bound.width/2 - 200, y: bound.height/2 - 100, width: 400, height: 200))
myView.layer.borderWidth = 3
myView.layer.borderColor = UIColor.black.cgColor
myView.layer.cornerRadius = 7
myView.backgroundColor = .white
let button = UIButton.init(frame: CGRect.init(x: 250, y: 130, width: 150, height: 70))
button.backgroundColor = .gray
button.layer.cornerRadius = 2.0;
button.layer.borderColor = UIColor.black.cgColor
button.layer.borderWidth = 1.0;
button.addTarget(self, action: #selector(close), for: .touchUpInside)
button.setTitle("OK", for: .normal)
let label = UILabel.init(frame: CGRect.init(x: 10, y: 10, width: 400, height: 100))
label.text = "myUIView"
label.numberOfLines = 0
myView.addSubview(button)
myView.addSubview(label)
self.alertView = myView
self.view.addSubview(myView)
}
func close() {
self.alertView?.removeFromSuperview()
}
この方法だと当たり前ですが、AlertViewの後ろに控えるボタンなどをタップすること出来てしまいます。
UIAlertViewみたいな使い方がしたいなら、更に全体を覆うようなUIViewを追加するなどが必要です。
方法2:UIWindowを使って作る
UIWindowを使ってAlertViewを作成します。
protocol MyWindowAlertProtocol: class {
func closeWindowAlert()
}
class MyWindowAlert: NSObject {
var window: UIWindow?
weak var alertProtocol: MyWindowAlertProtocol?
init (with comment: String, delegate: MyWindowAlertProtocol) {
super.init()
self.alertProtocol = delegate
let bound = UIScreen.main.bounds
let myWindow = UIWindow.init(frame: bound)
myWindow.backgroundColor = UIColor.black.withAlphaComponent(0.2)
let myView = UIView.init(frame: CGRect.init(x: bound.width/2 - 200, y: bound.height/2 - 100, width: 400, height: 200))
(中略)
label.numberOfLines = 0
myView.addSubview(button)
myView.addSubview(label)
myWindow.addSubview(myView)
self.window = myWindow
}
func show() {
self.window?.makeKeyAndVisible()
}
func close() {
self.window = nil
self.alertProtocol?.closeWindowAlert()
}
}
呼び出し元では。
var alert: MyWindowAlert?
@IBAction func showWindowAlertButtonPushed(_ sender: Any) {
self.alert = MyWindowAlert.init(with: "My Window Alert", delegate: self as MyWindowAlertProtocol)
self.alert?.show()
}
extension ViewController: MyWindowAlertProtocol {
func closeWindowAlert() {
self.alert = nil
}
}
先ほどと違って、AlertViewは表示された際に後ろに控えるボタンなどタップできなくなります。
ただし、UIWindowのインスタンスをどこかで保持しておかなければなりません。
例では、MyWindowAlertが保持し、その保持しているMyWindowAlertをViewControllerが保持しています。
なので削除する際には、delegateを使うなどでnilを代入します。
ちなみに、UIWindowはインスタンスにnilを入れることで解放されます。
保持して最後にnilを入れるって操作がなんか嫌だなと。
方法3:UIViewControllerを使って作る
UIAlertViewと同様にinitしてshowして終わりにしたい。というわけでこんな感じなりました。
class MyViewControllerAlert: UIViewController {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
convenience init() {
self.init(nibName: nil, bundle: nil)
}
func setAlert(_ comment: String) {
self.modalTransitionStyle = .crossDissolve
self.modalPresentationStyle = .overCurrentContext
self.view.backgroundColor = UIColor.black.withAlphaComponent(0.4)
let bound = self.view.bounds
let myView = UIView.init(frame: CGRect.init(x: bound.width/2 - 200, y: bound.height/2 - 100, width: 400, height: 200))
(中略)
label.numberOfLines = 0
myView.addSubview(button)
myView.addSubview(label)
self.view.addSubview(myView)
}
func close() {
for view in self.view.subviews {
view.removeFromSuperview()
}
self.dismiss(animated: true, completion: nil)
}
}
呼び出し元では、
@IBAction func showAlertVCPushed(_ sender: Any) {
let vc = MyViewControllerAlert()
vc.setAlert("My ViewController Alert")
self.present(vc, animated: true, completion: nil)
}
できました。initとして値セットしてpresentで終わり。
後ろに控えるボタンなどにも触れることもできません。
まとめ
ViewControllerを使うことで、AlertViewっぽいものが作れました。
もっと良い方法などあれば、コメントいただけると嬉しいです。