自定义 UIView 类中的 UIButton 不起作用

Posted

技术标签:

【中文标题】自定义 UIView 类中的 UIButton 不起作用【英文标题】:UIButton inside custom UIView class not working 【发布时间】:2018-03-22 13:59:08 【问题描述】:

我知道这个问题已经被问过很多次了,但没有一个解决方案对我有用。

我有一个自定义 UIView 类,用于显示警报消息。我添加了 UIButton 来关闭视图。但是,当我选择它时,什么也没有发生。

import UIKit
public class Alert: UIView 

    public var image: UIImage?
public var title: String?
public var message: String?
public var closeButtonText: String?

public var dialogBackgroundColor: UIColor = .white
public var dialogTitleTextColor: UIColor = .black
public var dialogMessageTextColor: UIColor = UIColor(red: 0.2, green: 0.2, blue: 0.2, alpha: 1)
public var dialogImageColor: UIColor = UIColor(red:0.47, green:0.72, blue:0.35, alpha:1.0)
public var overlayColor: UIColor = .black
public var overlayOpacity: CGFloat = 0.66

public var paddingSingleTextOnly: CGFloat = 8
public var paddingTopAndBottom: CGFloat = 24
public var paddingFromSides: CGFloat = 8
public var seperatorHeight: CGFloat = 6

private var height: CGFloat = 0
private var width: CGFloat = 0
private var maxSize: CGSize = CGSize()
private let marginFromSides: CGFloat = 80

public lazy var imageSize: CGSize = CGSize(width: 75, height: 75)

public var overlay = false
public var blurOverlay = true

//animation duration
public var duration = 0.33

private var onComplete: (() ->  Void)?

@objc public var titleFont: UIFont =  UIFont.systemFont(ofSize: 18)
@objc public var messageFont: UIFont = UIFont.systemFont(ofSize: 15)


private lazy var backgroundView: UIView = 
    let view = UIView()
    view.alpha = 0
    return view
()

public let dialogView: UIView = 
    let view = UIView()
    view.layer.cornerRadius = 6
    view.layer.masksToBounds = true
    view.alpha = 0
    view.clipsToBounds = true
    return view
()

private lazy var imageView: UIImageView = 
    let view = UIImageView()
    view.contentMode = .scaleAspectFit
    return view
()

public lazy var closeButton: UIButton = 
    let button = UIButton()
    return button
()

private lazy var titleLabel: UILabel = 
    let label = UILabel()
    label.numberOfLines = 0
    label.textAlignment = .center
    return label
()

private lazy var messageLabel: UILabel = 
    let label = UILabel()
    label.numberOfLines = 0
    label.textAlignment = .center
    return label
()

@objc func closeButtonTapped(sender: UIButton)
    dismiss()


private func calculations() 
    height += paddingTopAndBottom
    maxSize = CGSize(width: frame.width - marginFromSides * 2, height: frame.height - marginFromSides)


public convenience init(title:String, message: String, image:UIImage) 
    self.init(frame: UIScreen.main.bounds)
    self.title = title
    self.message = message
    self.image = image


public convenience init(title:String, image:UIImage) 
    self.init(frame: UIScreen.main.bounds)
    self.title = title
    self.image = image


public convenience init(title: String, message: String) 
    self.init(frame: UIScreen.main.bounds)
    self.title = title
    self.message = message


public convenience init(message: String) 
    self.init(frame: UIScreen.main.bounds)
    paddingTopAndBottom = paddingSingleTextOnly
    paddingFromSides = paddingSingleTextOnly * 2
    self.message = message


override init(frame: CGRect) 
    super.init(frame: frame)


public required init?(coder aDecoder: NSCoder) 
    fatalError("init(coder:) has not been implemented")


private func createOverlay() 
    backgroundView.frame = frame
    backgroundView.backgroundColor = overlayColor
    backgroundView.isUserInteractionEnabled = true
    addSubview(backgroundView)
    if let window = UIApplication.shared.keyWindow 
        window.addSubview(backgroundView)
     else if let window = UIApplication.shared.delegate?.window??.rootViewController 
        window.view.addSubview(self)
    


private func createBlurOverlay() 
    backgroundView.frame = frame
    //Blur Effect
    let blurEffect = UIBlurEffect(style: UIBlurEffectStyle.dark)
    let blurEffectView = UIVisualEffectView(effect: blurEffect)
    blurEffectView.frame = frame
    blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    backgroundView.addSubview(blurEffectView)
    addSubview(backgroundView)
    if let window = UIApplication.shared.keyWindow 
        window.addSubview(backgroundView)
     else if let window = UIApplication.shared.delegate?.window??.rootViewController 
        window.view.addSubview(self)
    


private func createTitle(title: String) 
    titleLabel.font = titleFont
    titleLabel.text = title
    titleLabel.frame.origin.y = height + 2
    let titleLabelSize = titleLabel.sizeThatFits(maxSize)
    handleSize(size: titleLabelSize)
    titleLabel.frame.size = titleLabelSize
    titleLabel.textColor = self.dialogTitleTextColor
    dialogView.addSubview(titleLabel)


private func createMessage(message: String) 
    messageLabel.font = messageFont
    messageLabel.text = message
    messageLabel.frame.origin.y = height
    let messageLabelSize = messageLabel.sizeThatFits(maxSize)
    messageLabel.frame.size = messageLabelSize
    messageLabel.textColor = self.dialogMessageTextColor
    handleSize(size: messageLabelSize)
    dialogView.addSubview(messageLabel)



private func createImage(image: UIImage) 
    imageView.image = image.withRenderingMode(.alwaysTemplate)
    imageView.frame.origin.y = height
    imageView.frame.size = imageSize
    imageView.tintColor = self.dialogImageColor
    handleSize(size: imageSize)
    dialogView.addSubview(imageView)


private func createButton()
    closeButton.setTitle("Close", for: .normal)
    closeButton.tintColor = UIColor.white
    closeButton.frame.origin.y = height + 20
    let closeButtonSize = CGSize(width: width - 60, height: 60)
    closeButton.frame.size = closeButtonSize
    closeButton.layer.cornerRadius = 6
    closeButton.backgroundColor = Color.NavigationBar.tintColor
    closeButton.isUserInteractionEnabled = true
    handleSize(size: closeButtonSize)
    dialogView.addSubview(closeButton)


private func createDialog() 
    centerAll()
    height += paddingTopAndBottom
    dialogView.frame.size = CGSize(width: width, height: height)
    dialogView.backgroundColor = self.dialogBackgroundColor
    dialogView.isUserInteractionEnabled = true
    addSubview(dialogView)
    self.dialogView.center = self.center
    self.dialogView.transform = CGAffineTransform(scaleX: 1.15, y: 1.15)
    if let window = UIApplication.shared.keyWindow 
        window.addSubview(dialogView)
        closeButton.addTarget(self, action: #selector(closeButtonTapped(sender:)), for: .touchUpInside)
     else if let window = UIApplication.shared.delegate?.window??.rootViewController 
        UIApplication.topViewController()?.view.addSubview(self)
        window.view.addSubview(self)
        closeButton.addTarget(self, action: #selector(closeButtonTapped(sender:)), for: .touchUpInside)
    


private func centerAll() 
    if ((messageLabel.text) != nil) 
        messageLabel.frame.origin.x = (width - messageLabel.frame.width) / 2
    

    if ((titleLabel.text) != nil) 
        titleLabel.frame.origin.x = (width - titleLabel.frame.width) / 2
    

    if ((imageView.image) != nil) 
        imageView.frame.origin.x = (width - imageView.frame.width) / 2
    
    closeButton.frame.origin.x = (width - closeButton.frame.width) / 2



private func handleSize(size: CGSize) 
    if width < size.width + paddingFromSides * 2 
        width = size.width + paddingFromSides * 2
    
    if paddingTopAndBottom != paddingSingleTextOnly 
        height += seperatorHeight
    
    height += size.height


private func showAnimation() 
    UIView.animate(withDuration: duration, animations: 
        if self.overlay 
            self.backgroundView.alpha = self.overlayOpacity
            self.dialogView.transform = CGAffineTransform(scaleX: 1, y: 1)
        
        self.dialogView.alpha = 1
    )


public func show() 
    if let complete = onComplete 
        self.onComplete = complete
    
    calculations()

    if self.overlay 
        if blurOverlay 
            createBlurOverlay()
         else 
            createOverlay()
        
    

    if let img = image 
        createImage(image: img)
    
    if let title = title 
        createTitle(title: title)
    
    if let message = message 
        createMessage(message: message)
    
    createButton()
    createDialog()

    showAnimation()



public func dismiss()
    UIView.animate(withDuration: duration, animations: 
        if self.overlay 
            self.backgroundView.alpha = 0
        
        self.dialogView.transform = CGAffineTransform(scaleX: 1.15, y: 1.15)
        self.dialogView.alpha = 0
    , completion:  (completed) in
        self.dialogView.removeFromSuperview()
        if (self.overlay)
        
            self.backgroundView.removeFromSuperview()
        
        self.removeFromSuperview()
        if let completionHandler = self.onComplete 
            completionHandler()
        
    )


我如何创建警报;

let alert = Alert(title: "hata",message: "hata mesajı ekrana basıldı", image: #imageLiteral(resourceName: "error"))
alert.show()

如果我在 UIViewController(我创建这个 UIView 的地方)中声明目标为

Alert.closeButton.addTarget(self, action: #selector(closeButtonTapped(sender:), for: .touchUPInside)

并在 UIViewController 中创建函数它正在工作。我不知道为什么它在自定义类中不起作用。

所以我的问题是如何在点击按钮时关闭警报视图?

我尝试了以下解决方案,但对我不起作用;

UIButton target action inside custom class

【问题讨论】:

您的代码并没有真正显示正在发生的事情...您在哪里将按钮添加到视图中?你是如何显示视图的?请看:How to create a Minimal, Complete, and Verifiable example @DonMag 我编辑了我的代码。你能检查一下吗?谢谢。 你需要更彻底。我将您的代码粘贴到一个 swift 文件中,并且有几十个错误(未解析的标识符等)。将您的代码剥离到最基本的开始 - 只需要显示您的视图并添加按钮。一旦你的按钮/关闭工作,然后你可以添加其他所有内容。 我添加了整个代码。这个应该可以的。 @DonMag 【参考方案1】:

假设这些行在函数内部 - 例如由于点击按钮:

@IBAction func didTap(_ sender: Any) 
    let alert = Alert(title: "hata",message: "hata mesajı ekrana basıldı", image: #imageLiteral(resourceName: "error"))
    alert.show()

您正在创建 Alert 类的实例,在其中调用 .show() 函数,然后它超出范围。

因此,只要该函数存在,alert 就不再存在,并且其中的任何代码都无法运行。

你需要有一个类级别的变量在显示时保持它:

class MyViewController: UIViewController 

    var alert: Alert?

    @IBAction func didTap(_ sender: Any) 
        alert = Alert(title: "hata",message: "hata mesajı ekrana basıldı", image: #imageLiteral(resourceName: "error"))
        alert?.show()
    


这是处理Alert 视图的“错误方式”和“正确方式”的演示:https://github.com/DonMag/EmreTest

【讨论】:

我认为您误解了我想要实现的目标。我在警报视图中有 UIButton,该按钮关闭警报视图。所以我不想在按钮点击功能中调用 show() 。谢谢你:) 不...这是您在问题How I create the alert; 中发布的VIewController 中的代码。这两行代码是从 ViewController 中的 somewhere 调用的。无论您从哪里调用它,let alert = Alert(...) 创建的对象都会在该代码函数/方法/块完成时超出范围,并且您的新对象会消失。 view 仍然在屏幕上,但它背后的 code 不见了。 @EmreÖnder - 请参阅我的答案的编辑。自己试试吧……新建一个ViewController,加个按钮,用上面的代码测试一下。 我编辑我的问题以更清楚。我要问的是如何关闭警报视图,而不是打开它。 @EmreÖnder - 如果您尝试我的回答,您会看到“警报视图”中的按钮有效 现在关闭视图。问题是,当您实例化 Alert 类并退出该代码行所在的任何函数时,您还 DESTROY 该类,因此按钮代码不能运行 - 它不再存在!试着做我给你看的,你会看到的。【参考方案2】:

看这段代码

1- 在 UIView 类中调用 IBAction

import UIKit

public class Alert: UIView 

    public lazy var closeButton: UIButton = 
        let button = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
        button.backgroundColor = #colorLiteral(red: 0.9254902005, green: 0.2352941185, blue: 0.1019607857, alpha: 1)
        return button
    ()

    func createDialog() 
          closeButton.addTarget(self, action: #selector(self.closeButtonTapped(sender:)), for: .touchUpInside)
        self.addSubview(closeButton)
    

    @objc func closeButtonTapped(sender: UIButton)
        print("Call   1")
    

class ViewController: UIViewController 
    override func viewDidLoad() 
        super.viewDidLoad()
        let alert = Alert(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
        alert.createDialog()

        self.view.addSubview(alert)
    


2- 在 UIViewController 类中调用 IBAction

import UIKit

public class Alert: UIView 

    public lazy var closeButton: UIButton = 
        let button = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
        button.backgroundColor = #colorLiteral(red: 0.9254902005, green: 0.2352941185, blue: 0.1019607857, alpha: 1)
        return button
    ()

    func createDialog() 
        //  closeButton.addTarget(self, action: #selector(self.closeButtonTapped(sender:)), for: .touchUpInside)
        self.addSubview(closeButton)
    

    @objc func closeButtonTapped(sender: UIButton)
        print("Call   1")
    

class ViewController: UIViewController 
    override func viewDidLoad() 
        super.viewDidLoad()
        let alert = Alert(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
        alert.createDialog()
        alert.closeButton.addTarget(self, action: #selector(self.closeButtonTappedController(_:)), for: .touchUpInside)
        self.view.addSubview(alert)
    

    @IBAction func closeButtonTappedController(_ sender:UIButton)
        print("Call   2")

    

【讨论】:

呃...不要发布代码的屏幕截图。 它已被修改,但我正在添加屏幕以显示输出

以上是关于自定义 UIView 类中的 UIButton 不起作用的主要内容,如果未能解决你的问题,请参考以下文章

为啥从自定义 UIView 向 UIButton 添加子视图不显示添加的子视图?

UIVIew 中的 UIButton 不在 iPhone 应用程序中居中

初始化后访问自定义 UIButton 类中的插座

尽管 userInteractionEnabled = true,但 UIView 中的 UIButton 没有响应触摸事件

未设置自定义 UIView iboutlet

ViewController 不符合自定义 UIButton 类