关于ios:快速的自定义警报(UIAlertView)


Custom Alert (UIAlertView) with swift

如何使用Swift创建自定义警报? 我尝试翻译来自Objective c的指南,但加载全屏布局

为了容易,我可以加载具有透明背景的新布局,我尝试这样做:

1
2
3
4
5
6
7
8
9
10
    listaalertviewcontroller.view.backgroundColor = UIColor.clearColor()
    let purple = UIColor.purpleColor() // 1.0 alpha
    let semi = purple.colorWithAlphaComponent(0.5)

    listaalertviewcontroller.view.backgroundColor = semi


    presentingViewController.modalPresentationStyle = UIModalPresentationStyle.CurrentContext

    self.presentViewController(listaalertviewcontroller, animated: true, completion: nil)

在动画中它是透明的,但是当动画结束时它是不透明的...而我在视图中关闭了不透明选项...我在做什么错?


在Swift 5和Xcode 10中经过测试的代码

如何制作自己的自定义提醒

我想做类似的事情。首先,不推荐使用UIAlertView,而推荐使用UIAlertController。请参阅以下答案以显示警报的标准方法:

  • 如何在Swift中创建UIAlertView?

而且UIAlertViewUIAlertController都不允许太多自定义。一种选择是使用一些第三方代码。但是,我发现通过显示另一个视图控制器模式来创建自己的Alert并不困难。

这里的例子只是一个概念证明。您可以根据需要设计警报。

enter image description here

故事板

您应该有两个View Controller。您的第二个视图控制器将成为您的警报。将类名称设置为AlertViewContoller,将情节提要ID设置为alert。 (这两个名称都是我们在下面的代码中定义的名称,没什么特别的。如果需要,可以先添加代码。如果先添加代码,实际上可能会更容易。)

enter image description here

将根视图的背景色(在"警报视图控制器"中)设置为清除(或半透明的黑色对于警报而言是不错的选择)。添加另一个UIView并以约束为中心。将其用作警报背景,然后将所需的内容放入其中。对于我的示例,我添加了UIButton

enter image description here

ViewController.swift

1
2
3
4
5
6
7
8
9
10
11
12
import UIKit
class ViewController: UIViewController {

    @IBAction func showAlertButtonTapped(_ sender: UIButton) {

        let storyboard = UIStoryboard(name:"Main", bundle: nil)
        let myAlert = storyboard.instantiateViewController(withIdentifier:"alert")
        myAlert.modalPresentationStyle = UIModalPresentationStyle.overCurrentContext
        myAlert.modalTransitionStyle = UIModalTransitionStyle.crossDissolve
        self.present(myAlert, animated: true, completion: nil)
    }
}

AlertViewController.swift

1
2
3
4
5
6
7
import UIKit
class AlertViewController: UIViewController {

    @IBAction func dismissButtonTapped(_ sender: UIButton) {
        self.dismiss(animated: true, completion: nil)
    }
}

不要忘记挂上插座。

您可以将onTouchUp事件侦听器添加到背景视图中,以在用户在其外部单击时关闭弹出窗口。

而已。您应该能够发出现在可以想象的任何警报。无需第三方代码。

这是我制作的另一个自定义提醒。仍然很难看,但是它显示了您可以做的更多事情。

enter image description here

其他选择

但是,有时不需要重新发明轮子。第三方项目SDCAlertView(MIT许可证)给我留下了深刻的印象。它是用Swift编写的,但是您也可以将它用于Objective-C项目。它提供了广泛的可定制性。


这是Swift 3代码。非常感谢@Suragch提供了一种很棒的方法来创建自定义AlertView。

ViewController.swift

1
2
3
4
5
6
7
8
9
10
11
12
import UIKit
class ViewController: UIViewController {

@IBAction func showAlertButtonTapped(sender: UIButton) {

        let storyboard = UIStoryboard(name:"Main", bundle: nil)
        let myAlert = storyboard.instantiateViewController(withIdentifier:"storyboardID")
        myAlert.modalPresentationStyle = UIModalPresentationStyle.overCurrentContext
        myAlert.modalTransitionStyle = UIModalTransitionStyle.crossDissolve
        self.present(myAlert, animated: true, completion: nil)

}

AlertViewController.swift

1
2
3
4
5
6
7
import UIKit
class AlertViewController: UIViewController {

    @IBAction func dismissButtonTapped(sender: UIButton) {
        self.dismiss(animated: true, completion: nil)
    }
}

为了使其更加有趣或在iOS中设置默认效果,您可以添加VisualEffectView或将主UIView的颜色更改为深色,并将其alpha设置为70%。我喜欢第二种方法,因为模糊效果不如使用70 Alpha的视图平滑。

使用VisualEffectView的效果:

enter image description here

使用带有70 Alpha的UIView的效果:

enter image description here


Swift 4中的自定义Alert UIView类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import UIKit


    class Dialouge: UIView {
    @IBOutlet weak var lblTitle: UILabel!
    @IBOutlet weak var lblDescription: UILabel!
    @IBOutlet weak var btnLeft: UIButton!
    @IBOutlet weak var btnRight: UIButton!
    @IBOutlet weak var viewBg: UIButton!

    var leftAction  = {}
    var rightAction  = {}


    override func draw(_ rect: CGRect)
    {

        self.btnRight.layer.cornerRadius = self.btnRight.frame.height/2
        self.btnLeft.layer.cornerRadius = self.btnLeft.frame.height/2
        self.btnLeft.layer.borderWidth = 1.0
        self.btnLeft.layer.borderColor = #colorLiteral(red: 0.267678082, green: 0.2990377247, blue: 0.7881471515, alpha: 1)
    }
    @IBAction func leftAction(_ sender: Any) {

        leftAction()
    }

    @IBAction func rightAction(_ sender: Any) {
        rightAction()
    }
    @IBAction func bgTapped(_ sender: Any) {
        self.removeFromSuperview()
    }
    }

强文本
##使用Tabbar的自定义警报。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
    let custView = Bundle.main.loadNibNamed("Dialouge", owner: self, options:
     nil)![0] as? Dialouge
        custView?.lblDescription.text ="Are you sure you want to delete post?"
        custView?.lblTitle.text ="Delete Post"
        custView?.btnLeft.setTitle("Yes", for: .normal)
        custView?.btnRight.setTitle("No", for: .normal)
        custView?.leftAction = {
            self.deletePost(postId: self.curr_post.id,completion: {
                custView?.removeFromSuperview()
            })
        }
        custView?.rightAction = {
            custView?.removeFromSuperview()
        }
        if let tbc = self.parentt?.tabBarController {
            custView?.frame = tbc.view.frame
            DispatchQueue.main.async {
                tbc.view.addSubview(custView!)
            }
        }else if let tbc = self.parView?.parenttprof {
            custView?.frame = tbc.view.frame
            DispatchQueue.main.async {
                tbc.view.addSubview(custView!)
            }
        }
        else
        {
            custView?.frame = self.parView?.view.frame ?? CGRect.zero
            DispatchQueue.main.async {
                self.parView?.view.addSubview(custView!)
            }
            }

如今,警报仅仅是一个简单的呈现视图控制器。您可以编写一个呈现的视图控制器,其行为类似于警报-即它弹出到屏幕上并使背后的任何内容变暗-但它是您的视图控制器,您可以随意提供任何喜欢的界面。

为使您入门,我编写了一个github项目,您可以下载并运行该项目,并对其进行修改以适合您的实际需求。

我将展示代码的关键部分。"警报"视图控制器在其初始值设定项中将其自身的模式表示样式设置为custom并设置一个过渡委托:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class CustomAlertViewController : UIViewController {
    let transitioner = CAVTransitioner()

    override init(nibName: String?, bundle: Bundle?) {
        super.init(nibName: nibName, bundle: bundle)
        self.modalPresentationStyle = .custom
        self.transitioningDelegate = self.transitioner
    }

    convenience init() {
        self.init(nibName:nil, bundle:nil)
    }

    required init?(coder: NSCoder) {
        fatalError("NSCoding not supported")
    }
}

所有工作均由过渡的代表完成:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
class CAVTransitioner : NSObject, UIViewControllerTransitioningDelegate {
    func presentationController(
        forPresented presented: UIViewController,
        presenting: UIViewController?,
        source: UIViewController)
        -> UIPresentationController? {
            return MyPresentationController(
                presentedViewController: presented, presenting: presenting)
    }
}

class MyPresentationController : UIPresentationController {

    func decorateView(_ v:UIView) {
        // iOS 8 doesn't have this
//        v.layer.borderColor = UIColor.blue.cgColor
//        v.layer.borderWidth = 2
        v.layer.cornerRadius = 8

        let m1 = UIInterpolatingMotionEffect(
            keyPath:"center.x", type:.tiltAlongHorizontalAxis)
        m1.maximumRelativeValue = 10.0
        m1.minimumRelativeValue = -10.0
        let m2 = UIInterpolatingMotionEffect(
            keyPath:"center.y", type:.tiltAlongVerticalAxis)
        m2.maximumRelativeValue = 10.0
        m2.minimumRelativeValue = -10.0
        let g = UIMotionEffectGroup()
        g.motionEffects = [m1,m2]
        v.addMotionEffect(g)
    }

    override func presentationTransitionWillBegin() {
        self.decorateView(self.presentedView!)
        let vc = self.presentingViewController
        let v = vc.view!
        let con = self.containerView!
        let shadow = UIView(frame:con.bounds)
        shadow.backgroundColor = UIColor(white:0, alpha:0.4)
        shadow.alpha = 0
        con.insertSubview(shadow, at: 0)
        shadow.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        let tc = vc.transitionCoordinator!
        tc.animate(alongsideTransition: { _ in
            shadow.alpha = 1
        }) { _ in
            v.tintAdjustmentMode = .dimmed
        }
    }

    override func dismissalTransitionWillBegin() {
        let vc = self.presentingViewController
        let v = vc.view!
        let con = self.containerView!
        let shadow = con.subviews[0]
        let tc = vc.transitionCoordinator!
        tc.animate(alongsideTransition: { _ in
            shadow.alpha = 0
        }) { _ in
            v.tintAdjustmentMode = .automatic
        }
    }

    override var frameOfPresentedViewInContainerView : CGRect {
        // we want to center the presented view at its"native" size
        // I can think of a lot of ways to do this,
        // but here we just assume that it *is* its native size
        let v = self.presentedView!
        let con = self.containerView!
        v.center = CGPoint(x: con.bounds.midX, y: con.bounds.midY)
        return v.frame.integral
    }

    override func containerViewWillLayoutSubviews() {
        // deal with future rotation
        // again, I can think of more than one approach
        let v = self.presentedView!
        v.autoresizingMask = [
            .flexibleTopMargin, .flexibleBottomMargin,
            .flexibleLeftMargin, .flexibleRightMargin
        ]
        v.translatesAutoresizingMaskIntoConstraints = true
    }

}

extension CAVTransitioner { // UIViewControllerTransitioningDelegate
    func animationController(
        forPresented presented:UIViewController,
        presenting: UIViewController,
        source: UIViewController)
        -> UIViewControllerAnimatedTransitioning? {
            return self
    }

    func animationController(
        forDismissed dismissed: UIViewController)
        -> UIViewControllerAnimatedTransitioning? {
            return self
    }
}

extension CAVTransitioner : UIViewControllerAnimatedTransitioning {
    func transitionDuration(
        using transitionContext: UIViewControllerContextTransitioning?)
        -> TimeInterval {
            return 0.25
    }

    func animateTransition(
        using transitionContext: UIViewControllerContextTransitioning) {

        let con = transitionContext.containerView

        let v1 = transitionContext.view(forKey: .from)
        let v2 = transitionContext.view(forKey: .to)

        // we are using the same object (self) as animation controller
        // for both presentation and dismissal
        // so we have to distinguish the two cases

        if let v2 = v2 { // presenting
            con.addSubview(v2)
            let scale = CGAffineTransform(scaleX: 1.6, y: 1.6)
            v2.transform = scale
            v2.alpha = 0
            UIView.animate(withDuration: 0.25, animations: {
                v2.alpha = 1
                v2.transform = .identity
            }) { _ in
                transitionContext.completeTransition(true)
            }
        } else if let v1 = v1 { // dismissing
            UIView.animate(withDuration: 0.25, animations: {
                v1.alpha = 0
            }) { _ in
                transitionContext.completeTransition(true)
            }
        }

    }
}

它看起来像很多代码,我想是的,但是几乎全部都局限于一个类,它完全是样板。只需复制并粘贴。您所要做的就是编写"警报"视图控制器的内部界面和行为,为它提供按钮和文本以及您想要的任何其他内容,就像对其他任何视图控制器所做的一样。


使用https://github.com/shantaramk/Custom-Alert-View

毫不费力地实现这一点。只需按照以下步骤操作:

  • 向下拖动项目目录中的AlertView文件夹

  • 显示AlertView弹出窗口

    1
    2
    3
    4
    5
    6
    7
    func showUpdateProfilePopup(_ message: String) {
    let alertView = AlertView(title: AlertMessage.success, message: message, okButtonText: LocalizedStrings.okay, cancelButtonText:"") { (_, button) in
        if button == .other {
            self.navigationController?.popViewController(animated: true)
        }
    }
    alertView.show(animated: true)

    }