有什么方法可以访问在 IBAction 中修改的变量吗?

Are there any ways to access a variable modified in an IBAction?

这是我正在尝试做的事情:

var usernameCheckerResponse : String = ""

 //This IBAction is a UITextfield that sends post request when editing is finshed.
 @IBAction func usernameChecker(_ sender: Any) {

 // perform post request with URLSession
 // post request returns url response from URLSession 
 // the value of this response is either 'usernameExists' or 'usernameAvailable'
 // usernameCheckerResponse = String(describing : response) 

}
//use modified usernameCheckerResponse variable outside the IBAction function. 
//For example like this:

   func UsernameExists () -> Bool {
   if(usernameCheckerResponse == "usernameExists"){
   return true
  } else { return false }
}

我知道 IBAction 只会 return 无效,所以这个问题是否存在? 任何帮助 and/or 的建议将不胜感激。

绝对是。这是一个例子,

var usernameCheckerResponse : String = ""

 //This IBAction is a UITextfield that sends post request when editing is finshed.
 @IBAction func usernameChecker(_ sender: Any) {

 //post request
 // post request returns url response
 // usernameCheckerResponse = String(describing : response) 

}
//use modified usernameCheckerResponse variable outside the IBAction function.

func accessVariable() {
   print("\(usernameCheckerResponse")
}

请记住,这里的技巧是在变量发生变化时访问它。为此,您需要选择某种方式来跟踪它。委派可能是最标准的方式。 See this. 你必须更具体地说明你为什么要更改变量,因为我需要知道什么在使用它(委托要求你非常具体地说明谁在参与)。

我还想更具体地说明委派的工作原理。您将指定何时调用 'accessVariable()' 函数,在您想要修改变量的位置(这总是在两个不同的 classes 或结构之间)。请记住,如果您只是想在同一个 class 中共享变量,则不需要使用委托。调用函数 'accessVariable()' 就足够了。但是,如果在这种情况下您希望某些事情在同一个 class 中发生,但您确实想控制函数完成的顺序,那么您需要使用回调。

顺便说一句 Leo,这样做会使应用程序崩溃...

一般来说,您应该将 IBAction 函数视为 按钮等控件的连接点。 你永远不会自己称呼它。 如果您需要这样做,请创建另一个功能 并让 IBAction 函数调用它。

由于您使用 URLSession 从外部获取数据 源,您需要注意这不会同步发生。 将呼叫发送到您的 API 并调用完成处理程序 返回数据时。

所有这些代码都会进入您的 ViewController

// Set up a reusable session with appropriate timeouts
internal static var session: URLSession  {
    let sessionConfig = URLSessionConfiguration.default
    sessionConfig.timeoutIntervalForRequest = 6.0
    sessionConfig.timeoutIntervalForResource = 18.0

    return URLSession( configuration: sessionConfig )
}


// Create an httpPost function with a completion handler
// Completion handler takes :
//   success: Bool true/false if things worked or did not work
//   value: String string value returned or "" for failures
//   error: Error? the error object if there was one else nil
func httpPost(_ apiPath: String, params: [String: String], completion:@escaping (Bool, String, Error?) -> Void) {
    // Create POST request
    if let requestURL = URL( string: apiPath ) {
        print("requestUrl \(apiPath)")
        // Create POST request
        var request = URLRequest( url: requestURL )
        request.httpMethod = "POST"

        var postVars : [String : String ] = params
        var postString = postVars.toHttpArgString()

        request.httpBody = postString.data( using: String.Encoding.utf8, allowLossyConversion: true )

        let sendTask = ViewController.session.dataTask( with: request) {
            (data, response, error) in

            if let nserror = error as NSError? {
                // There was an error
                // Log it or whatever
                completion(false, "", error)
                return
            }

            // Here you handle getting data into a suitable format

            let resultString = "whatever you got from api call"
            // Send it back to the completion block
            completion(true, resultString, nil)
        }
        sendTask.resume()
    }
}
// I assume you have a text field with the user name you want to try
@IBOutlet weak var usernameToCheck : UITextField!
@IBAction func usernameChecker(_ sender: Any) {
    guard let username = usernameToCheck.text else {
        // This is unlikely to happen but just in case.
        return
    }
    httpPost("https://someapicall", params: ["username" : username] ) {
        (success, value, error) in
        // This code gets called when the http request returns data.
        // This does not happen on the main thread.
        if success {
            if value == "usernameExists" {
                // User name already exists. Choose a different one.
                DispatchQueue.main.async {
                    // put code here if you need to do anything to the UI, like alerts, screen transitions etc.
                }
            }
            else if value == "usernameAvailable" {
                // You can use this user name
                DispatchQueue.main.async {
                    // put code here if you need to do anything to the UI, like alerts, screen transitions etc.
                }
            }
            else {
                // Unexpected response from server
            }

        }
        else {
            // Something did not work
            // alert "Unable to connect to server"
        }

    }
}

要使此代码正常工作,您需要:

// Syntatic sugar to convert [String:String] to http arg string
protocol ArgType {}
extension String: ArgType {}
extension Dictionary where Key: ArgType,  Value: ArgType  {
    // Implement using a loop
    func toHttpArgString() -> String {
        var r = String()
        for (n, v) in self {
            if !r.isEmpty { r += "&" }
            r += "\(n)=\(v)"
        }
        return r
    }

}