Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/swift/17.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Swift 按navigationItem按钮时,活动指示器未设置动画_Swift_Uiactivityindicatorview - Fatal编程技术网

Swift 按navigationItem按钮时,活动指示器未设置动画

Swift 按navigationItem按钮时,活动指示器未设置动画,swift,uiactivityindicatorview,Swift,Uiactivityindicatorview,我尝试在按下navigationItem按钮时触发活动指示器的动画。但我发现活动指示器没有旋转。我尝试将scanerIndicator.startAnimating()放入主线程,但没有任何帮助 代码是从路由器打开的端口收集的,我想在按下navigationItem按钮时开始旋转,在返回openPorts时停止旋转。感谢您提供有关哪里出错的任何线索/提示 override func viewDidLoad() { super.viewDidLoad() .

我尝试在按下navigationItem按钮时触发活动指示器的动画。但我发现活动指示器没有旋转。我尝试将
scanerIndicator.startAnimating()
放入主线程,但没有任何帮助

代码是从路由器打开的端口收集的,我想在按下navigationItem按钮时开始旋转,在返回openPorts时停止旋转。感谢您提供有关哪里出错的任何线索/提示

    override func viewDidLoad() {
        super.viewDidLoad()
        ...
        
        navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Start", style: .plain, target: self, action: #selector(startScan))
        ...
    }

    @objc func startScan() {
        scanerIndicator.startAnimating()
        
        if let address = serverAddress.text, !address.isEmpty {
            if let start = Int(startPort.text!) {
                if let stop = Int(stopPort.text!) {
                    if start < stop {
                        openPorts = netUtility.scanPorts(address: address, start: start, stop: stop)
                        print("Open Open: \(openPorts)")
                        if !openPorts.isEmpty {
                            scanerIndicator.stopAnimating()
                            table.reloadData()
                        } else {
                            showErrorMessage(errorTitle: "Not at all", errorMessage: "No open ports were found")
                        }
                    } else {
                        showErrorMessage(errorTitle: "Range error", errorMessage: "Start port should be smaller than stop port")
                    }
                } else {
                    showErrorMessage(errorTitle: "Empty fields", errorMessage: "Please fill all the necessary data")
                }
            } else {
                showErrorMessage(errorTitle: "Empty fields", errorMessage: "Please fill all the necessary data")
            }
        } else {
            showErrorMessage(errorTitle: "Empty fields", errorMessage: "Please fill all the necessary data")
        }
    }

要理解您的代码在做什么有点困难,但我的猜测是,即使您使用队列进行端口扫描,因为您使用的是DispatchGroup,代码会一直阻塞,直到所有端口扫描完成

如果您有执行以下操作的同步代码:

  • 开始设置活动指示器的动画
  • 执行长时间运行的任务(在主线程上)
  • 停止设置活动指示器的动画
  • 那你就永远看不到动画了。问题在于,只有在代码返回且应用程序访问其事件循环时,动画才会启动

    相反,您需要这样编写代码:

    scanerIndicator.startAnimating()
    DispatchQueue.main.async {
       //Do long-running task
       scanerIndicator.stopAnimating()
    }
    
    这是因为在调用
    startAnimating()
    之后,您将向主调度队列(在主线程上)添加一个异步调用,然后返回。应用程序的函数调用堆栈返回所有值,应用程序访问事件循环,活动指示器开始旋转。然后,系统接收添加到主队列中的异步任务,并开始运行该任务。最后,当长时间运行的任务完成时,关闭活动指示器(在内部调用的代码中)
    async()
    调用。

    最后,我终于让这段代码正常工作了,
    DispatchQueue
    真的让我一整天都在焦头烂额

    我的固定流程:

        func abc() {
            activityIndicator.startAnimating()
            
            DispatchQueue.global(qos: .default).async {
                // put your heavy code here
                
                DispatchQueue.main.async {
                    // UI code must be on main thread
                    activityIndicator.stopAnimating()
                }
            }
        }
    
    
    我做什么

  • 首先,我将两个端口扫描代码合并为一个函数
  • 将耗时较长的代码放入
    DispatchQueue.global(qos:.default).async
    。我尝试先使用主线程,但失败了,而且长时间运行的代码应该在后台线程中。是
    global()
    是后台线程吗?我稍后会深入了解:)
  • 当这段繁重的代码完成时,使用@escaping completionHandler作为回调函数将openPorts提供给调用方。因为在这种情况下不能使用返回值,因为我们不知道重代码何时完成,所以我们使用callback将参数传递回调用方
  • 调用者:记住在这里把UI代码放在主线程上

    @objc func startScan() {
            scanerIndicator.startAnimating()
            view.endEditing(true)
            self.view.isUserInteractionEnabled = false
            
            if let address = serverAddress.text, !address.isEmpty {
                if let start = Int(startPort.text!) {
                    if let stop = Int(stopPort.text!) {
                        if start < stop {
                            netUtility.scanPorts(address: address, start: start, stop: stop) { [self] (availablePorts) in
                                openPorts = availablePorts
                                print("$Open ports: \(self.openPorts)")
                                if !openPorts.isEmpty {
                                    DispatchQueue.main.async {
                                        table.reloadData()
                                        self.scanerIndicator.stopAnimating()
                                    }
                                } else {
                                    showErrorMessage(errorTitle: "Not at all", errorMessage: "No open ports were found")
                                }
                            }
                        } else {
                            showErrorMessage(errorTitle: "Range error", errorMessage: "Start port should be smaller than stop port")
                        }
                    } else {
                        showErrorMessage(errorTitle: "Empty fields", errorMessage: "Please fill all the necessary data")
                    }
                } else {
                    showErrorMessage(errorTitle: "Empty fields", errorMessage: "Please fill all the necessary data")
                }
            } else {
                showErrorMessage(errorTitle: "Empty fields", errorMessage: "Please fill all the necessary data")
            }
        }
    
    @objc func startScan(){
    scanerIndicator.startAnimating()
    view.endEditing(真)
    self.view.isUserInteractionEnabled=false
    如果let address=serverAddress.text,!address.isEmpty{
    如果让start=Int(startPort.text!){
    如果let stop=Int(stopPort.text!){
    如果开始<停止{
    扫描端口(地址:地址,开始:开始,停止:停止){[self](可用的报告)在
    openPorts=可用的端口
    打印(“$Open ports:\(self.openPorts)”)
    如果!openPorts.isEmpty{
    DispatchQueue.main.async{
    表.重新加载数据()
    self.scanerIndicator.stopAnimating()
    }
    }否则{
    淋浴错误消息(错误标题:“根本没有”,错误消息:“未找到打开的端口”)
    }
    }
    }否则{
    淋浴错误信息(错误标题:“范围错误”,错误信息:“开始端口应小于停止端口”)
    }
    }否则{
    淋浴信息(errorTitle:“空白字段”,errorMessage:“请填写所有必要的数据”)
    }
    }否则{
    淋浴信息(errorTitle:“空白字段”,errorMessage:“请填写所有必要的数据”)
    }
    }否则{
    淋浴信息(errorTitle:“空白字段”,errorMessage:“请填写所有必要的数据”)
    }
    }
    
    谢谢,我尝试将长期运行代码放在主线程上,这一次,在返回长期运行代码后,指示器将设置动画。请看我的更新。我不明白你在说什么。您说“这一次,在返回长时间运行的代码之后,指示器将被设置为动画”。您是说,在进行更改之后,指示器将在扫描代码运行时旋转?“我的意思是,在返回长时间运行的代码后,指示器会旋转。好的,我已经检查了这一整天的问题,我还创建了一些简单的项目进行比较,并证明您的答案是正确的。因此,问题应该出现在我收集开放端口的代码中。我现在还在检查。谢谢邓肯,我现在已经修好了!
        func abc() {
            activityIndicator.startAnimating()
            
            DispatchQueue.global(qos: .default).async {
                // put your heavy code here
                
                DispatchQueue.main.async {
                    // UI code must be on main thread
                    activityIndicator.stopAnimating()
                }
            }
        }
    
    
        // Scans ports from an address and a range given by the user
        func scanPorts(address : String, start : Int, stop : Int, completion: @escaping ([Int]) -> ()) {
            
            DispatchQueue.global(qos: .default).async {
                for port in start...stop {
                    let client = TCPClient(address: address, port: Int32(port))
                    switch client.connect(timeout: 2) {
                        case .success:
                            self.openPorts.append(port)
                            print("HH: port: \(self.openPorts)")
                        case .failure(_):
                            print("port \(port) closed")
                    }
                    client.close()
                }
                completion(self.openPorts)
            }
        }
    
    @objc func startScan() {
            scanerIndicator.startAnimating()
            view.endEditing(true)
            self.view.isUserInteractionEnabled = false
            
            if let address = serverAddress.text, !address.isEmpty {
                if let start = Int(startPort.text!) {
                    if let stop = Int(stopPort.text!) {
                        if start < stop {
                            netUtility.scanPorts(address: address, start: start, stop: stop) { [self] (availablePorts) in
                                openPorts = availablePorts
                                print("$Open ports: \(self.openPorts)")
                                if !openPorts.isEmpty {
                                    DispatchQueue.main.async {
                                        table.reloadData()
                                        self.scanerIndicator.stopAnimating()
                                    }
                                } else {
                                    showErrorMessage(errorTitle: "Not at all", errorMessage: "No open ports were found")
                                }
                            }
                        } else {
                            showErrorMessage(errorTitle: "Range error", errorMessage: "Start port should be smaller than stop port")
                        }
                    } else {
                        showErrorMessage(errorTitle: "Empty fields", errorMessage: "Please fill all the necessary data")
                    }
                } else {
                    showErrorMessage(errorTitle: "Empty fields", errorMessage: "Please fill all the necessary data")
                }
            } else {
                showErrorMessage(errorTitle: "Empty fields", errorMessage: "Please fill all the necessary data")
            }
        }