我正在为我的 iOS 应用程序构建一个自定义通知系统,其中临时通知视图显示在屏幕顶部。这些视图具有分层结构,其中包含用于模糊效果的 UIVisualEffectView 和包含图标和文本的内容视图。
我希望用户通过向下滑动来关闭通知。我成功地发现滑动手势已添加到视图中,但不知何故,没有检测到滑动,甚至没有检测到点击或平移手势。
滑动手势有时无法一致触发。下面是相关代码:
class NotificationViewController: UIViewController, UIGestureRecognizerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
// Show a notification after 1 second
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
self.showNotification(message: "Swipe down to dismiss!")
}
}
func showNotification(message: String) {
let notificationView = createNotificationView(message: message)
view.addSubview(notificationView)
positionNotification(notificationView, above: nil)
// Automatically dismiss after 3 seconds
DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) {
self.dismissNotification(notificationView)
}
}
private func createNotificationView(message: String) -> UIView {
// Create the notification view
let notificationView = UIView()
notificationView.backgroundColor = .clear
notificationView.layer.cornerRadius = 12
notificationView.layer.borderWidth = 1
notificationView.layer.borderColor = UIColor.darkGray.cgColor
notificationView.translatesAutoresizingMaskIntoConstraints = false
notificationView.isUserInteractionEnabled = true
// Add blur effect
let blurEffect = UIBlurEffect(style: .systemChromeMaterial)
let blurEffectView = UIVisualEffectView(effect: blurEffect)
blurEffectView.frame = notificationView.bounds
blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
blurEffectView.isUserInteractionEnabled = false
notificationView.addSubview(blurEffectView)
// Add content to the blur effect view
let label = UILabel()
label.text = message
label.textColor = .label
label.textAlignment = .center
label.translatesAutoresizingMaskIntoConstraints = false
blurEffectView.contentView.addSubview(label)
NSLayoutConstraint.activate([
label.centerXAnchor.constraint(equalTo: blurEffectView.contentView.centerXAnchor),
label.centerYAnchor.constraint(equalTo: blurEffectView.contentView.centerYAnchor)
])
// Add swipe gesture recognizer
let swipeGesture = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipe(_:)))
swipeGesture.direction = .down
swipeGesture.delegate = self
notificationView.addGestureRecognizer(swipeGesture)
return notificationView
}
private func positionNotification(_ notificationView: UIView, above previousNotificationView: UIView?) {
notificationView.translatesAutoresizingMaskIntoConstraints = false
if let previousView = previousNotificationView {
NSLayoutConstraint.activate([
notificationView.bottomAnchor.constraint(equalTo: previousView.topAnchor, constant: -10),
notificationView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
notificationView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.9),
notificationView.heightAnchor.constraint(equalToConstant: 60)
])
} else {
NSLayoutConstraint.activate([
notificationView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 60),
notificationView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
notificationView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.9),
notificationView.heightAnchor.constraint(equalToConstant: 60)
])
}
}
@objc private func handleSwipe(_ gesture: UISwipeGestureRecognizer) {
guard let notificationView = gesture.view else { return }
print("Swipe detected on notification!")
dismissNotification(notificationView)
}
private func dismissNotification(_ notificationView: UIView) {
UIView.animate(withDuration: 0.3, animations: {
notificationView.alpha = 0
}) { _ in
notificationView.removeFromSuperview()
}
}
}
同样的方法也适用于其他视图,但它们是在父类级别声明的,而这个“通知视图”是在函数内初始化的,但值和其他一切都运行良好。这应该有效,所以我不确定我错过了什么。
希望得到一些建议。
通过从 UIView 更改为 UIViewController 解决了该问题。
class NotificationViewController: UIViewController {
private let message: String
private let textColor: UIColor
private let backgroundColor: UIColor
private let iconName: String?
private let iconColor: UIColor
private let iconHeight: CGFloat
private let displayDuration: Double
init(message: String, textColor: UIColor, backgroundColor: UIColor, iconName: String?, iconColor: UIColor, iconHeight: CGFloat, displayDuration: Double) {
self.message = message
self.textColor = textColor
self.backgroundColor = backgroundColor
self.iconName = iconName
self.iconColor = iconColor
self.iconHeight = iconHeight
self.displayDuration = displayDuration
super.init(nibName: nil, bundle: nil)
self.modalPresentationStyle = .overCurrentContext
self.modalTransitionStyle = .crossDissolve
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
self.definesPresentationContext = true
setupNotificationView()
}
private func setupNotificationView() {
self.view.backgroundColor = .clear // Make sure background is transparent
// Create the notification content view
let notificationContentView = UIView()
notificationContentView.backgroundColor = .clear
notificationContentView.layer.cornerRadius = ViewSetUpManager.mediumCornerRadius
notificationContentView.layer.borderWidth = 1
notificationContentView.layer.borderColor = UIColor.darkGray.cgColor
notificationContentView.clipsToBounds = true
notificationContentView.isUserInteractionEnabled = true
// Add blur effect
let blurEffect = UIBlurEffect(style: .systemChromeMaterial)
let blurEffectView = UIVisualEffectView(effect: blurEffect)
notificationContentView.addSubview(blurEffectView)
blurEffectView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
// Add icon and label
let iconImageView = UIImageView()
if let iconName = iconName {
iconImageView.image = UIImage(systemName: iconName)
iconImageView.contentMode = .scaleAspectFit
iconImageView.tintColor = iconColor
iconImageView.isUserInteractionEnabled = false
blurEffectView.contentView.addSubview(iconImageView)
iconImageView.snp.makeConstraints { make in
make.leading.equalToSuperview().offset(16)
make.centerY.equalToSuperview()
make.width.height.equalTo(iconHeight)
}
}
let label = UILabel()
label.text = message
label.font = UIFont.systemFont(ofSize: ViewSetUpManager.shared.genericTitleFontSize(), weight: .regular)
label.textColor = textColor
label.numberOfLines = 2
label.textAlignment = .center
label.adjustsFontSizeToFitWidth = true
label.isUserInteractionEnabled = false
blurEffectView.contentView.addSubview(label)
label.snp.makeConstraints { make in
make.leading.equalTo(iconImageView.snp.trailing).offset(8)
make.trailing.equalToSuperview().inset(16)
make.centerY.equalToSuperview()
}
// Add notificationContentView to self.view
self.view.addSubview(notificationContentView)
notificationContentView.snp.makeConstraints { make in
make.bottom.equalTo(self.view.safeAreaLayoutGuide).offset(-(self.tabBarHeight() + 50))
make.centerX.equalToSuperview()
make.width.equalToSuperview().multipliedBy(0.9)
make.height.greaterThanOrEqualTo(60)
}
// Add swipe gesture recognizer
let swipeGesture = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipeGesture(_:)))
swipeGesture.direction = [.left, .down, .right]
swipeGesture.delegate = self
notificationContentView.addGestureRecognizer(swipeGesture)
// Automatically dismiss after displayDuration
DispatchQueue.main.asyncAfter(deadline: .now() + displayDuration) {
self.dismiss(animated: true, completion: nil)
}
}
private func tabBarHeight() -> CGFloat {
return self.presentingViewController?.tabBarController?.tabBar.frame.height ?? 0
}
@objc private func handleSwipeGesture(_ gesture: UISwipeGestureRecognizer) {
self.dismiss(animated: true, completion: nil)
}
}
extension NotificationViewController: UIGestureRecognizerDelegate {
// Implement delegate methods if necessary
}