未从 Superview 中删除 UIView 警报
UIView alerts not being removed from Superview
我使用了 YouTube 视频中的一些代码(自定义提醒 Swift 5 (Xcode 11) - 2020 iOS App IOS Academy) 在 swift 中创建自定义警报,从 iPhone 屏幕顶部进入场景,停在中间屏幕上,然后当用户按下 OK 按钮时,警报通过向下滑动到场景底部退出屏幕。
问题:
问题是,当代码显示第一个警报时,一切似乎都很好,但是如果显示第二个或第三个不同的警报,似乎第一个警报子视图(或第二个/第三个等)没有从超级视图中正确删除,这意味着当显示第二个/第三个警报时,它显示在前一个警报的顶部。
我正在使用 Xcode v13.0。我怀疑它发生的原因是因为 removeFromSuperview
发生在一个 ObjC 函数中,该函数在创建子视图的地方之外。但是,我不知道如何解决这个问题。我尝试了很多不同的方法,并在高处和低处搜索了类似的例子。
查看控制器代码:
这是我的视图控制器的代码:
import UIKit
class ViewController: UIViewController {
var myCounter : Int = 0
@IBOutlet weak var myButton: UIButton!
let customAlert = MyAlert()
override func viewDidLoad() {
super.viewDidLoad()
myButton.backgroundColor = .link
myButton.setTitleColor(.white, for: .normal)
myButton.setTitle("Show Alert", for: .normal)
}
@IBAction func didTapButton(_ sender: Any) {
if myCounter > 3 {
myCounter = 1
}
if myCounter == 1 {
customAlert.showMySomethingAlert(on: self)
}
if myCounter == 2 {
customAlert.showMySomethingAlert2(on: self)
}
if myCounter == 3 {
customAlert.showMySomethingAlert3(on: self)
}
myCounter = myCounter + 1
}
@objc func dismissAlert() {
customAlert.dismissAlert()
}
}
CustomAlert Swift 文件:
import Foundation
import UIKit
class MyAlert {
struct Constants {
static let backgroundAlphaTo: CGFloat = 0.6
}
// MARK: Define the greying out of the scene
private let backgroundView: UIView = {
let backgroundView = UIView()
backgroundView.backgroundColor = .black
backgroundView.alpha = 0
return backgroundView
}()
// MARK: Define the Alert frame
private let alertView: UIView = {
let alert = UIView()
alert.backgroundColor = .white
alert.layer.masksToBounds = true
alert.layer.cornerRadius = 4
return alert
}()
private let myView: UIView = {
let myView = UIView()
myView.translatesAutoresizingMaskIntoConstraints = false
return myView
}()
private var myTargetView: UIView?
// MARK: Show the alert on the scene
func showAlert(with title: String,
message: String,
on viewController: UIViewController) {
guard let targetView = viewController.view else {
return
}
// MARK: Determine size of frame needed
var numberOfLines = message.count / 40
print("Number of lines is \(numberOfLines)")
var heightOfAlert : CGFloat = 100.0
heightOfAlert = heightOfAlert + (CGFloat(numberOfLines * 25))
// MARK: Attributes for the frame
myTargetView = targetView
backgroundView.frame = targetView.bounds
targetView.addSubview(backgroundView)
targetView.addSubview(alertView)
alertView.frame = CGRect(x: 40,
y: -300,
width: targetView.frame.size.width-80,
height: heightOfAlert)
// ***********************************************
// MARK: Attributes for image within the alert
let myImageButton = UIButton(frame: CGRect(x: 20, y: 20, width: 50, height: 50))
myImageButton.setImage(UIImage(named:"HarbourBridge750x390.png"), for: .normal)
alertView.addSubview(myImageButton)
// ***********************************************
// MARK: Attributes for the title within the alert
let titleLabel = UILabel(frame: CGRect(x: 0,
y: 0,
width: alertView.frame.size.width,
height: 80))
titleLabel.text = title
titleLabel.textAlignment = .center
//titleLabel.textColor = .black
titleLabel.textColor = UIColor(red: 33.00/255, green: 150.00/255, blue: 243.00/255, alpha: 1.00)
titleLabel.font = UIFont.boldSystemFont(ofSize: 12.00)
alertView.addSubview(titleLabel)
// ***********************************************
// MARK: Attributes for the message label within the alert
let messageLabel = UILabel(frame: CGRect(x: 15,
y: 80,
width: alertView.frame.size.width-30,
height: (CGFloat(numberOfLines * 25))))
messageLabel.numberOfLines = 0
messageLabel.text = message
messageLabel.textAlignment = .left
messageLabel.textColor = .black
messageLabel.font = UIFont.systemFont(ofSize: 12.00)
alertView.addSubview(messageLabel)
// ***********************************************
// MARK: Attributes for the Dismiss button within the alert
let button = UIButton(frame: CGRect(x: 0,
y: alertView.frame.size.height-50,
width: alertView.frame.size.width,
height: 50))
button.setTitle("Dismiss", for: .normal)
button.setTitleColor(.link, for: .normal)
button.addTarget(self,
action: #selector(dismissAlert),
for: .touchUpInside)
alertView.addSubview(button)
// MARK: Attributes for the animation
UIView.animate(withDuration: 0.25,
animations: {
self.backgroundView.alpha = Constants.backgroundAlphaTo
}, completion: { done in
if done {
UIView.animate(withDuration: 0.25, animations: {
self.alertView.center = targetView.center
})
}
})
}
// MARK: Dismiss the Alert
@objc func dismissAlert() {
guard let targetView = myTargetView else {
return
}
UIView.animate(withDuration: 0.25,
animations: {
self.alertView.frame = CGRect(x: 40,
y: targetView.frame.size.height,
width: targetView.frame.size.width-80,
height: 300)
}, completion: { done in
if done {
UIView.animate(withDuration: 0.25, animations: {
self.backgroundView.alpha = 0
}, completion: { done in
if done {
self.alertView.removeFromSuperview()
self.backgroundView.removeFromSuperview()
}
})
}
})
}
// MARK: Setup the alerts to keep the code tidy within the main body files
func showMySomethingAlert(on vc: UIViewController) {
showAlert(with: "My Something Alert", message: "Something has gone wrong and your payment was not successful. This app will continue to operate in trial mode.", on: vc)
}
func showMySomethingAlert2(on vc: UIViewController) {
showAlert(with: "My Something Alert", message: "This is a completely different alert and the text should be totally different and not overlap the previous alert whihc appears to be wht is happening", on: vc)
}
func showMySomethingAlert3(on vc: UIViewController) {
showAlert(with: "My Something Alert", message: "THIS IS YOUR THIRD ALERT AND is a mixture of lower and UPPER CASE CHARACTERS.", on: vc)
}
}
使用 UIView 超类对 MyAlert 进行子类化。
class MyAlert: UIView{
//......
}
您的问题出在您的 MyAlert
class 中,您正在重用 alertView
的实例,但每次添加新的 myImageButton
titleLabel
messageLabel
和 button
你第一次打电话给 customAlert.showMySomethingAlert(on: self)
你有
alertView1
//Childs
myImageButton1
titleLabel1
messageLabel1
button1
当您拨打 customAlert.showMySomethingAlert2(on: self)
时,您将拥有
alertView1
//Childs
myImageButton1
titleLabel1
messageLabel1
button1
//Second instances
myImageButton2
titleLabel2
messageLabel2
button2
而且第一次添加的视图还在,所以你需要删除旧的或者重新使用它们会更好。
删除前。如果警报始终具有相同的视图,则不推荐
从其父视图中删除警报视图后,您需要执行此操作。
self.alertView.removeFromSuperview()
//Add this to remove child views
for subview in self.alertView.subviews {
subview.removeFromSuperview()
}
如果您的警报总是有保存视图,最好将新文本和图像设置为已创建的视图,而不是创建新的并在每次 showAlert
调用时添加。
我使用了 YouTube 视频中的一些代码(自定义提醒 Swift 5 (Xcode 11) - 2020 iOS App IOS Academy) 在 swift 中创建自定义警报,从 iPhone 屏幕顶部进入场景,停在中间屏幕上,然后当用户按下 OK 按钮时,警报通过向下滑动到场景底部退出屏幕。
问题: 问题是,当代码显示第一个警报时,一切似乎都很好,但是如果显示第二个或第三个不同的警报,似乎第一个警报子视图(或第二个/第三个等)没有从超级视图中正确删除,这意味着当显示第二个/第三个警报时,它显示在前一个警报的顶部。
我正在使用 Xcode v13.0。我怀疑它发生的原因是因为 removeFromSuperview
发生在一个 ObjC 函数中,该函数在创建子视图的地方之外。但是,我不知道如何解决这个问题。我尝试了很多不同的方法,并在高处和低处搜索了类似的例子。
查看控制器代码: 这是我的视图控制器的代码:
import UIKit
class ViewController: UIViewController {
var myCounter : Int = 0
@IBOutlet weak var myButton: UIButton!
let customAlert = MyAlert()
override func viewDidLoad() {
super.viewDidLoad()
myButton.backgroundColor = .link
myButton.setTitleColor(.white, for: .normal)
myButton.setTitle("Show Alert", for: .normal)
}
@IBAction func didTapButton(_ sender: Any) {
if myCounter > 3 {
myCounter = 1
}
if myCounter == 1 {
customAlert.showMySomethingAlert(on: self)
}
if myCounter == 2 {
customAlert.showMySomethingAlert2(on: self)
}
if myCounter == 3 {
customAlert.showMySomethingAlert3(on: self)
}
myCounter = myCounter + 1
}
@objc func dismissAlert() {
customAlert.dismissAlert()
}
}
CustomAlert Swift 文件:
import Foundation
import UIKit
class MyAlert {
struct Constants {
static let backgroundAlphaTo: CGFloat = 0.6
}
// MARK: Define the greying out of the scene
private let backgroundView: UIView = {
let backgroundView = UIView()
backgroundView.backgroundColor = .black
backgroundView.alpha = 0
return backgroundView
}()
// MARK: Define the Alert frame
private let alertView: UIView = {
let alert = UIView()
alert.backgroundColor = .white
alert.layer.masksToBounds = true
alert.layer.cornerRadius = 4
return alert
}()
private let myView: UIView = {
let myView = UIView()
myView.translatesAutoresizingMaskIntoConstraints = false
return myView
}()
private var myTargetView: UIView?
// MARK: Show the alert on the scene
func showAlert(with title: String,
message: String,
on viewController: UIViewController) {
guard let targetView = viewController.view else {
return
}
// MARK: Determine size of frame needed
var numberOfLines = message.count / 40
print("Number of lines is \(numberOfLines)")
var heightOfAlert : CGFloat = 100.0
heightOfAlert = heightOfAlert + (CGFloat(numberOfLines * 25))
// MARK: Attributes for the frame
myTargetView = targetView
backgroundView.frame = targetView.bounds
targetView.addSubview(backgroundView)
targetView.addSubview(alertView)
alertView.frame = CGRect(x: 40,
y: -300,
width: targetView.frame.size.width-80,
height: heightOfAlert)
// ***********************************************
// MARK: Attributes for image within the alert
let myImageButton = UIButton(frame: CGRect(x: 20, y: 20, width: 50, height: 50))
myImageButton.setImage(UIImage(named:"HarbourBridge750x390.png"), for: .normal)
alertView.addSubview(myImageButton)
// ***********************************************
// MARK: Attributes for the title within the alert
let titleLabel = UILabel(frame: CGRect(x: 0,
y: 0,
width: alertView.frame.size.width,
height: 80))
titleLabel.text = title
titleLabel.textAlignment = .center
//titleLabel.textColor = .black
titleLabel.textColor = UIColor(red: 33.00/255, green: 150.00/255, blue: 243.00/255, alpha: 1.00)
titleLabel.font = UIFont.boldSystemFont(ofSize: 12.00)
alertView.addSubview(titleLabel)
// ***********************************************
// MARK: Attributes for the message label within the alert
let messageLabel = UILabel(frame: CGRect(x: 15,
y: 80,
width: alertView.frame.size.width-30,
height: (CGFloat(numberOfLines * 25))))
messageLabel.numberOfLines = 0
messageLabel.text = message
messageLabel.textAlignment = .left
messageLabel.textColor = .black
messageLabel.font = UIFont.systemFont(ofSize: 12.00)
alertView.addSubview(messageLabel)
// ***********************************************
// MARK: Attributes for the Dismiss button within the alert
let button = UIButton(frame: CGRect(x: 0,
y: alertView.frame.size.height-50,
width: alertView.frame.size.width,
height: 50))
button.setTitle("Dismiss", for: .normal)
button.setTitleColor(.link, for: .normal)
button.addTarget(self,
action: #selector(dismissAlert),
for: .touchUpInside)
alertView.addSubview(button)
// MARK: Attributes for the animation
UIView.animate(withDuration: 0.25,
animations: {
self.backgroundView.alpha = Constants.backgroundAlphaTo
}, completion: { done in
if done {
UIView.animate(withDuration: 0.25, animations: {
self.alertView.center = targetView.center
})
}
})
}
// MARK: Dismiss the Alert
@objc func dismissAlert() {
guard let targetView = myTargetView else {
return
}
UIView.animate(withDuration: 0.25,
animations: {
self.alertView.frame = CGRect(x: 40,
y: targetView.frame.size.height,
width: targetView.frame.size.width-80,
height: 300)
}, completion: { done in
if done {
UIView.animate(withDuration: 0.25, animations: {
self.backgroundView.alpha = 0
}, completion: { done in
if done {
self.alertView.removeFromSuperview()
self.backgroundView.removeFromSuperview()
}
})
}
})
}
// MARK: Setup the alerts to keep the code tidy within the main body files
func showMySomethingAlert(on vc: UIViewController) {
showAlert(with: "My Something Alert", message: "Something has gone wrong and your payment was not successful. This app will continue to operate in trial mode.", on: vc)
}
func showMySomethingAlert2(on vc: UIViewController) {
showAlert(with: "My Something Alert", message: "This is a completely different alert and the text should be totally different and not overlap the previous alert whihc appears to be wht is happening", on: vc)
}
func showMySomethingAlert3(on vc: UIViewController) {
showAlert(with: "My Something Alert", message: "THIS IS YOUR THIRD ALERT AND is a mixture of lower and UPPER CASE CHARACTERS.", on: vc)
}
}
使用 UIView 超类对 MyAlert 进行子类化。
class MyAlert: UIView{
//......
}
您的问题出在您的 MyAlert
class 中,您正在重用 alertView
的实例,但每次添加新的 myImageButton
titleLabel
messageLabel
和 button
你第一次打电话给 customAlert.showMySomethingAlert(on: self)
你有
alertView1
//Childs
myImageButton1
titleLabel1
messageLabel1
button1
当您拨打 customAlert.showMySomethingAlert2(on: self)
时,您将拥有
alertView1
//Childs
myImageButton1
titleLabel1
messageLabel1
button1
//Second instances
myImageButton2
titleLabel2
messageLabel2
button2
而且第一次添加的视图还在,所以你需要删除旧的或者重新使用它们会更好。
删除前。如果警报始终具有相同的视图,则不推荐 从其父视图中删除警报视图后,您需要执行此操作。
self.alertView.removeFromSuperview()
//Add this to remove child views
for subview in self.alertView.subviews {
subview.removeFromSuperview()
}
如果您的警报总是有保存视图,最好将新文本和图像设置为已创建的视图,而不是创建新的并在每次 showAlert
调用时添加。