Я хотел опубликовать созданное мной решение, в котором будет отображаться индикатор выполнения в отдельном окне, показывающий прогресс с результатами, которые могут быть перечислены.
Прикрепленное изображение ниже того, как оно выглядит.
![enter image description here](https://i.stack.imgur.com/ifyY8.png)
Совместимость: Swift 3
Особенности: Код с резьбойToRun & Cancel + Кнопка Готово.Автоматически показывает оставшуюся сумму прогресса, если вы хотите использовать функцию updateProgress.
Код очень прост и легко модифицируется, это просто раскадровка и контроллер представления.
- Используйте приведенный ниже XML-код и сохраните его под финальным именем, которое я предлагаю ниже, где-нибудь в вашем проекте.
ProgressWindow.storyboard
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11542" systemVersion="16B2555" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11524"/>
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Progress-->
<scene sceneID="5gE-ws-FsC">
<objects>
<viewController storyboardIdentifier="progressWindow" title="Progress" id="IB9-Dc-dCV" customClass="ProgressWindowViewController" customModule="WorkOrders" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Lvc-9P-nmJ"/>
<viewControllerLayoutGuide type="bottom" id="xJS-yG-jWM"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="iDk-68-mde">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Please Wait..." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="9ui-W7-ucD">
<rect key="frame" x="16" y="84" width="343" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<progressView opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ov7-yH-A5z">
<rect key="frame" x="16" y="113" width="343" height="2"/>
</progressView>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" editable="NO" text="Starting Up..." textAlignment="natural" selectable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bwA-YT-FcE">
<rect key="frame" x="16" y="123" width="343" height="464"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
</textView>
<navigationBar contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="RPs-Mo-Cfx">
<rect key="frame" x="-4" y="20" width="383" height="44"/>
<items>
<navigationItem title="Starting Sync..." id="Y87-LY-5o5">
<barButtonItem key="rightBarButtonItem" title="Cancel" id="AD3-in-E6j">
<connections>
<action selector="cancelNavItemButtonActionWithSender:" destination="IB9-Dc-dCV" id="IF1-MG-v2x"/>
</connections>
</barButtonItem>
</navigationItem>
</items>
</navigationBar>
<button opaque="NO" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="6lh-KK-lX1">
<rect key="frame" x="113" y="595" width="150" height="42"/>
<fontDescription key="fontDescription" type="system" pointSize="25"/>
<state key="normal" title="Done"/>
<state key="disabled" title="Please Wait..."/>
<connections>
<action selector="doneButtonActionWithSender:" destination="IB9-Dc-dCV" eventType="touchUpInside" id="KQH-Th-NAC"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="leadingMargin" secondItem="ov7-yH-A5z" secondAttribute="leading" id="9Gm-bd-GY6"/>
<constraint firstItem="xJS-yG-jWM" firstAttribute="top" secondItem="bwA-YT-FcE" secondAttribute="bottom" constant="20" id="CT1-0k-Skt"/>
<constraint firstItem="6lh-KK-lX1" firstAttribute="top" secondItem="bwA-YT-FcE" secondAttribute="bottom" constant="8" symbolic="YES" id="Fb8-eP-lxu"/>
<constraint firstItem="RPs-Mo-Cfx" firstAttribute="leading" secondItem="iDk-68-mde" secondAttribute="leadingMargin" constant="-20" id="JSY-Na-oAF"/>
<constraint firstItem="xJS-yG-jWM" firstAttribute="top" secondItem="6lh-KK-lX1" secondAttribute="bottom" constant="30" id="NHY-fO-W26"/>
<constraint firstAttribute="trailingMargin" secondItem="RPs-Mo-Cfx" secondAttribute="trailing" constant="-20" id="QxH-pj-oOA"/>
<constraint firstItem="RPs-Mo-Cfx" firstAttribute="top" secondItem="Lvc-9P-nmJ" secondAttribute="bottom" id="VIf-63-vaw"/>
<constraint firstAttribute="trailingMargin" secondItem="bwA-YT-FcE" secondAttribute="trailing" id="WxH-hu-ZVQ"/>
<constraint firstAttribute="leadingMargin" secondItem="bwA-YT-FcE" secondAttribute="leading" id="XEd-Ba-ZfL"/>
<constraint firstItem="bwA-YT-FcE" firstAttribute="top" secondItem="ov7-yH-A5z" secondAttribute="bottom" constant="8" id="Xjr-bH-ILB"/>
<constraint firstItem="6lh-KK-lX1" firstAttribute="centerY" secondItem="iDk-68-mde" secondAttribute="centerY" id="ZU1-pD-czP"/>
<constraint firstItem="ov7-yH-A5z" firstAttribute="top" secondItem="9ui-W7-ucD" secondAttribute="bottom" constant="8" symbolic="YES" id="avI-Ab-G29"/>
<constraint firstAttribute="leadingMargin" secondItem="9ui-W7-ucD" secondAttribute="leading" id="dse-zV-g00"/>
<constraint firstItem="6lh-KK-lX1" firstAttribute="centerX" secondItem="iDk-68-mde" secondAttribute="centerX" id="i5Q-oY-DdV"/>
<constraint firstAttribute="trailingMargin" secondItem="9ui-W7-ucD" secondAttribute="trailing" id="jqu-1f-IuA"/>
<constraint firstItem="9ui-W7-ucD" firstAttribute="top" secondItem="RPs-Mo-Cfx" secondAttribute="bottom" constant="20" id="nrH-ey-Zcm"/>
<constraint firstAttribute="trailingMargin" secondItem="ov7-yH-A5z" secondAttribute="trailing" id="qha-Es-6Au"/>
</constraints>
<variation key="default">
<mask key="constraints">
<exclude reference="ZU1-pD-czP"/>
<exclude reference="CT1-0k-Skt"/>
</mask>
</variation>
</view>
<connections>
<outlet property="cancelNavButton" destination="AD3-in-E6j" id="SJc-Bc-N6j"/>
<outlet property="currentProgressLabel" destination="9ui-W7-ucD" id="zij-yQ-MFX"/>
<outlet property="doneButton" destination="6lh-KK-lX1" id="rh2-RF-4ak"/>
<outlet property="navItemLabel" destination="Y87-LY-5o5" id="ijO-a7-TrD"/>
<outlet property="navigationBar" destination="RPs-Mo-Cfx" id="WEq-F4-Pup"/>
<outlet property="theProgressBar" destination="ov7-yH-A5z" id="FUE-9J-iBh"/>
<outlet property="theTextView" destination="bwA-YT-FcE" id="1sR-23-NZH"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="TH6-NB-Eos" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-492" y="1474"/>
</scene>
</scenes>
</document>
Сохраните следующий код ниже в свой проект с именем ProgressWindowViewController.swift
ProgressWindowViewController.swift
import UIKit
protocol progressWindowDelegate : class{
var titleToGive : String {get}
func codeToRun(progressWindowViewController:ProgressWindowViewController)
var codeToCancel : ()->() {get}
}
class ProgressWindowViewController: UIViewController {
@IBOutlet weak var theTextView: UITextView!
@IBOutlet weak var currentProgressLabel: UILabel!
@IBOutlet weak var theProgressBar: UIProgressView!
@IBOutlet weak var navItemLabel: UINavigationItem!
@IBOutlet weak var doneButton: UIButton!
@IBOutlet weak var cancelNavButton: UIBarButtonItem!
@IBOutlet weak var navigationBar: UINavigationBar!
//For showing network activity
var indicator: UIActivityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.gray)
//Sets delegate
weak var controllerDelegate:progressWindowDelegate? = nil
override func viewDidLoad() {
super.viewDidLoad()
navItemLabel.title = controllerDelegate!.titleToGive
//Run on the main thread first then in background thread.
DispatchQueue.main.async {
DispatchQueue.global(qos: .background).async{
self.controllerDelegate?.codeToRun(progressWindowViewController: self)
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func writeToProgressWindow(text:NSMutableAttributedString){
DispatchQueue.main.async(execute: {
print("dispatch_queue_get_main_queue -> writeToProgressWindow()")
self.theTextView.attributedText = text
})
}
func updateNetworkIndicator(active:Bool){
DispatchQueue.main.async(execute: {
if(active){
UIApplication.shared.isNetworkActivityIndicatorVisible = true
}else{
UIApplication.shared.isNetworkActivityIndicatorVisible = false
}
})
}
func updateProgress(updatetext:String,amount:Int,left:Int){
DispatchQueue.main.async(execute: {
print("dispatch_queue_get_main_queue -> updatingProgress()")
self.currentProgressLabel.text = updatetext+" : \(amount) / \(left)"
self.theProgressBar.setProgress(Float(amount/left), animated: true) //progress is represented as a percentage of the total
})
}
func updateProgressWindowWeFinished(title:String){
//Enable done button and Disable/Hide Cancel Button. Add Final Messages
DispatchQueue.main.async(execute: {
self.doneButton.isEnabled = true
self.navItemLabel.title = title
self.cancelNavButton.isEnabled = false
self.cancelNavButton.tintColor = UIColor.clear
})
}
@IBAction func cancelNavItemButtonAction(sender: UIBarButtonItem) {
//Run on the main thread first then in background thread.
DispatchQueue.main.async {
DispatchQueue.global(qos: .background).sync{
print("dispatch_queue_priority_default")
self.controllerDelegate?.codeToCancel()
}
self.dismiss(animated: true, completion: nil)//closes the window.
}
}
@IBAction func doneButtonAction(sender: UIButton) {
self.dismiss(animated: true, completion: nil)//closes the window.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
3.) Чтобы использовать васпросто установите контроллер представления, который вызвал его на progressWindowDelegate, и предоставьте необходимую информацию для связи с поповером.
![enter image description here](https://i.stack.imgur.com/H5mwD.png)
Ниже приведен пример кода ниже, если вам нужноэто:
SampleViewControllerWithYesButton.swift
class SyncViewController: UIViewController, progressWindowDelegate {
var codeToCancel = {print("code to cancel")}
var titleToGive = "Starting Sync..."
func codeToRun(progressWindowViewController:ProgressWindowViewController) {
print("code to run")
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
@IBAction func yesButtonAction(_ sender: UIButton) {
let storyboard = UIStoryboard(name: "ProgressWindow", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "progressWindow") as! ProgressWindowViewController
controller.controllerDelegate = self
self.present(controller, animated: true, completion: nil)
}
@IBAction func noButtonAction(_ sender: UIButton) {
tabBarController?.selectedIndex = 1 //Send them to the list then.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
Все, что вы введете в codeToRun (), будет работать как фоновый поток.Для доступа к любому пользовательскому интерфейсу codeToRun имеет доступ к progressWindowViewController, который был предварительно настроен для подключения к пользовательскому интерфейсу с этими элементами под вашим контролем.
@IBOutlet weak var theTextView: UITextView!
@IBOutlet weak var currentProgressLabel: UILabel!
@IBOutlet weak var theProgressBar: UIProgressView!
@IBOutlet weak var navItemLabel: UINavigationItem!
@IBOutlet weak var doneButton: UIButton!
@IBOutlet weak var cancelNavButton: UIBarButtonItem!
@IBOutlet weak var navigationBar: UINavigationBar!
Я использую это во всех своих проектах в любое время, когда явыполнение синхронизации, загрузки или трудоемкого сетевого задания.
Здесь также отображается индикатор активности, показывающий, что вы пытаетесь что-то сделать.Все это можно настроить, так как его очень легко просмотреть и понять, как оно работает.Надеюсь, это поможет вам сэкономить некоторое время и быстро получить окно типа прогресса.