自定义Swift UIAlertView

8

我正在尝试创建一个确认删除弹出窗口视图。由于我想要的设计与典型的UIAlertView弹出框风格非常不同,因此我决定创建一个自定义的ConfirmationViewController并触发它弹出。

这是典型UIAlertView的外观:

输入图像描述

而这是我希望我的弹出窗口视图所具有的外观:

输入图像描述

以下是我目前制作自定义ConfirmationViewController弹出窗口视图的方法:

let confirmationViewController = ConfirmationViewController()
confirmationViewController.delegate = self
confirmationViewController.setTitleLabel("Are you sure you want to remove \(firstName)?")
confirmationViewController.modalPresentationStyle = UIModalPresentationStyle.Popover
confirmationViewController.preferredContentSize = CGSizeMake(230, 130)

let popoverConfirmationViewController = confirmationViewController.popoverPresentationController
popoverConfirmationViewController?.permittedArrowDirections = UIPopoverArrowDirection(rawValue: 0)
popoverConfirmationViewController?.delegate = self
popoverConfirmationViewController?.sourceView = self.view
popoverConfirmationViewController?.sourceRect = CGRectMake(CGRectGetMidX(self.view.bounds), CGRectGetMidY(self.view.bounds),0,0)
presentViewController(
    confirmationViewController,
    animated: true,
    completion: nil)

当按下CANCELREMOVE按钮时,以下是我获取通知的方式:

extension UserProfileTableViewController: ConfirmationViewControllerDelegate {
    func cancelButtonPressed() {
        print("Cancel button pressed")
    }

    func confirmationButtonPressed(objectToDelete: AnyObject?) {
        print("Delete button pressed")
    }
}

然而,我喜欢使用UIAlertView的原因是,我可以硬编码想要在按下特定按钮时执行的操作,就像这样:

let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .Alert)

let cancelAction = UIAlertAction(title: "Cancel", style: .Default, handler: {(ACTION) in
    print("Perform cancel action")
})

let deleteAction = UIAlertAction(title: "Remove", style: .Destructive, handler: {(ACTION) in
    print("Perform delete action")
})

alertController.addAction(cancelAction)
alertController.addAction(deleteAction)

presentViewController(alertController, animated: true, completion: nil)

所以我的问题是,我如何创建一个完成处理程序(内联),以便当使用我的自定义ConfirmationViewController按下CANCEL或REMOVE按钮时,可以触发操作,就像我已经展示了如何使用UIAlertController完成操作一样,而不是我目前使用委托的方式?
答案是仅仅使用UIAlertController创建我正在寻找的自定义弹出框吗?如果是这样,我如何自定义它以达到我想要的程度?
提前感谢您并为长篇大论道歉 :)
附:这是我的ConfirmationViewController和ConfirmationViewControllerDelegate的样子:
protocol ConfirmationViewControllerDelegate {
    func cancelButtonPressed()
    func confirmationButtonPressed(objectToDelete: AnyObject?)
}

class ConfirmationViewController: UIViewController {
    var didSetupConstraints = false

    let titleLabel = UILabel.newAutoLayoutView()
    let buttonContainer = UIView.newAutoLayoutView()
    let cancelButton = ButtonWithPressingEffect.newAutoLayoutView()
    let confirmationButton = ButtonWithPressingEffect.newAutoLayoutView()

    var delegate: ConfirmationViewControllerDelegate?

    var objectToDelete: AnyObject?

    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = UIColor.whiteColor()

        titleLabel.numberOfLines = 0

        cancelButton.backgroundColor = UIColor.colorFromCode(0x7f7f7f)
        cancelButton.layer.cornerRadius = 5
        cancelButton.setAttributedTitle(NSMutableAttributedString(
            string: "CANCEL",
            attributes: [
                NSFontAttributeName: UIFont(name: "AvenirNextLTPro-Demi", size: 12)!,
                NSForegroundColorAttributeName: UIColor.whiteColor(),
                NSKernAttributeName: 0.2
            ]
        ), forState: UIControlState.Normal)
        cancelButton.addTarget(self, action: #selector(cancelButtonPressed), forControlEvents: .TouchUpInside)

        confirmationButton.backgroundColor = Application.redColor
        confirmationButton.layer.cornerRadius = 5
        confirmationButton.setAttributedTitle(NSMutableAttributedString(
            string: "REMOVE",
            attributes: [
                NSFontAttributeName: UIFont(name: "AvenirNextLTPro-Demi", size: 12)!,
                NSForegroundColorAttributeName: UIColor.whiteColor(),
                NSKernAttributeName: 0.2
            ]
        ), forState: UIControlState.Normal)
        confirmationButton.addTarget(self, action: #selector(confirmationButtonPresssed), forControlEvents: .TouchUpInside)

        view.addSubview(titleLabel)
        view.addSubview(buttonContainer)
        buttonContainer.addSubview(cancelButton)
        buttonContainer.addSubview(confirmationButton)
        updateViewConstraints()
    }

    func cancelButtonPressed() {
        delegate?.cancelButtonPressed()
        dismissViewControllerAnimated(false, completion: nil)
    }

    func confirmationButtonPresssed() {
        delegate?.confirmationButtonPressed(objectToDelete)
        dismissViewControllerAnimated(false, completion: nil)
    }

    func setTitleLabel(text: String) {
        let paragraphStyle = NSMutableParagraphStyle()
        paragraphStyle.alignment = NSTextAlignment.Center
        paragraphStyle.lineSpacing = 4.5
        titleLabel.attributedText = NSMutableAttributedString(
            string: text,
            attributes: [
                NSFontAttributeName: UIFont(name: "AvenirNextLTPro-Regular", size: 14)!,
                NSForegroundColorAttributeName: UIColor.colorFromCode(0x151515),
                NSKernAttributeName: 0.5,
                NSParagraphStyleAttributeName: paragraphStyle
            ]
        )
    }

    override func updateViewConstraints() {
        if !didSetupConstraints {
            titleLabel.autoPinEdgesToSuperviewEdgesWithInsets(UIEdgeInsets(top: 10, left: 10, bottom: 0, right: 10), excludingEdge: .Bottom)
            titleLabel.autoAlignAxisToSuperviewAxis(.Vertical)

            buttonContainer.autoPinEdge(.Top, toEdge: .Bottom, ofView: titleLabel, withOffset: 3)
            buttonContainer.autoAlignAxisToSuperviewAxis(.Vertical)
            buttonContainer.autoPinEdgeToSuperviewEdge(.Bottom, withInset: 10)

            let contactViews: NSArray = [cancelButton, confirmationButton]
            contactViews.autoDistributeViewsAlongAxis(.Horizontal, alignedTo: .Horizontal, withFixedSpacing: 7, insetSpacing: true, matchedSizes: false)

            cancelButton.autoPinEdgeToSuperviewEdge(.Top)
            cancelButton.autoPinEdgeToSuperviewEdge(.Bottom)
            cancelButton.autoSetDimensionsToSize(CGSize(width: 100, height: 50))

            confirmationButton.autoPinEdgeToSuperviewEdge(.Top)
            confirmationButton.autoPinEdgeToSuperviewEdge(.Bottom)
            confirmationButton.autoSetDimensionsToSize(CGSize(width: 100, height: 50))

            didSetupConstraints = true
        }

        super.updateViewConstraints()
    }
}

确认视图控制器类是否在您的控制之下并且可以进行编辑? - SeanCAtkinson
是的先生,我刚刚更新了我的代码,包括ConfirmationViewController @SeanCAtkinson。 - Thomas
1个回答

9
以下内容可以实现它。注意,还有很多改进的空间。例如,您可以为要删除的对象使用通用的“AnyObject”代替。如果您在内联传递闭包,则不一定需要传递它,因此可能可以将其删除。
您还可以使按钮更具可重用性,而不是硬编码为取消和删除,但现在我们偏离了主题 :)
class ConfirmViewController : UIViewController {
    var onCancel : (() -> Void)?
    var onConfirm : ((AnyObject?) -> Void)?

    var objectToDelete : AnyObject?

    func cancelButtonPressed() {
        // defered to ensure it is performed no matter what code path is taken
        defer {
            dismissViewControllerAnimated(false, completion: nil)
        }

        let onCancel = self.onCancel
        // deliberately set to nil just in case there is a self reference
        self.onCancel = nil
        guard let block = onCancel else { return }
        block()
    }

    func confirmationButtonPresssed() {
        // defered to ensure it is performed no matter what code path is taken
        defer {
            dismissViewControllerAnimated(false, completion: nil)
        }
        let onConfirm = self.onConfirm
        // deliberately set to nil just in case there is a self reference
        self.onConfirm = nil
        guard let block = onConfirm else { return }
        block(self.objectToDelete)
    }
}

let confirm = ConfirmViewController()
confirm.objectToDelete = NSObject()
confirm.onCancel = {
    // perform some action here
}
confirm.onConfirm = { objectToDelete in
    // delete your object here
}

我非常喜欢这种设计模式。使用委托模式而不是这种模式来处理视图,有什么特别的原因吗?@SeanCAtkinson - Thomas
1
这取决于具体的使用情况。在这种情况下,基于块的API非常适合,因为它简单易用,并且您可以在创建实例时声明其行为。随着您的需求变得更加复杂,您会趋向于使用委托。 - SeanCAtkinson
快速问题..如果我想让onConfirm函数引用一个变量,该变量将在ConfirmationViewController中被修改怎么办?无论我在onConfirm函数中使用什么对象,都将设置为传递时对象的值,而不是代码实际运行时的值,对吗? - Thomas
我需要在这种情况下使用委托,对吗?@SeanCAtkinson - Thomas
1
它将通过引用在ConfirmationViewController中保存变量,而不是值本身。这意味着如果在onConfirm块运行时该值已更改,则它将使用新值而不是旧值。如果您需要使用旧值,请在声明onConfirm块的位置之外捕获您想要的特定值并引用该新变量。 - SeanCAtkinson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接