微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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

如何解决按NavigationItem按钮时,活动指示器未设置动画

我尝试在按下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")
        }
    }

收集端口的代码

   // MARK: - Port Scaner
    // Get number of threads for scan ports
    func getSegmentsQueues(min: Int,max: Int,maxPerSegment: Int) -> [[Int]] {
        
        var start: Int = min
        var portSegments = [[Int]]()
        
        while start <= max {
            var _portSegment = [Int]()
            
            for _ in 1...maxPerSegment {
                
                if start <= max {
                    _portSegment.append(start)
                }
                
                start += 1
            }
            
            portSegments.append(_portSegment)
        }
        
        return portSegments
    }


    // Crate queques for scan ports by segments
    func QueuedispatchPort(address: String,minPort: Int,maxPort: Int,segmentsQueues: (Int,Int,Int) -> [[Int]]) -> [Int] {
        var openPorts : [Int] = []
        let segmentPorts = segmentsQueues(minPort,maxPort,1);
        
        let group = dispatchGroup()
        
        for segment in segmentPorts {
            group.enter()
            dispatchQueue.global().async {
                
                for port in segment {
                    let client = TCPClient(address: address,port: Int32(port))
                    switch client.connect(timeout: 2) {
                        case .success:
                            openPorts.append(port)
                        
                        case .failure(_):
                            print("port \(port) closed")
                    }
                    
                    client.close()
                }
                group.leave()
            }
        }
        
        group.wait()

        return openPorts
    }
    
    // Scans ports from an address and a range given by the user
    func scanPorts(address : String,start : Int,stop : Int) -> [Int] {
        let openPorts = QueuedispatchPort(
            address: address,minPort: start,maxPort: stop,segmentsQueues:
            getSegmentsQueues(min:max:maxPerSegment:))
        
        return openPorts
    }

代码更新,我将代码块(扫描端口)放在主线程上,这次删除stopAnimating()。在长期返回代码后(在dispatchQueue.main中是什么),对activityIndi​​cator进行动画处理。还是不行...

@objc func startScan() {
        scanerIndicator.startAnimating()
        
        dispatchQueue.main.async { [self] in
            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,stop: stop)
                            print("Open Open: \(openPorts)")
                            if !openPorts.isEmpty {
                                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")
            }
        }
    }

解决方法

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

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

  1. 开始制作动画活动指示器
  2. 执行长时间运行的任务(在主线程上)
  3. 停止动画活动指标

然后您再也看不到动画了。问题在于动画要等到您的代码返回并且您的应用访问其事件循环后才能开始。

您需要像这样编写代码:

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

我做什么?

  1. 首先,我将两个端口扫描代码合并为1个功能。
  2. 将耗时的代码放入DispatchQueue.global(qos: .default).async中。我首先尝试使用主线程,但是失败了,而且长时间运行的代码也应该在后台线程中。 global()是后台线程吗?我待会儿再挖:)
  3. 在繁重的代码完成后,使用@escapingcompleteHandler作为回调,将openPorts提供给调用方。因为在这种情况下您不能使用返回值,因为我们不知道繁重的代码何时完成,所以我们使用回调将参数传递回调用方。
    // 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)
        }
    }

调用方:只记得在此处将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")
        }
    }

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。