XCODE SWIFT 如何在按下按钮后执行代码,但在另一个 file/class 中?
XCODE SWIFT How do I execute code after a button is pressed, but in another file/class?
我正在使用MapKit,用户可以添加注释。他们可以点击屏幕,这会提示他们是否要使用 UIAlert 添加注释,如果他们说是,它会显示另一个视图控制器,以便用户可以输入有关注释的信息,如位置名称、描述等. 该视图控制器在顶部有一个 'Done' BarButtonItem 来确认他们输入的信息,并创建注释。
@IBAction func doneButtonPressed(_ sender: UIBarButtonItem) {
doneButtonHasBeenPressed = true
dismiss(animated: true, completion: nil)
}
问题是,必须在原始视图控制器中的 'touchesEnded' 函数中创建注释,将用户发送到他们输入注释信息的视图控制器,因为这是它获取的地方来自(使用 touchesEnded)的 CLCoordinate2D。正是在同一个 touchesEnded 函数中,我将用户发送到下一个视图控制器。这是 touchesEnded 代码及其使用的辅助函数:
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let touchLocation = touch.location(in: view)
// Converts CGPoint coordinates and UIView to CLLocationCordinate2D (map coordinates) Remember to rename if addButtonPressed order of creation of annotation gets changed!
let coordinatesTouchedToCreateAnnotation = mapView.convert(touchLocation, toCoordinateFrom: view)
if userIsAllowedToAddAnnotation {
let alertController = UIAlertController(title: "Confirm", message: "Are you sure you want to add a jump location here?", preferredStyle: .alert)
let noAction = UIAlertAction(title: "No", style: .cancel, handler: nil)
let yesAction = UIAlertAction(title: "Yes", style: .default) { (action) in
// Segue takes user to JumpSpotCreatorController to input information about the jump location
self.performSegue(withIdentifier: "CreateJumpSpot", sender: self)
if self.jumpSpotCreatorController.doneButtonHasBeenPressed == true {
self.jumpSpotCreatorController.doneButtonHasBeenPressed = false
self.createJumpSpotAnnotation(coordinatesDeterminedByTouch: coordinatesTouchedToCreateAnnotation)
self.userIsAllowedToAddAnnotation = false
self.tapToAddJumpSpotLabel.isHidden = true
}
}
alertController.addAction(noAction)
alertController.addAction(yesAction)
present(alertController, animated: true, completion: nil)
} else {
return
}
}
}
// Force unwrap is okay because this will only be called if 'Done' button is pressed in the JumpSpotCreatorController, which mandates that those inputs not be nil.
func createJumpSpotAnnotation(coordinatesDeterminedByTouch: CLLocationCoordinate2D) {
mapView.addAnnotation(JumpSpotAnnotation(name: jumpSpotCreatorController.nameTextField.text!, coordinate: coordinatesDeterminedByTouch, estimatedHeight: jumpSpotCreatorController.estimatedHeightTextField.text!, locationDescription: jumpSpotCreatorController.descripitionTextView.text, warnings: jumpSpotCreatorController.warningsTextView.text ?? "", image: jumpSpotCreatorController.jumpSpotImageView.image ?? UIImage(imageLiteralResourceName: "Image-1")))
}
如您所见,在 touchesEnded 函数中创建注释的代码块(位于我向 alertController 添加操作的正上方,以防您找不到它。它大约有 4 行)是立即执行,而不是在我需要时执行,即在我的其他视图控制器 (JumpSpotCreatorController) 中按下 'Done' 按钮后。我尝试使用 doneButtonHasBeenPressed 变量修复它,但它没有任何区别(原因很明显)。我怎样才能在按下完成按钮后才执行它?我无法将另一个视图控制器初始化为主视图控制器中的对象(带有 touchesEnded 的是主视图控制器),因为它会在两个视图控制器之间创建一个无限循环的引用。 DispatchQueue 能以某种方式提供帮助吗?我已经研究了几个小时,但不太清楚如何在这里应用它。非常感谢。
我没有 100% 关注,但这有帮助吗:
按钮被点击并且你在“A”
“A”调用 B 做某事,AND,
“A”传递对自身的引用
(也就是说,为B中的那个调用“添加一个参数”,它是class“A”的一个变量。假设这个变量被命名为“callMeWhenYou'reFinished”)
当“B”完成它必须做的事情时
只需调用 callMeWhenYou'reFinished#WhateverFunction
...你走吧!
作为一种“一般编程问题”(忘记 Swift 中可用的内容等),您只是在描述“回调”。如果我没听错,希望对你有所帮助!
进一步:
有一些循环引用是完全可以的。 (所以,A 知道 B 并且 B 得到了对 A 中某事的回调。)
显然,如果您随后错误地进行了无限循环,那就是无限循环。但是“不小心造成无限循环”与您是否有单独的 classes、引用等完全没有关系
如果我没猜错,你正在尝试得到这样的东西:
Class A {
weak var referenceToB: B?
@IBAction func buttonAction(_ sender: Any) {
guard var referenceToB = referenceToB else {
fatalError("referenceToB not set!")
}
referenceToB!.otherFunction()
}
}
Class B {
func otherFunction() {
//stuff
}
}
在你的例子中,在你实例化你的 VC 之后,分配一个对包含所需功能的 class 对象的引用。
有多种方法可以做到这一点 --- 各有利弊。
此方法使用委托/协议模式。
我们定义了一个协议,它将允许 类 在他们的委托中执行函数 类:
// protocol / delegate pttern
protocol JumpSpotDelegate: class {
func createJumpSpotAnnotation(_ name: String, estimatedHeight: String, locationDescription: String, warnings: String, image: UIImage?)
func cancelAnnotation()
}
在您的地图视图的控制器中,我们符合该委托:
class MapViewController: UIViewController, JumpSpotDelegate {
在JumpSpotCreatorController
中,我们设置了一个delegate
属性:
class JumpSpotCreatorController: UIViewController {
weak var delegate: JumpSpotDelegate?
当我们导航到 JumpSpotCreatorController
时,我们将自己指定为其委托:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// make sure we're acting on the correct segue
if segue.identifier == "CreateJumpSpot", let vc = segue.destination as? JumpSpotCreatorController {
// set the delegate in the JumpSpotCreatorController we're navigating to
vc.delegate = self
}
}
在JumpSpotCreatorController
中,当点击完成按钮时,我们通过委托函数告诉地图控制器:
delegate?.createJumpSpotAnnotation(name, estimatedHeight: estimatedHeight, locationDescription: description, warnings: warnings, image: img)
一起来了。我通过 let
语句添加了对象来编写此...我希望您将它们作为 @IBOutlet
连接:
// protocol / delegate pttern
protocol JumpSpotDelegate: class {
func createJumpSpotAnnotation(_ name: String, estimatedHeight: String, locationDescription: String, warnings: String, image: UIImage?)
func cancelAnnotation()
}
class MapViewController: UIViewController, JumpSpotDelegate {
// this will hold the touch point while we navigate to and back from the JumpSpotCreatorController
var lastTouch: CLLocationCoordinate2D?
let mapView: MKMapView!
var userIsAllowedToAddAnnotation = true
let tapToAddJumpSpotLabel = UILabel()
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let touchLocation = touch.location(in: view)
// Converts CGPoint coordinates and UIView to CLLocationCordinate2D (map coordinates)
// store in class property
self.lastTouch = mapView.convert(touchLocation, toCoordinateFrom: view)
if userIsAllowedToAddAnnotation {
let alertController = UIAlertController(title: "Confirm", message: "Are you sure you want to add a jump location here?", preferredStyle: .alert)
let noAction = UIAlertAction(title: "No", style: .cancel, handler: nil)
let yesAction = UIAlertAction(title: "Yes", style: .default) { (action) in
// Segue takes user to JumpSpotCreatorController to input information about the jump location
self.performSegue(withIdentifier: "CreateJumpSpot", sender: self)
}
alertController.addAction(noAction)
alertController.addAction(yesAction)
present(alertController, animated: true, completion: nil)
} else {
return
}
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// make sure we're acting on the correct segue
if segue.identifier == "CreateJumpSpot", let vc = segue.destination as? JumpSpotCreatorController {
// set the delegate in the JumpSpotCreatorController we're navigating to
vc.delegate = self
}
}
// called by Button action in JumpSpotCreatorController
func createJumpSpotAnnotation(_ name: String, estimatedHeight: String, locationDescription: String, warnings: String, image: UIImage?) {
// the coordinate parameter was stored in our class property "lastTouch"
guard let lastTouch = self.lastTouch else {
// self.lastTouch was not set!
return
}
mapView.addAnnotation(JumpSpotAnnotation(name: name, coordinate: lastTouch, estimatedHeight: estimatedHeight, locationDescription: description, warnings: warnings, image: image ?? UIImage(imageLiteralResourceName: "Image-1")))
self.userIsAllowedToAddAnnotation = false
self.tapToAddJumpSpotLabel.isHidden = true
// pop from JumpSpotCreatorController back to self
self.navigationController?.popViewController(animated: true)
}
// I'm assuming you would also have a Cancel button?
func cancelAnnotation() {
self.lastTouch = nil
// pop from JumpSpotCreatorController back to self
self.navigationController?.popViewController(animated: true)
}
}
class JumpSpotCreatorController: UIViewController {
weak var delegate: JumpSpotDelegate?
let nameTextField = UITextField()
let estimatedHeightTextField = UITextField()
let descripitionTextView = UITextView()
let warningsTextView = UITextView()
let jumpSpotImageView = UIImageView()
@IBAction func doneBtnTapped() {
// presumably, you'll validate all these
guard let name = nameTextField.text,
let estimatedHeight = estimatedHeightTextField.text,
let description = descripitionTextView.text,
let warnings = warningsTextView.text,
let img = jumpSpotImageView.image else {
// notify user required fields were blank
return
}
delegate?.createJumpSpotAnnotation(name, estimatedHeight: estimatedHeight, locationDescription: description, warnings: warnings, image: img)
}
@IBAction func cancelBtnTapped() {
delegate?.cancelAnnotation()
}
}
我正在使用MapKit,用户可以添加注释。他们可以点击屏幕,这会提示他们是否要使用 UIAlert 添加注释,如果他们说是,它会显示另一个视图控制器,以便用户可以输入有关注释的信息,如位置名称、描述等. 该视图控制器在顶部有一个 'Done' BarButtonItem 来确认他们输入的信息,并创建注释。
@IBAction func doneButtonPressed(_ sender: UIBarButtonItem) {
doneButtonHasBeenPressed = true
dismiss(animated: true, completion: nil)
}
问题是,必须在原始视图控制器中的 'touchesEnded' 函数中创建注释,将用户发送到他们输入注释信息的视图控制器,因为这是它获取的地方来自(使用 touchesEnded)的 CLCoordinate2D。正是在同一个 touchesEnded 函数中,我将用户发送到下一个视图控制器。这是 touchesEnded 代码及其使用的辅助函数:
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let touchLocation = touch.location(in: view)
// Converts CGPoint coordinates and UIView to CLLocationCordinate2D (map coordinates) Remember to rename if addButtonPressed order of creation of annotation gets changed!
let coordinatesTouchedToCreateAnnotation = mapView.convert(touchLocation, toCoordinateFrom: view)
if userIsAllowedToAddAnnotation {
let alertController = UIAlertController(title: "Confirm", message: "Are you sure you want to add a jump location here?", preferredStyle: .alert)
let noAction = UIAlertAction(title: "No", style: .cancel, handler: nil)
let yesAction = UIAlertAction(title: "Yes", style: .default) { (action) in
// Segue takes user to JumpSpotCreatorController to input information about the jump location
self.performSegue(withIdentifier: "CreateJumpSpot", sender: self)
if self.jumpSpotCreatorController.doneButtonHasBeenPressed == true {
self.jumpSpotCreatorController.doneButtonHasBeenPressed = false
self.createJumpSpotAnnotation(coordinatesDeterminedByTouch: coordinatesTouchedToCreateAnnotation)
self.userIsAllowedToAddAnnotation = false
self.tapToAddJumpSpotLabel.isHidden = true
}
}
alertController.addAction(noAction)
alertController.addAction(yesAction)
present(alertController, animated: true, completion: nil)
} else {
return
}
}
}
// Force unwrap is okay because this will only be called if 'Done' button is pressed in the JumpSpotCreatorController, which mandates that those inputs not be nil.
func createJumpSpotAnnotation(coordinatesDeterminedByTouch: CLLocationCoordinate2D) {
mapView.addAnnotation(JumpSpotAnnotation(name: jumpSpotCreatorController.nameTextField.text!, coordinate: coordinatesDeterminedByTouch, estimatedHeight: jumpSpotCreatorController.estimatedHeightTextField.text!, locationDescription: jumpSpotCreatorController.descripitionTextView.text, warnings: jumpSpotCreatorController.warningsTextView.text ?? "", image: jumpSpotCreatorController.jumpSpotImageView.image ?? UIImage(imageLiteralResourceName: "Image-1")))
}
如您所见,在 touchesEnded 函数中创建注释的代码块(位于我向 alertController 添加操作的正上方,以防您找不到它。它大约有 4 行)是立即执行,而不是在我需要时执行,即在我的其他视图控制器 (JumpSpotCreatorController) 中按下 'Done' 按钮后。我尝试使用 doneButtonHasBeenPressed 变量修复它,但它没有任何区别(原因很明显)。我怎样才能在按下完成按钮后才执行它?我无法将另一个视图控制器初始化为主视图控制器中的对象(带有 touchesEnded 的是主视图控制器),因为它会在两个视图控制器之间创建一个无限循环的引用。 DispatchQueue 能以某种方式提供帮助吗?我已经研究了几个小时,但不太清楚如何在这里应用它。非常感谢。
我没有 100% 关注,但这有帮助吗:
按钮被点击并且你在“A”
“A”调用 B 做某事,AND,
“A”传递对自身的引用
(也就是说,为B中的那个调用“添加一个参数”,它是class“A”的一个变量。假设这个变量被命名为“callMeWhenYou'reFinished”)
当“B”完成它必须做的事情时
只需调用 callMeWhenYou'reFinished#WhateverFunction
...你走吧!
作为一种“一般编程问题”(忘记 Swift 中可用的内容等),您只是在描述“回调”。如果我没听错,希望对你有所帮助!
进一步:
有一些循环引用是完全可以的。 (所以,A 知道 B 并且 B 得到了对 A 中某事的回调。)
显然,如果您随后错误地进行了无限循环,那就是无限循环。但是“不小心造成无限循环”与您是否有单独的 classes、引用等完全没有关系
如果我没猜错,你正在尝试得到这样的东西:
Class A {
weak var referenceToB: B?
@IBAction func buttonAction(_ sender: Any) {
guard var referenceToB = referenceToB else {
fatalError("referenceToB not set!")
}
referenceToB!.otherFunction()
}
}
Class B {
func otherFunction() {
//stuff
}
}
在你的例子中,在你实例化你的 VC 之后,分配一个对包含所需功能的 class 对象的引用。
有多种方法可以做到这一点 --- 各有利弊。
此方法使用委托/协议模式。
我们定义了一个协议,它将允许 类 在他们的委托中执行函数 类:
// protocol / delegate pttern
protocol JumpSpotDelegate: class {
func createJumpSpotAnnotation(_ name: String, estimatedHeight: String, locationDescription: String, warnings: String, image: UIImage?)
func cancelAnnotation()
}
在您的地图视图的控制器中,我们符合该委托:
class MapViewController: UIViewController, JumpSpotDelegate {
在JumpSpotCreatorController
中,我们设置了一个delegate
属性:
class JumpSpotCreatorController: UIViewController {
weak var delegate: JumpSpotDelegate?
当我们导航到 JumpSpotCreatorController
时,我们将自己指定为其委托:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// make sure we're acting on the correct segue
if segue.identifier == "CreateJumpSpot", let vc = segue.destination as? JumpSpotCreatorController {
// set the delegate in the JumpSpotCreatorController we're navigating to
vc.delegate = self
}
}
在JumpSpotCreatorController
中,当点击完成按钮时,我们通过委托函数告诉地图控制器:
delegate?.createJumpSpotAnnotation(name, estimatedHeight: estimatedHeight, locationDescription: description, warnings: warnings, image: img)
一起来了。我通过 let
语句添加了对象来编写此...我希望您将它们作为 @IBOutlet
连接:
// protocol / delegate pttern
protocol JumpSpotDelegate: class {
func createJumpSpotAnnotation(_ name: String, estimatedHeight: String, locationDescription: String, warnings: String, image: UIImage?)
func cancelAnnotation()
}
class MapViewController: UIViewController, JumpSpotDelegate {
// this will hold the touch point while we navigate to and back from the JumpSpotCreatorController
var lastTouch: CLLocationCoordinate2D?
let mapView: MKMapView!
var userIsAllowedToAddAnnotation = true
let tapToAddJumpSpotLabel = UILabel()
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let touchLocation = touch.location(in: view)
// Converts CGPoint coordinates and UIView to CLLocationCordinate2D (map coordinates)
// store in class property
self.lastTouch = mapView.convert(touchLocation, toCoordinateFrom: view)
if userIsAllowedToAddAnnotation {
let alertController = UIAlertController(title: "Confirm", message: "Are you sure you want to add a jump location here?", preferredStyle: .alert)
let noAction = UIAlertAction(title: "No", style: .cancel, handler: nil)
let yesAction = UIAlertAction(title: "Yes", style: .default) { (action) in
// Segue takes user to JumpSpotCreatorController to input information about the jump location
self.performSegue(withIdentifier: "CreateJumpSpot", sender: self)
}
alertController.addAction(noAction)
alertController.addAction(yesAction)
present(alertController, animated: true, completion: nil)
} else {
return
}
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// make sure we're acting on the correct segue
if segue.identifier == "CreateJumpSpot", let vc = segue.destination as? JumpSpotCreatorController {
// set the delegate in the JumpSpotCreatorController we're navigating to
vc.delegate = self
}
}
// called by Button action in JumpSpotCreatorController
func createJumpSpotAnnotation(_ name: String, estimatedHeight: String, locationDescription: String, warnings: String, image: UIImage?) {
// the coordinate parameter was stored in our class property "lastTouch"
guard let lastTouch = self.lastTouch else {
// self.lastTouch was not set!
return
}
mapView.addAnnotation(JumpSpotAnnotation(name: name, coordinate: lastTouch, estimatedHeight: estimatedHeight, locationDescription: description, warnings: warnings, image: image ?? UIImage(imageLiteralResourceName: "Image-1")))
self.userIsAllowedToAddAnnotation = false
self.tapToAddJumpSpotLabel.isHidden = true
// pop from JumpSpotCreatorController back to self
self.navigationController?.popViewController(animated: true)
}
// I'm assuming you would also have a Cancel button?
func cancelAnnotation() {
self.lastTouch = nil
// pop from JumpSpotCreatorController back to self
self.navigationController?.popViewController(animated: true)
}
}
class JumpSpotCreatorController: UIViewController {
weak var delegate: JumpSpotDelegate?
let nameTextField = UITextField()
let estimatedHeightTextField = UITextField()
let descripitionTextView = UITextView()
let warningsTextView = UITextView()
let jumpSpotImageView = UIImageView()
@IBAction func doneBtnTapped() {
// presumably, you'll validate all these
guard let name = nameTextField.text,
let estimatedHeight = estimatedHeightTextField.text,
let description = descripitionTextView.text,
let warnings = warningsTextView.text,
let img = jumpSpotImageView.image else {
// notify user required fields were blank
return
}
delegate?.createJumpSpotAnnotation(name, estimatedHeight: estimatedHeight, locationDescription: description, warnings: warnings, image: img)
}
@IBAction func cancelBtnTapped() {
delegate?.cancelAnnotation()
}
}