简体   繁体   English

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

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

I am using MapKit, and the user has the ability to add annotations.我正在使用 MapKit,用户可以添加注释。 They can tap on the screen, which prompts them if they want to add an annotation with a UIAlert, and If they say yes, it presents another view controller so the user can input information about the annotation, like the location name, description, etc. That view controller has a 'Done' BarButtonItem up top to confirm the information that they input, and create the annotation.他们可以点击屏幕,提示他们是否要添加带有 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). 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 )。 It is in that same touchesEnded function where I send the user to that next view controller.在同样的 touchesEnded function 中,我将用户发送到下一个视图 controller。 Here is the touchesEnded code and a helper function it uses:这是 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")))
}

As you can see, the block of code that creates the Annotation in the touchesEnded function (located right above where I add actions to the alertController, in case you can't find it. It's about 4 lines), is executed immediately, as opposed to when I need it to be, which is once the 'Done' button is pressed in my other view controller (JumpSpotCreatorController).如您所见,在 touchesEnded function 中创建注释的代码块(位于我向 alertController 添加操作的正上方,以防您找不到它。大约 4 行)立即执行,而不是当我需要它时,即在我的另一个视图 controller (JumpSpotCreatorController) 中按下“完成”按钮后。 I tried fixing that with the doneButtonHasBeenPressed variable, but it makes no difference (for obvious reasons).我尝试使用 doneButtonHasBeenPressed 变量修复它,但它没有区别(原因很明显)。 How can I execute it only once that done button is pressed?只有在按下完成按钮后,我才能执行它? I can't initialize the other view controller as an object in the main one (the one with touchesEnded is the main one) because it will create an infinite loop of references between the two view controllers.我无法在主视图中将另一个视图 controller 初始化为 object(带有 touchesEnded 的视图是主视图),因为它会在两个视图控制器之间创建一个无限循环的引用。 Can DispatchQueue help in some way? DispatchQueue 能以某种方式提供帮助吗? I've researched it for hours but can't quite figure out how to apply it here.我已经研究了几个小时,但不太清楚如何在这里应用它。 Thanks a lot.非常感谢。

If I got it right you are trying to get something like this:如果我做对了,您正试图得到这样的东西:

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

In your case, after you have instantiated your VC assign a reference to an object of the class containing the desired function.在您的情况下,在您实例化您的 VC 后,分配对包含所需 function 的 class 的 object 的引用。

I don't follow 100% but would this help:我不遵循 100% 但这会有所帮助:

  • button is tapped and you are in "A"按钮被点击,你在“A”

  • "A" calls to B to do something, AND, “A”要求 B 做某事,并且,

  • "A" passes along a reference to itself “A”传递对自身的引用

(that is to say, "add an argument" to that call in B, which is a variable of class "A". let's say that variable is named "callMeWhenYou'reFinished") (也就是说,给B中的那个调用“添加一个参数”,它是class“A”的变量。假设该变量名为“callMeWhenYou'reFinished”)

  • when "B" finishes doing what it has to do当“B”完成它必须做的事情时

  • simply call callMeWhenYou'reFinished#WhateverFunction只需调用 callMeWhenYou'reFinished#WhateverFunction

... and away you go! ......你走吧!

As a sort of "general programming issue" (forgetting about what is available in Swift, etc) you're just describing "a callback".作为一种“一般编程问题”(忘记 Swift 等可用的内容),您只是在描述“回调”。 If I did follow you correctly, hope it helps!如果我确实正确地关注了您,希望对您有所帮助!


Further:更远:

It's perfectly ok to have references that are sort of circular.有某种循环的引用是完全可以的。 (So, A knows about B and B is given a callback to something in A.) (所以,A 知道 B 并且 B 被给予了 A 中某事的回调。)

Obviously, if you then by mistake make an infinite loop, that's an infinite loop.显然,如果您随后错误地进行了无限循环,那就是无限循环。 But "accidentally making an infinite loop" has absolutely no connection to whether or not you have separate classes, references, etc.但是“不小心造成了无限循环”与您是否有单独的类、引用等完全没有关系。

There are a number of ways to do this --- each with pros and cons.有很多方法可以做到这一点——每一种都有优点和缺点。

This approach uses the delegate / protocol pattern.这种方法使用委托/协议模式。

We define a protocol, which will allow classes to execute functions in their delegate classes:我们定义了一个协议,它允许类在它们的委托类中执行函数:

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

In the controller with your map view, we conform to that delegate:在 controller 和您的 map 视图中,我们符合该委托:

class MapViewController: UIViewController, JumpSpotDelegate {

In JumpSpotCreatorController , we set up a delegate property:JumpSpotCreatorController中,我们设置了一个delegate属性:

class JumpSpotCreatorController: UIViewController {
    weak var delegate: JumpSpotDelegate?

When we navigate to the JumpSpotCreatorController , we assign self as its delegate:当我们导航到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
        }
    }

In JumpSpotCreatorController , when Done button is tapped, we tell the map controller via the delegate function:JumpSpotCreatorController中,当点击完成按钮时,我们通过委托 function 告诉 map controller:

    delegate?.createJumpSpotAnnotation(name, estimatedHeight: estimatedHeight, locationDescription: description, warnings: warnings, image: img)

Here it is together.这里是一起的。 I added objects via let statements to write this... I expect you have them as @IBOutlet connections:我通过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.

相关问题 如何在swift文件中获取类的引用,并在另一个swift文件中进行编码? -xcode /迅速 - How do i get a reference of a class in a swift file and code that in another swift file? - xcode / swift iOS Xcode(swift) - 如何在展开segue后执行代码 - iOS Xcode (swift) - how to execute code after unwind segue 按下按钮后迅速出现 SIGABRT 错误-segue 不执行 - SIGABRT error in swift after a button is pressed - the segue doesn't execute 断点后如何在Xcode中查看此Swift类对象的内容? - How do I see contents of this Swift class object in Xcode after breakpoint? 如何在XCode UI中将情节提要线连接到Swift代码? - How do I wire up the storyboard to Swift code in XCode UI? 如何在 Swift 中每 5 分钟执行一次代码? - How do I execute code once in a 5 minute in Swift? 在位置访问权限中按下允许按钮后如何执行动作? - how to execute an action after allow button is pressed in the location access permission? 在Swift 3中按下按钮时如何转到另一个视图控制器 - How to go to another view controller when button is pressed in swift 3 按下按钮时如何使标签滑到屏幕上? SWIFT 2 - How do I make a label slide onto the screen when a button is pressed? SWIFT 2 每次按下按钮时,如何从标签“26”中减去 1? (迅捷初学者) - How do I subtract 1 from a label "26" everytime a button is pressed? (Swift Beginner)
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM