1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

自作AlertViewを作ってみる

Last updated at Posted at 2016-12-18

はじめに

標準のUIAlertViewではなく、自分でカスタマイズしたAlertViewを表示したい時ってありますよね。ということで、実際に作ってみました。

方法1:UIViewを使って作る

UIViewに作成して、そこにLabelやButtonを設置する。
UIViewを保持しておいて、Buttonが押されたタイミングで親Viewから削除する。

UIView
    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を作成します。

MyWindowAlert
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()
    }
}

呼び出し元では。

ViewController
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して終わりにしたい。というわけでこんな感じなりました。

MyAlertViewController
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)
    }
}

呼び出し元では、

ViewController
@IBAction func showAlertVCPushed(_ sender: Any) {
    let vc = MyViewControllerAlert()
    vc.setAlert("My ViewController Alert")
    self.present(vc, animated: true, completion: nil)
}

できました。initとして値セットしてpresentで終わり。
後ろに控えるボタンなどにも触れることもできません。

まとめ

ViewControllerを使うことで、AlertViewっぽいものが作れました。
もっと良い方法などあれば、コメントいただけると嬉しいです。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?