簡體   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