繁体   English   中英

XCODE SWIFT 如何在按下按钮后执行代码,但在另一个文件/类中?

[英]XCODE SWIFT How do I execute code after a button is pressed, but in another file/class?

我正在使用 MapKit,用户可以添加注释。 他们可以点击屏幕,提示他们是否要添加带有 UIAlert 的注释,如果他们说是,它会显示另一个视图 controller,以便用户可以输入有关注释的信息,例如位置名称、描述等. 该视图 controller 顶部有一个“完成”BarButtonItem 以确认他们输入的信息,并创建注释。

@IBAction func doneButtonPressed(_ sender: UIBarButtonItem) {
        doneButtonHasBeenPressed = true
        dismiss(animated: true, completion: nil)
}

The problem is, The annotation has to be created in my 'touchesEnded' function in the original view controller, that sent the user to the view controller where they input the annotation information, because that is where it gets the CLCoordinate2D from (using the touchesEnded )。 在同样的 touchesEnded function 中,我将用户发送到下一个视图 controller。 这是 touchesEnded 代码和它使用的助手 function:

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 function 中创建注释的代码块(位于我向 alertController 添加操作的正上方,以防您找不到它。大约 4 行)立即执行,而不是当我需要它时,即在我的另一个视图 controller (JumpSpotCreatorController) 中按下“完成”按钮后。 我尝试使用 doneButtonHasBeenPressed 变量修复它,但它没有区别(原因很明显)。 只有在按下完成按钮后,我才能执行它? 我无法在主视图中将另一个视图 controller 初始化为 object(带有 touchesEnded 的视图是主视图),因为它会在两个视图控制器之间创建一个无限循环的引用。 DispatchQueue 能以某种方式提供帮助吗? 我已经研究了几个小时,但不太清楚如何在这里应用它。 非常感谢。

如果我做对了,您正试图得到这样的东西:

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 后,分配对包含所需 function 的 class 的 object 的引用。

我不遵循 100% 但这会有所帮助:

  • 按钮被点击,你在“A”

  • “A”要求 B 做某事,并且,

  • “A”传递对自身的引用

(也就是说,给B中的那个调用“添加一个参数”,它是class“A”的变量。假设该变量名为“callMeWhenYou'reFinished”)

  • 当“B”完成它必须做的事情时

  • 只需调用 callMeWhenYou'reFinished#WhateverFunction

......你走吧!

作为一种“一般编程问题”(忘记 Swift 等可用的内容),您只是在描述“回调”。 如果我确实正确地关注了您,希望对您有所帮助!


更远:

有某种循环的引用是完全可以的。 (所以,A 知道 B 并且 B 被给予了 A 中某事的回调。)

显然,如果您随后错误地进行了无限循环,那就是无限循环。 但是“不小心造成了无限循环”与您是否有单独的类、引用等完全没有关系。

有很多方法可以做到这一点——每一种都有优点和缺点。

这种方法使用委托/协议模式。

我们定义了一个协议,它允许类在它们的委托类中执行函数:

// protocol / delegate pttern
protocol JumpSpotDelegate: class {
    func createJumpSpotAnnotation(_ name: String, estimatedHeight: String, locationDescription: String, warnings: String, image: UIImage?)
    func cancelAnnotation()
}

在 controller 和您的 map 视图中,我们符合该委托:

class MapViewController: UIViewController, JumpSpotDelegate {

JumpSpotCreatorController中,我们设置了一个delegate属性:

class JumpSpotCreatorController: UIViewController {
    weak var delegate: JumpSpotDelegate?

当我们导航到JumpSpotCreatorController时,我们将 self 指定为其委托:

    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中,当点击完成按钮时,我们通过委托 function 告诉 map controller:

    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()
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM