mirror of https://github.com/xaoxuu/ProHUD
Merge branch 'dev-1.1'
# Conflicts: # ProHUD/Guard/GuardController.swift # ProHUD/HUDController.swift
This commit is contained in:
commit
42e36b8f8e
|
@ -8,8 +8,12 @@
|
|||
|
||||
/* Begin PBXBuildFile section */
|
||||
33E2B6CF0D9BD11D8C027DE6 /* Pods_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB925B38880FB296AF5D219F /* Pods_Example.framework */; };
|
||||
CD10F0DA211582580077CAFF /* header.gif in Resources */ = {isa = PBXBuildFile; fileRef = CD10F0D9211582580077CAFF /* header.gif */; };
|
||||
CD95D26F22E732CE007559A3 /* TestA.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD95D26E22E732CE007559A3 /* TestA.swift */; };
|
||||
CD4B40AA23017C09005111B9 /* RootVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD4B40A923017C09005111B9 /* RootVC.swift */; };
|
||||
CD8BFF1823014850001E08DD /* TestToastVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD8BFF1723014850001E08DD /* TestToastVC.swift */; };
|
||||
CD8BFF1A2301485E001E08DD /* TestAlertVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD8BFF192301485E001E08DD /* TestAlertVC.swift */; };
|
||||
CD8BFF1C23014867001E08DD /* TestGuardVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD8BFF1B23014867001E08DD /* TestGuardVC.swift */; };
|
||||
CD8BFF1E230148DD001E08DD /* BaseListVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD8BFF1D230148DD001E08DD /* BaseListVC.swift */; };
|
||||
CD8BFF2023014CB5001E08DD /* EmptyVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD8BFF1F23014CB5001E08DD /* EmptyVC.swift */; };
|
||||
CDA4E03C20D3935B00CD2A0C /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDA4E03B20D3935B00CD2A0C /* AppDelegate.swift */; };
|
||||
CDA4E03E20D3935B00CD2A0C /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDA4E03D20D3935B00CD2A0C /* ViewController.swift */; };
|
||||
CDA4E04120D3935B00CD2A0C /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CDA4E03F20D3935B00CD2A0C /* Main.storyboard */; };
|
||||
|
@ -21,9 +25,13 @@
|
|||
AB925B38880FB296AF5D219F /* Pods_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
BA1A6035F1B9A658B3BB225C /* Pods-Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-Example/Pods-Example.release.xcconfig"; sourceTree = "<group>"; };
|
||||
CA1298266BE89D7950DE99F2 /* Pods-Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Example/Pods-Example.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
CD10F0D9211582580077CAFF /* header.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = header.gif; sourceTree = "<group>"; };
|
||||
CD4B40A923017C09005111B9 /* RootVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootVC.swift; sourceTree = "<group>"; };
|
||||
CD59584620E36DA8000F6427 /* Example-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Example-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
CD95D26E22E732CE007559A3 /* TestA.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestA.swift; sourceTree = "<group>"; };
|
||||
CD8BFF1723014850001E08DD /* TestToastVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestToastVC.swift; sourceTree = "<group>"; };
|
||||
CD8BFF192301485E001E08DD /* TestAlertVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestAlertVC.swift; sourceTree = "<group>"; };
|
||||
CD8BFF1B23014867001E08DD /* TestGuardVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestGuardVC.swift; sourceTree = "<group>"; };
|
||||
CD8BFF1D230148DD001E08DD /* BaseListVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseListVC.swift; sourceTree = "<group>"; };
|
||||
CD8BFF1F23014CB5001E08DD /* EmptyVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyVC.swift; sourceTree = "<group>"; };
|
||||
CDA4E03820D3935B00CD2A0C /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
CDA4E03B20D3935B00CD2A0C /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
CDA4E03D20D3935B00CD2A0C /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
|
||||
|
@ -84,11 +92,15 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
CDA4E03B20D3935B00CD2A0C /* AppDelegate.swift */,
|
||||
CD4B40A923017C09005111B9 /* RootVC.swift */,
|
||||
CD8BFF1D230148DD001E08DD /* BaseListVC.swift */,
|
||||
CD8BFF1F23014CB5001E08DD /* EmptyVC.swift */,
|
||||
CDA4E03D20D3935B00CD2A0C /* ViewController.swift */,
|
||||
CD95D26E22E732CE007559A3 /* TestA.swift */,
|
||||
CD8BFF1723014850001E08DD /* TestToastVC.swift */,
|
||||
CD8BFF192301485E001E08DD /* TestAlertVC.swift */,
|
||||
CD8BFF1B23014867001E08DD /* TestGuardVC.swift */,
|
||||
CDA4E03F20D3935B00CD2A0C /* Main.storyboard */,
|
||||
CDA4E04220D3935C00CD2A0C /* Assets.xcassets */,
|
||||
CD10F0D9211582580077CAFF /* header.gif */,
|
||||
CDA4E04420D3935C00CD2A0C /* LaunchScreen.storyboard */,
|
||||
CDA4E04720D3935C00CD2A0C /* Info.plist */,
|
||||
CD59584620E36DA8000F6427 /* Example-Bridging-Header.h */,
|
||||
|
@ -161,7 +173,6 @@
|
|||
CDA4E04620D3935C00CD2A0C /* LaunchScreen.storyboard in Resources */,
|
||||
CDA4E04320D3935C00CD2A0C /* Assets.xcassets in Resources */,
|
||||
CDA4E04120D3935B00CD2A0C /* Main.storyboard in Resources */,
|
||||
CD10F0DA211582580077CAFF /* header.gif in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -230,9 +241,14 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
CD8BFF1823014850001E08DD /* TestToastVC.swift in Sources */,
|
||||
CDA4E03E20D3935B00CD2A0C /* ViewController.swift in Sources */,
|
||||
CD8BFF2023014CB5001E08DD /* EmptyVC.swift in Sources */,
|
||||
CD8BFF1A2301485E001E08DD /* TestAlertVC.swift in Sources */,
|
||||
CD4B40AA23017C09005111B9 /* RootVC.swift in Sources */,
|
||||
CDA4E03C20D3935B00CD2A0C /* AppDelegate.swift in Sources */,
|
||||
CD95D26F22E732CE007559A3 /* TestA.swift in Sources */,
|
||||
CD8BFF1C23014867001E08DD /* TestGuardVC.swift in Sources */,
|
||||
CD8BFF1E230148DD001E08DD /* BaseListVC.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
|
@ -9,8 +9,6 @@
|
|||
import UIKit
|
||||
import ProHUD
|
||||
|
||||
let hud = ProHUD.shared
|
||||
|
||||
@UIApplicationMain
|
||||
class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
|
||||
|
@ -20,9 +18,30 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
||||
// Override point for customization after application launch.
|
||||
|
||||
window = UIWindow.init(frame: UIScreen.main.bounds)
|
||||
window?.rootViewController = RootVC()
|
||||
window?.makeKeyAndVisible()
|
||||
|
||||
|
||||
|
||||
ProHUD.config { (cfg) in
|
||||
cfg.rootViewController = window!.rootViewController
|
||||
cfg.alert { (a) in
|
||||
a.titleFont = .bold(22)
|
||||
a.bodyFont = .regular(17)
|
||||
a.boldTextFont = .bold(18)
|
||||
a.buttonFont = .bold(18)
|
||||
a.forceQuitTimer = 3
|
||||
}
|
||||
cfg.toast { (t) in
|
||||
t.titleFont = .bold(18)
|
||||
t.bodyFont = .regular(16)
|
||||
}
|
||||
cfg.guard { (g) in
|
||||
g.titleFont = .bold(22)
|
||||
g.subTitleFont = .bold(20)
|
||||
g.bodyFont = .regular(17)
|
||||
g.buttonFont = .bold(18)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "alert-circle.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "alert-circle (1).png",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
},
|
||||
"properties" : {
|
||||
"template-rendering-intent" : "template"
|
||||
}
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 1.0 KiB |
Binary file not shown.
Before Width: | Height: | Size: 961 B |
|
@ -1,22 +0,0 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "header_center@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "header_center@3x.png",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 28 KiB |
Binary file not shown.
Before Width: | Height: | Size: 44 KiB |
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14835.7" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14835.7" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="ttk-4w-IeX">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
|
@ -8,42 +8,39 @@
|
|||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<!--ProHUD-->
|
||||
<scene sceneID="tne-QT-ifu">
|
||||
<objects>
|
||||
<viewController id="BYZ-38-t0r" customClass="ViewController" customModule="Example" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" image="bg" translatesAutoresizingMaskIntoConstraints="NO" id="0oA-CL-uZS">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
</imageView>
|
||||
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="PcH-Px-Vql">
|
||||
<rect key="frame" x="51" y="120" width="46" height="30"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<state key="normal" title="Button"/>
|
||||
<connections>
|
||||
<action selector="test:" destination="BYZ-38-t0r" eventType="touchUpInside" id="mff-h5-exz"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="0oA-CL-uZS" firstAttribute="leading" secondItem="8bC-Xf-vdC" secondAttribute="leading" id="5mO-PC-le4"/>
|
||||
<constraint firstItem="0oA-CL-uZS" firstAttribute="top" secondItem="8bC-Xf-vdC" secondAttribute="top" id="Fay-dw-LvJ"/>
|
||||
<constraint firstAttribute="bottom" secondItem="0oA-CL-uZS" secondAttribute="bottom" id="TC7-O2-j1c"/>
|
||||
<constraint firstAttribute="trailing" secondItem="0oA-CL-uZS" secondAttribute="trailing" id="U3A-8b-rnZ"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
|
||||
</view>
|
||||
<navigationItem key="navigationItem" title="ProHUD" id="Yzl-Q6-9v7"/>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="142" y="134"/>
|
||||
<point key="canvasLocation" x="1050.7246376811595" y="133.92857142857142"/>
|
||||
</scene>
|
||||
<!--Navigation Controller-->
|
||||
<scene sceneID="HjC-hd-4PZ">
|
||||
<objects>
|
||||
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="ttk-4w-IeX" sceneMemberID="viewController">
|
||||
<toolbarItems/>
|
||||
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="juX-kz-8Ti">
|
||||
<rect key="frame" x="0.0" y="44" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</navigationBar>
|
||||
<nil name="viewControllers"/>
|
||||
<connections>
|
||||
<segue destination="BYZ-38-t0r" kind="relationship" relationship="rootViewController" id="gyV-fc-5FK"/>
|
||||
</connections>
|
||||
</navigationController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="Lg9-xW-IRh" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="140.57971014492756" y="133.92857142857142"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="bg" width="414" height="896"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
//
|
||||
// BaseListVC.swift
|
||||
// Example
|
||||
//
|
||||
// Created by xaoxuu on 2019/8/12.
|
||||
// Copyright © 2019 Titan Studio. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class BaseListVC: UIViewController {
|
||||
|
||||
lazy var tableView: UITableView = {
|
||||
let tv = UITableView()
|
||||
|
||||
return tv
|
||||
}()
|
||||
|
||||
var titles: [String] {
|
||||
return ["Toast", "Alert", "Guard"]
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
|
||||
view.addSubview(tableView)
|
||||
tableView.dataSource = self
|
||||
tableView.delegate = self
|
||||
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
|
||||
tableView.snp.makeConstraints { (mk) in
|
||||
mk.edges.equalToSuperview()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
extension BaseListVC: UITableViewDataSource, UITableViewDelegate {
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
return titles.count
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
|
||||
cell.textLabel?.text = titles[indexPath.row]
|
||||
return cell
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
tableView.deselectRow(at: indexPath, animated: true)
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
//
|
||||
// EmptyVC.swift
|
||||
// Example
|
||||
//
|
||||
// Created by xaoxuu on 2019/8/12.
|
||||
// Copyright © 2019 Titan Studio. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import SnapKit
|
||||
import Inspire
|
||||
|
||||
class EmptyVC: UIViewController {
|
||||
|
||||
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
view.backgroundColor = UIColor(white: 0.95, alpha: 1)
|
||||
// Do any additional setup after loading the view.
|
||||
|
||||
let lb = UILabel()
|
||||
lb.numberOfLines = 0
|
||||
lb.text = title
|
||||
lb.font = .regular(40)
|
||||
view.addSubview(lb)
|
||||
lb.snp.makeConstraints { (mk) in
|
||||
mk.center.equalToSuperview()
|
||||
mk.leading.greaterThanOrEqualToSuperview().offset(16)
|
||||
mk.trailing.lessThanOrEqualToSuperview().offset(-16)
|
||||
}
|
||||
|
||||
let btn = UIButton(type: .system)
|
||||
btn.titleLabel?.font = .bold(20)
|
||||
btn.setTitle("Dismiss", for: .normal)
|
||||
btn.addTarget(self, action: #selector(didTappedDismiss(_:)), for: .touchUpInside)
|
||||
view.addSubview(btn)
|
||||
btn.snp.makeConstraints { (mk) in
|
||||
mk.top.equalToSuperview().offset(Inspire.current.layout.safeAreaInsets(for: self).top)
|
||||
mk.trailing.equalToSuperview().offset(-16)
|
||||
mk.height.equalTo(44)
|
||||
}
|
||||
}
|
||||
|
||||
@objc func didTappedDismiss(_ sender: UIButton) {
|
||||
dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension UIViewController {
|
||||
func presentEmptyVC(title: String?) {
|
||||
let vc = EmptyVC()
|
||||
vc.title = title
|
||||
present(vc, animated: true, completion: nil)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
//
|
||||
// RootVC.swift
|
||||
// Example
|
||||
//
|
||||
// Created by xaoxuu on 2019/8/12.
|
||||
// Copyright © 2019 Titan Studio. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class RootVC: UIViewController {
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
// Do any additional setup after loading the view.
|
||||
let vc = ViewController()
|
||||
let nav = UINavigationController(rootViewController: vc)
|
||||
addChild(nav)
|
||||
view.addSubview(nav.view)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
// 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.destination.
|
||||
// Pass the selected object to the new view controller.
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
//
|
||||
// TestA.swift
|
||||
// ProHUDExample
|
||||
//
|
||||
// Created by xaoxuu on 2019/7/23.
|
||||
// Copyright © 2019 Titan Studio. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
public class TestA: NSObject {
|
||||
|
||||
class func test1() {
|
||||
print(self, "test1")
|
||||
}
|
||||
class func test2() {
|
||||
print(self, "test2")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
open class TestB: NSObject {
|
||||
|
||||
|
||||
class func test1() {
|
||||
print(self, "test1")
|
||||
}
|
||||
open class func test2() {
|
||||
print(self, "test2")
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
class TestAA: TestA {
|
||||
|
||||
override class func test2() {
|
||||
print(self, "test2", "override")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class TestBB: TestB {
|
||||
|
||||
override class func test2() {
|
||||
print(self, "test2", "override")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
//
|
||||
// TestAlertVC.swift
|
||||
// Example
|
||||
//
|
||||
// Created by xaoxuu on 2019/8/12.
|
||||
// Copyright © 2019 Titan Studio. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import ProHUD
|
||||
|
||||
class TestAlertVC: BaseListVC {
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
// Do any additional setup after loading the view.
|
||||
}
|
||||
|
||||
override var titles: [String] {
|
||||
return ["场景:正在同步(超时)", "场景:同步成功(写法1)", "场景:同步成功(写法2)", "场景:同步失败和重试"]
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
tableView.deselectRow(at: indexPath, animated: true)
|
||||
let row = indexPath.row
|
||||
if row == 0 {
|
||||
func f() {
|
||||
Alert.push(scene: .loading, title: "正在同步", message: "请稍等片刻") { (a) in
|
||||
a.identifier = "loading"
|
||||
a.animate(rotate: true)
|
||||
a.didForceQuit { [weak self] in
|
||||
let t = Toast.push(scene: .loading, title: "正在同步", message: "请稍等片刻(点击展开为Alert)") { (vm) in
|
||||
vm.identifier = "loading"
|
||||
}
|
||||
t.animate(rotate: true)
|
||||
t.didTapped { [weak t] in
|
||||
t?.pop()
|
||||
f()
|
||||
}
|
||||
self?.simulateSync()
|
||||
}
|
||||
}
|
||||
simulateSync()
|
||||
}
|
||||
f()
|
||||
} else if row == 1 {
|
||||
Alert.push() { (a) in
|
||||
a.identifier = "loading"
|
||||
a.animate(rotate: true)
|
||||
a.update { (vm) in
|
||||
vm.scene = .loading
|
||||
vm.title = "正在同步"
|
||||
vm.message = "请稍等片刻"
|
||||
}
|
||||
}
|
||||
DispatchQueue.main.asyncAfter(deadline: .now()+2) {
|
||||
Alert.find("loading", last: { (a) in
|
||||
a.update { (vm) in
|
||||
vm.scene = .success
|
||||
vm.title = "同步成功"
|
||||
vm.message = nil
|
||||
}
|
||||
})
|
||||
}
|
||||
} else if row == 2 {
|
||||
let a = Alert.push() { (a) in
|
||||
a.identifier = "loading"
|
||||
}
|
||||
a.animate(rotate: true)
|
||||
a.update { (vm) in
|
||||
vm.scene = .loading
|
||||
vm.title = "正在同步"
|
||||
vm.message = "请稍等片刻"
|
||||
}
|
||||
DispatchQueue.main.asyncAfter(deadline: .now()+2) {
|
||||
Alert.find("loading", last: { (a) in
|
||||
a.update { (vm) in
|
||||
vm.scene = .success
|
||||
vm.title = "同步成功"
|
||||
vm.message = nil
|
||||
}
|
||||
})
|
||||
}
|
||||
} else if row == 3 {
|
||||
Alert.push() { (a) in
|
||||
a.identifier = "loading"
|
||||
}
|
||||
func loading() {
|
||||
Alert.find("loading", last: { (a) in
|
||||
a.update { (vm) in
|
||||
vm.scene = .loading
|
||||
vm.title = "正在同步"
|
||||
vm.message = "请稍等片刻"
|
||||
vm.remove(action: 0, 1)
|
||||
}
|
||||
a.animate(rotate: true)
|
||||
DispatchQueue.main.asyncAfter(deadline: .now()+2) {
|
||||
a.update { (vm) in
|
||||
vm.scene = .error
|
||||
vm.title = "同步失败"
|
||||
vm.message = "请检查网络是否通畅"
|
||||
vm.add(action: .default, title: "重试") {
|
||||
loading()
|
||||
}
|
||||
vm.add(action: .cancel, title: "取消", handler: nil)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
loading()
|
||||
}
|
||||
}
|
||||
|
||||
func simulateSync() {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 15) {
|
||||
Alert.find("loading", last: { (a) in
|
||||
a.update { (vm) in
|
||||
vm.scene = .success
|
||||
vm.title = "同步成功"
|
||||
vm.message = "啊哈哈哈哈哈哈哈哈"
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
//
|
||||
// TestGuardVC.swift
|
||||
// Example
|
||||
//
|
||||
// Created by xaoxuu on 2019/8/12.
|
||||
// Copyright © 2019 Titan Studio. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import ProHUD
|
||||
import Inspire
|
||||
|
||||
class TestGuardVC: BaseListVC {
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
// Do any additional setup after loading the view.
|
||||
}
|
||||
|
||||
override var titles: [String] {
|
||||
return ["场景:删除菜单", "场景:升级至专业版", "场景:隐私协议页面"]
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
tableView.deselectRow(at: indexPath, animated: true)
|
||||
let row = indexPath.row
|
||||
if row == 0 {
|
||||
Guard.push() { (vc) in
|
||||
vc.update { (vm) in
|
||||
vm.add(action: .destructive, title: "删除") { [weak vc] in
|
||||
Alert.push(scene: .delete, title: "确认删除", message: "此操作不可撤销") { (vc) in
|
||||
vc.update { (vm) in
|
||||
vm.add(action: .destructive, title: "删除") { [weak vc] in
|
||||
vc?.pop()
|
||||
}
|
||||
vm.add(action: .cancel, title: "取消", handler: nil)
|
||||
}
|
||||
}
|
||||
vc?.pop()
|
||||
}
|
||||
vm.add(action: .cancel, title: "取消", handler: nil)
|
||||
}
|
||||
}
|
||||
} else if row == 1 {
|
||||
// 可以通过id来避免重复
|
||||
Guard.find("pro") {
|
||||
Guard.push() { (vc) in
|
||||
vc.identifier = "pro"
|
||||
vc.update { (vm) in
|
||||
vm.add(title: "升级至专业版")
|
||||
vm.add(subTitle: "解锁功能")
|
||||
vm.add(message: "功能1功能2...")
|
||||
vm.add(subTitle: "价格")
|
||||
vm.add(message: "只需一次性付费$2999即可永久享用。")
|
||||
vm.add(action: .destructive, title: "购买") { [weak vc] in
|
||||
Alert.push(scene: .confirm, title: "确认购买", message: "一旦购买拒不退款") { (vc) in
|
||||
vc.identifier = "confirm"
|
||||
vc.update { (vm) in
|
||||
vm.add(action: .destructive, title: "购买") { [weak vc] in
|
||||
vc?.update({ (vm) in
|
||||
vm.scene = .success
|
||||
vm.title = "购买成功"
|
||||
vm.message = "感谢您的支持"
|
||||
vm.remove(action: 1)
|
||||
vm.update(action: 0, style: .default, title: "我知道了") {
|
||||
vc?.pop()
|
||||
}
|
||||
})
|
||||
}
|
||||
vm.add(action: .cancel, title: "取消", handler: nil)
|
||||
}
|
||||
}
|
||||
vc?.pop()
|
||||
}
|
||||
vm.add(action: .cancel, title: "取消", handler: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if row == 2 {
|
||||
Guard.push() { (vc) in
|
||||
vc.isFullScreen = true
|
||||
vc.update { (vm) in
|
||||
let titleLabel = vm.add(title: "隐私协议")
|
||||
titleLabel.snp.makeConstraints { (mk) in
|
||||
mk.height.equalTo(44)
|
||||
}
|
||||
let tv = UITextView()
|
||||
tv.backgroundColor = .white
|
||||
tv.isEditable = false
|
||||
vc.textStack.addArrangedSubview(tv)
|
||||
tv.text = "这里可以插入一个webView"
|
||||
vm.add(message: "请认真阅读以上内容,当您阅读完毕并同意协议内容时点击接受按钮。")
|
||||
|
||||
vm.add(action: .default, title: "接受") { [weak vc] in
|
||||
vc?.pop()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
//
|
||||
// TestToastVC.swift
|
||||
// Example
|
||||
//
|
||||
// Created by xaoxuu on 2019/8/12.
|
||||
// Copyright © 2019 Titan Studio. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import ProHUD
|
||||
|
||||
class TestToastVC: BaseListVC {
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
// Do any additional setup after loading the view.
|
||||
}
|
||||
|
||||
override var titles: [String] {
|
||||
return ["场景:正在同步",
|
||||
"场景:同步成功",
|
||||
"场景:同步失败",
|
||||
"场景:设备电量过低",
|
||||
"传入指定图标",
|
||||
"禁止手势移除",
|
||||
"组合使用示例",
|
||||
"避免重复发布同一条信息",
|
||||
"根据id查找并修改实例"]
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
tableView.deselectRow(at: indexPath, animated: true)
|
||||
let row = indexPath.row
|
||||
if row == 0 {
|
||||
Toast.push(scene: .loading, title: "正在同步", message: "请稍等片刻") { (vm) in
|
||||
vm.identifier = "loading"
|
||||
}.animate(rotate: true)
|
||||
simulateSync()
|
||||
} else if row == 1 {
|
||||
let t = Toast.push(scene: .success, title: "同步成功", message: "点击查看详情")
|
||||
t.didTapped { [weak self, weak t] in
|
||||
self?.presentEmptyVC(title: "详情")
|
||||
t?.pop()
|
||||
}
|
||||
} else if row == 2 {
|
||||
Toast.push(scene: .error, title: "同步失败", message: "请稍后重试。点击查看详情") { (vc) in
|
||||
vc.update { (vm) in
|
||||
vm.duration = 0
|
||||
}
|
||||
vc.didTapped { [weak self, weak vc] in
|
||||
self?.presentEmptyVC(title: "这是错误详情")
|
||||
vc?.pop()
|
||||
}
|
||||
}
|
||||
} else if row == 3 {
|
||||
Toast.push(scene: .warning, title: "设备电量过低", message: "请及时对设备进行充电,以免影响使用。")
|
||||
|
||||
} else if row == 4 {
|
||||
Toast.push(scene: .default, title: "传入指定图标测试", message: "这是消息内容") { (vc) in
|
||||
vc.update { (vm) in
|
||||
vm.icon = UIImage(named: "icon_download")
|
||||
}
|
||||
}
|
||||
} else if row == 5 {
|
||||
Toast.push(scene: .default, title: "禁止手势移除", message: "这条消息无法通过向上滑动移出屏幕。5秒后自动消失,每次拖拽都会刷新倒计时。") { (vc) in
|
||||
vc.update { (vm) in
|
||||
vm.isRemovable = false
|
||||
vm.duration = 5
|
||||
}
|
||||
}
|
||||
} else if row == 6 {
|
||||
Toast.push(scene: .default, title: "好友邀请", message: "你收到一条好友邀请,点击查看详情。", duration: 10) { (vc) in
|
||||
vc.identifier = "xxx"
|
||||
vc.didTapped { [weak vc] in
|
||||
vc?.pop()
|
||||
Alert.push(scene: .confirm, title: "好友邀请", message: "用户xxx想要添加你为好友,是否同意?") { (vc) in
|
||||
vc.update { (vm) in
|
||||
vm.add(action: .default, title: "接受") { [weak vc] in
|
||||
vc?.pop()
|
||||
Toast.push(scene: .success, title: "好友添加成功", message: "这是消息内容")
|
||||
}
|
||||
vm.add(action: .cancel, title: "拒绝") {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if row == 7 {
|
||||
Toast.find("aaa", last: { (t) in
|
||||
t.pulse()
|
||||
t.update() { (vm) in
|
||||
vm.title = "已经存在了"
|
||||
}
|
||||
}) {
|
||||
Toast.push(title: "这是一条id为aaa的横幅", message: "避免重复发布同一条信息") { (t) in
|
||||
t.identifier = "aaa"
|
||||
t.update { (vm) in
|
||||
vm.scene = .warning
|
||||
vm.duration = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if row == 8 {
|
||||
Toast.find("aaa", last: { (t) in
|
||||
t.update { (vm) in
|
||||
vm.scene = .success
|
||||
vm.title = "找到了哈哈"
|
||||
vm.message = "根据id查找并修改实例"
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func simulateSync() {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
|
||||
Toast.find("loading", last: { (t) in
|
||||
t.update { (vm) in
|
||||
vm.scene = .success
|
||||
vm.title = "同步成功"
|
||||
vm.message = "啊哈哈哈哈哈哈哈哈"
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -8,150 +8,36 @@
|
|||
|
||||
import UIKit
|
||||
import ProHUD
|
||||
import SnapKit
|
||||
|
||||
class ViewController: UIViewController {
|
||||
class ViewController: BaseListVC {
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
// Do any additional setup after loading the view.
|
||||
|
||||
|
||||
ProHUD.config { (cfg) in
|
||||
cfg.alert { (a) in
|
||||
a.forceQuitTimer = 2
|
||||
// a.iconSize = .init(width: 20, height: 80)
|
||||
title = "ProHUD"
|
||||
}
|
||||
cfg.toast { (t) in
|
||||
// t.iconSize = .init(width: 300, height: 30)
|
||||
|
||||
override var titles: [String] {
|
||||
return ["Toast", "Alert", "Guard"]
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
tableView.deselectRow(at: indexPath, animated: true)
|
||||
if indexPath.row == 0 {
|
||||
let vc = TestToastVC()
|
||||
vc.title = titles[indexPath.row]
|
||||
navigationController?.pushViewController(vc, animated: true)
|
||||
} else if indexPath.row == 1 {
|
||||
let vc = TestAlertVC()
|
||||
vc.title = titles[indexPath.row]
|
||||
navigationController?.pushViewController(vc, animated: true)
|
||||
} else {
|
||||
let vc = TestGuardVC()
|
||||
vc.title = titles[indexPath.row]
|
||||
navigationController?.pushViewController(vc, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@IBAction func test(_ sender: UIButton) {
|
||||
|
||||
|
||||
// testUpdateAction()
|
||||
testGuard()
|
||||
// fastGuard()
|
||||
}
|
||||
|
||||
func testDelete() {
|
||||
let a = ProHUD.push(alert: .delete, title: "确认删除", message: "此操作不可撤销")
|
||||
a.add(action: .destructive, title: "确认", action: { [weak a] in
|
||||
a?.remove(action: 0, 1)
|
||||
a?.update(scene: .loading, title: "正在删除", message: "请稍后片刻")
|
||||
DispatchQueue.main.asyncAfter(deadline: .now()+1) {
|
||||
a?.update(scene: .success, title: "删除成功", message: "啊哈哈哈哈").duration(2)
|
||||
ProHUD.push(toast: .success, title: "删除成功", message: "aaa")
|
||||
}
|
||||
}).add(action: .cancel, title: "取消", action: nil)
|
||||
|
||||
}
|
||||
func testToast() {
|
||||
let t = ProHUD.Toast(scene: .loading, title: "正在加载", message: "请稍候片刻")
|
||||
|
||||
let a = ProHUD.push(alert : .loading, title: "正在加载", message: "请稍候片刻")
|
||||
a.didForceQuit {
|
||||
hud.push(t)
|
||||
}
|
||||
t.didTapped { [weak t] in
|
||||
t?.pop()
|
||||
let a2 = ProHUD.push(alert: .loading, title: "正在加载", message: "马上就要成功了")
|
||||
DispatchQueue.main.asyncAfter(deadline: .now()+1) {
|
||||
let a3 = ProHUD.push(alert: .error, title: "加载失败", message: "点击充实")
|
||||
a3.add(action: .default, title: "重新加载") { [weak a3] in
|
||||
a3?.update(scene: .success, title: "加载成功", message: "马上就要成功了")
|
||||
a3?.update(action: 0, style: .default, title: "OK", action: { [weak a3] in
|
||||
a3?.pop()
|
||||
}).remove(action: 1, 2)
|
||||
}.add(action: .destructive, title: "终止", action: nil).add(action: .cancel, title: "取消", action: nil)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
func testGuard() {
|
||||
let g = ProHUD.Guard(title: "请求权限", message: "请打开相机权限开关,否则无法进行测量。")
|
||||
|
||||
g.add(title: "呵呵")
|
||||
g.add(message: "请打开相机权限开关,否则无法进行测量。请打开相机权限开关,否则无法进行测量。")
|
||||
g.add(action: .default, title: "测试弹窗", action: { [weak self] in
|
||||
self?.testToast()
|
||||
})
|
||||
g.add(action: .destructive, title: "测试删除弹窗", action: { [weak self] in
|
||||
self?.testDelete()
|
||||
})
|
||||
g.add(action: .cancel, title: "我知道了", action: nil)
|
||||
|
||||
g.push(to: self)
|
||||
debugPrint("test: ", g)
|
||||
}
|
||||
|
||||
func testUpdateAction() {
|
||||
let a = ProHUD.push(alert: .confirm, title: "确认删除", message: "此操作无法撤销")
|
||||
a.add(action: .destructive, title: "删除") {
|
||||
a.remove(action: 0, 1).update(scene: .loading, title: "正在删除", message: "请稍后片刻")
|
||||
}.add(action: .cancel, title: "取消", action: nil)
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
func fastGuard() {
|
||||
let g = ProHUD.push(guard: self, title: "测试", message: "测试测试")
|
||||
g.add(action: .default, title: "默认按钮", action: {
|
||||
|
||||
})
|
||||
g.add(action: .cancel, title: "取消", action: nil)
|
||||
g.view.backgroundColor = .clear
|
||||
|
||||
// g.contentView.backgroundColor = UIColor.white
|
||||
|
||||
|
||||
}
|
||||
|
||||
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
// ProHUD.show(alert: .loading, title: "确认删除", message: "此操作不可撤销").timeout(nil)
|
||||
//
|
||||
// ProHUD.show(alert: .confirm, title: "确认删除", message: "此操作不可撤销").timeout(3)
|
||||
|
||||
//
|
||||
// a.addAction(style: .destructive, title: "删除") { [weak a] in
|
||||
// a?.updateContent(scene: .success, title: "操作成功", message: "恭喜,您已经成功删除了xxx")
|
||||
// a?.updateAction(index: 0, style: .default, title: "好的", action: {
|
||||
// a?.remove()
|
||||
// }).removeAction(index: 1)
|
||||
//// a?.updateContent(scene: .success, title: "操作成功").removeAction(index: -1).timeout(2)
|
||||
// }.addAction(style: .cancel, title: "取消", action: nil).didDisappear {
|
||||
// debugPrint("didDisappear")
|
||||
// }
|
||||
//
|
||||
// ProHUD.show(alert: .delete, title: "克里斯蒂娜删除", message: "克里斯蒂娜疯狂拉升的反馈老实交代分开就撒开了击快乐圣诞").addAction(style: .destructive, title: "删除") {
|
||||
//
|
||||
// }.addAction(style: .cancel, title: "取消", action: nil).didDisappear {
|
||||
// debugPrint("didDisappear")
|
||||
// }.addAction(style: .cancel, title: nil) {
|
||||
//
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
// ProHUD.show(toast: .loading, title: "正在加载", message: "拉升的反馈老实交代分开就撒开了击快乐圣反馈老实交代分开就撒开了击快乐圣")
|
||||
// ProHUD.show(toast: .loading, title: "正在加载", message: "哈克里斯蒂娜疯狂拉升的反馈老实交代分开就撒开了击快乐圣诞哈克里斯蒂娜疯狂拉升的反馈老实交代分开就撒开了击快乐圣诞")
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 493 KiB |
|
@ -10,21 +10,19 @@
|
|||
1AE9C44ABAF3F797A5518CE8 /* Pods_ProHUD.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C2011798511AD590A613E54E /* Pods_ProHUD.framework */; };
|
||||
CD16490B22EF09AB0077988C /* AlertModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD16490A22EF09AB0077988C /* AlertModel.swift */; };
|
||||
CD16490D22EF09B40077988C /* AlertConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD16490C22EF09B40077988C /* AlertConfig.swift */; };
|
||||
CD16490F22EF09D50077988C /* AlertView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD16490E22EF09D50077988C /* AlertView.swift */; };
|
||||
CD16491222EF0D900077988C /* HUDView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD16491122EF0D900077988C /* HUDView.swift */; };
|
||||
CD16491422EF12220077988C /* ProHUD.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CD16491322EF12220077988C /* ProHUD.xcassets */; };
|
||||
CD6CD86C22F1858F00F4FD4A /* ToastModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD6CD86B22F1858F00F4FD4A /* ToastModel.swift */; };
|
||||
CD6CD86E22F185A000F4FD4A /* ToastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD6CD86D22F185A000F4FD4A /* ToastView.swift */; };
|
||||
CD6CD87022F185A700F4FD4A /* ToastController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD6CD86F22F185A700F4FD4A /* ToastController.swift */; };
|
||||
CD6CD87222F185AF00F4FD4A /* ToastConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD6CD87122F185AF00F4FD4A /* ToastConfig.swift */; };
|
||||
CD6CD87522F185C200F4FD4A /* GuardController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD6CD87422F185C200F4FD4A /* GuardController.swift */; };
|
||||
CD6CD87922F185D000F4FD4A /* GuardConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD6CD87822F185D000F4FD4A /* GuardConfig.swift */; };
|
||||
CD6CD87B22F185D600F4FD4A /* GuardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD6CD87A22F185D600F4FD4A /* GuardView.swift */; };
|
||||
CD95D22122E72C4C007559A3 /* ProHUD.h in Headers */ = {isa = PBXBuildFile; fileRef = CD95D21F22E72C4C007559A3 /* ProHUD.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
CD95D26922E72DA1007559A3 /* AlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD95D26822E72DA1007559A3 /* AlertController.swift */; };
|
||||
CD95D26B22E72DB3007559A3 /* ProHUD.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD95D26A22E72DB3007559A3 /* ProHUD.swift */; };
|
||||
CDB6A07B22EEF06500AF6CF0 /* HUDController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDB6A07A22EEF06500AF6CF0 /* HUDController.swift */; };
|
||||
CDB6A07D22EEF19D00AF6CF0 /* HUDConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDB6A07C22EEF19D00AF6CF0 /* HUDConfig.swift */; };
|
||||
CDC39CFD22FD6DDF0070E914 /* GuardModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDC39CFC22FD6DDF0070E914 /* GuardModel.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
|
@ -32,16 +30,13 @@
|
|||
C2011798511AD590A613E54E /* Pods_ProHUD.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ProHUD.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
CD16490A22EF09AB0077988C /* AlertModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertModel.swift; sourceTree = "<group>"; };
|
||||
CD16490C22EF09B40077988C /* AlertConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertConfig.swift; sourceTree = "<group>"; };
|
||||
CD16490E22EF09D50077988C /* AlertView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertView.swift; sourceTree = "<group>"; };
|
||||
CD16491122EF0D900077988C /* HUDView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HUDView.swift; sourceTree = "<group>"; };
|
||||
CD16491322EF12220077988C /* ProHUD.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = ProHUD.xcassets; sourceTree = "<group>"; };
|
||||
CD6CD86B22F1858F00F4FD4A /* ToastModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToastModel.swift; sourceTree = "<group>"; };
|
||||
CD6CD86D22F185A000F4FD4A /* ToastView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToastView.swift; sourceTree = "<group>"; };
|
||||
CD6CD86F22F185A700F4FD4A /* ToastController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToastController.swift; sourceTree = "<group>"; };
|
||||
CD6CD87122F185AF00F4FD4A /* ToastConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToastConfig.swift; sourceTree = "<group>"; };
|
||||
CD6CD87422F185C200F4FD4A /* GuardController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GuardController.swift; sourceTree = "<group>"; };
|
||||
CD6CD87822F185D000F4FD4A /* GuardConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GuardConfig.swift; sourceTree = "<group>"; };
|
||||
CD6CD87A22F185D600F4FD4A /* GuardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GuardView.swift; sourceTree = "<group>"; };
|
||||
CD95D21C22E72C4C007559A3 /* ProHUD.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ProHUD.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
CD95D21F22E72C4C007559A3 /* ProHUD.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ProHUD.h; sourceTree = "<group>"; };
|
||||
CD95D22022E72C4C007559A3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
|
@ -50,6 +45,7 @@
|
|||
CD95D26A22E72DB3007559A3 /* ProHUD.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProHUD.swift; sourceTree = "<group>"; };
|
||||
CDB6A07A22EEF06500AF6CF0 /* HUDController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HUDController.swift; sourceTree = "<group>"; };
|
||||
CDB6A07C22EEF19D00AF6CF0 /* HUDConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HUDConfig.swift; sourceTree = "<group>"; };
|
||||
CDC39CFC22FD6DDF0070E914 /* GuardModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GuardModel.swift; sourceTree = "<group>"; };
|
||||
DC31EBFAC56868D6096A233A /* Pods-ProHUD.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ProHUD.release.xcconfig"; path = "Pods/Target Support Files/Pods-ProHUD/Pods-ProHUD.release.xcconfig"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
|
@ -88,7 +84,6 @@
|
|||
CD95D26822E72DA1007559A3 /* AlertController.swift */,
|
||||
CD16490A22EF09AB0077988C /* AlertModel.swift */,
|
||||
CD16490C22EF09B40077988C /* AlertConfig.swift */,
|
||||
CD16490E22EF09D50077988C /* AlertView.swift */,
|
||||
);
|
||||
path = Alert;
|
||||
sourceTree = "<group>";
|
||||
|
@ -97,7 +92,6 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
CD6CD86B22F1858F00F4FD4A /* ToastModel.swift */,
|
||||
CD6CD86D22F185A000F4FD4A /* ToastView.swift */,
|
||||
CD6CD86F22F185A700F4FD4A /* ToastController.swift */,
|
||||
CD6CD87122F185AF00F4FD4A /* ToastConfig.swift */,
|
||||
);
|
||||
|
@ -109,7 +103,7 @@
|
|||
children = (
|
||||
CD6CD87422F185C200F4FD4A /* GuardController.swift */,
|
||||
CD6CD87822F185D000F4FD4A /* GuardConfig.swift */,
|
||||
CD6CD87A22F185D600F4FD4A /* GuardView.swift */,
|
||||
CDC39CFC22FD6DDF0070E914 /* GuardModel.swift */,
|
||||
);
|
||||
path = Guard;
|
||||
sourceTree = "<group>";
|
||||
|
@ -277,12 +271,9 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
CD6CD87B22F185D600F4FD4A /* GuardView.swift in Sources */,
|
||||
CD95D26922E72DA1007559A3 /* AlertController.swift in Sources */,
|
||||
CD6CD87922F185D000F4FD4A /* GuardConfig.swift in Sources */,
|
||||
CDB6A07D22EEF19D00AF6CF0 /* HUDConfig.swift in Sources */,
|
||||
CD16490F22EF09D50077988C /* AlertView.swift in Sources */,
|
||||
CD6CD86E22F185A000F4FD4A /* ToastView.swift in Sources */,
|
||||
CD6CD87222F185AF00F4FD4A /* ToastConfig.swift in Sources */,
|
||||
CD6CD87522F185C200F4FD4A /* GuardController.swift in Sources */,
|
||||
CD6CD87022F185A700F4FD4A /* ToastController.swift in Sources */,
|
||||
|
@ -292,6 +283,7 @@
|
|||
CD16490B22EF09AB0077988C /* AlertModel.swift in Sources */,
|
||||
CD16491222EF0D900077988C /* HUDView.swift in Sources */,
|
||||
CDB6A07B22EEF06500AF6CF0 /* HUDController.swift in Sources */,
|
||||
CDC39CFD22FD6DDF0070E914 /* GuardModel.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
|
@ -22,11 +22,17 @@ public extension ProHUD.Configuration {
|
|||
/// 填充:元素内部控件距离元素边界的距离
|
||||
public var padding = CGFloat(16)
|
||||
|
||||
// MARK: 图标样式
|
||||
/// 图标、default按钮的颜色
|
||||
/// 颜色
|
||||
public var tintColor: UIColor?
|
||||
|
||||
// MARK: 图标样式
|
||||
/// 图标尺寸
|
||||
public var iconSize = CGSize(width: 48, height: 48)
|
||||
/// 某个场景的默认图片
|
||||
/// - Parameter callback: 回调
|
||||
public func iconForScene(_ callback: @escaping (ProHUD.Alert.Scene) -> UIImage?) {
|
||||
privIconForScene = callback
|
||||
}
|
||||
|
||||
// MARK: 文本样式
|
||||
/// 标题字体
|
||||
|
@ -34,7 +40,7 @@ public extension ProHUD.Configuration {
|
|||
/// 标题最多行数
|
||||
public var titleMaxLines = Int(1)
|
||||
|
||||
/// 加粗正文字体(如果只有标题或者只有正文,则显示这种字体)
|
||||
/// 加粗字体(如果只有标题或者只有正文,则显示这种字体)
|
||||
public var boldTextFont = UIFont.boldSystemFont(ofSize: 18)
|
||||
|
||||
/// 正文字体
|
||||
|
@ -48,16 +54,14 @@ public extension ProHUD.Configuration {
|
|||
|
||||
// MARK: 生命周期
|
||||
|
||||
/// 加载视图
|
||||
/// - Parameter callback: 回调代码
|
||||
public mutating func loadSubviews(_ callback: @escaping (ProHUD.Alert) -> Void) {
|
||||
privLoadSubviews = callback
|
||||
/// 刷新数据和布局
|
||||
public func reloadData(_ callback: @escaping (ProHUD.Alert) -> Void) {
|
||||
privReloadData = callback
|
||||
}
|
||||
|
||||
/// 更新视图
|
||||
/// - Parameter callback: 回调代码
|
||||
public mutating func reloadData(_ callback: @escaping (ProHUD.Alert) -> Void) {
|
||||
privReloadData = callback
|
||||
/// 默认持续时间(当viewmodel的duration为nil时,会从这里获取)
|
||||
public func durationForScene(_ callback: @escaping (ProHUD.Alert.Scene) -> TimeInterval?) {
|
||||
privDurationForScene = callback
|
||||
}
|
||||
|
||||
/// 多少秒后显示强制退出的按钮(只有无按钮的弹窗才会出现)
|
||||
|
@ -68,77 +72,45 @@ public extension ProHUD.Configuration {
|
|||
|
||||
/// 加载强制退出按钮
|
||||
/// - Parameter callback: 回调代码
|
||||
public mutating func loadForceQuitButton(_ callback: @escaping (ProHUD.Alert) -> Void) {
|
||||
public func loadForceQuitButton(_ callback: @escaping (ProHUD.Alert) -> Void) {
|
||||
privLoadForceQuitButton = callback
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - 默认实现
|
||||
|
||||
// MARK: - 内部调用
|
||||
internal extension ProHUD.Configuration.Alert {
|
||||
var loadSubviews: (ProHUD.Alert) -> Void {
|
||||
return privLoadSubviews
|
||||
}
|
||||
|
||||
var reloadData: (ProHUD.Alert) -> Void {
|
||||
return privReloadData
|
||||
}
|
||||
|
||||
var loadForceQuitButton: (ProHUD.Alert) -> Void {
|
||||
return privLoadForceQuitButton
|
||||
var durationForScene: (ProHUD.Alert.Scene) -> TimeInterval? {
|
||||
return privDurationForScene
|
||||
}
|
||||
|
||||
var setupDefaultDuration: (ProHUD.Alert) -> Void {
|
||||
return { (vc) in
|
||||
// 如果设置了超时时间,但是增加了按钮
|
||||
if let t = vc.model.duration, t > 0 {
|
||||
if vc.buttonEvents.count > 0 {
|
||||
vc.duration(nil)
|
||||
}
|
||||
} else if vc.model.duration == nil && vc.model.scene != .loading {
|
||||
// 如果没有设置超时时间
|
||||
vc.duration(2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var reloadStack: (ProHUD.Alert) -> Void {
|
||||
return { (vc) in
|
||||
if vc.textStack.arrangedSubviews.count > 0 {
|
||||
vc.contentStack.addArrangedSubview(vc.textStack)
|
||||
} else {
|
||||
vc.textStack.removeFromSuperview()
|
||||
}
|
||||
if vc.actionStack.arrangedSubviews.count > 0 {
|
||||
vc.contentStack.addArrangedSubview(vc.actionStack)
|
||||
} else {
|
||||
vc.actionStack.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate var privLoadSubviews: (ProHUD.Alert) -> Void = {
|
||||
// MARK: - 默认实现
|
||||
fileprivate var privLayoutContentView: (ProHUD.Alert) -> Void = {
|
||||
return { (vc) in
|
||||
debug(vc, "loadSubviews")
|
||||
let config = cfg.alert
|
||||
if vc.contentView.superview == nil {
|
||||
vc.view.addSubview(vc.contentView)
|
||||
vc.contentView.contentView.addSubview(vc.contentStack)
|
||||
|
||||
vc.contentStack.spacing = cfg.alert.margin + cfg.alert.padding
|
||||
|
||||
vc.contentView.layer.masksToBounds = true
|
||||
vc.contentView.layer.cornerRadius = cfg.alert.cornerRadius
|
||||
|
||||
let maxWidth = CGFloat.maximum(CGFloat.minimum(UIScreen.main.bounds.width * 0.68, cfg.alert.maxWidth), 268)
|
||||
vc.contentView.snp.makeConstraints { (mk) in
|
||||
mk.center.equalToSuperview()
|
||||
mk.width.lessThanOrEqualTo(maxWidth)
|
||||
}
|
||||
}
|
||||
if vc.contentStack.superview == nil {
|
||||
vc.contentView.contentView.addSubview(vc.contentStack)
|
||||
vc.contentStack.spacing = cfg.alert.margin + cfg.alert.padding
|
||||
vc.contentStack.snp.makeConstraints { (mk) in
|
||||
mk.centerX.equalToSuperview()
|
||||
mk.top.equalToSuperview().offset(cfg.alert.padding)
|
||||
|
@ -147,23 +119,13 @@ fileprivate var privLoadSubviews: (ProHUD.Alert) -> Void = {
|
|||
mk.trailing.equalToSuperview().offset(-cfg.alert.padding)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}()
|
||||
|
||||
fileprivate var privReloadData: (ProHUD.Alert) -> Void = {
|
||||
return { (vc) in
|
||||
debug(vc, "reloadData")
|
||||
let config = cfg.alert
|
||||
let isFirstLayout: Bool
|
||||
// 图标和文字至少有一个,如果都没有添加到视图中,说明是第一次layout
|
||||
if vc.textStack.superview == nil && vc.imageView?.superview == nil {
|
||||
isFirstLayout = true
|
||||
} else {
|
||||
isFirstLayout = false
|
||||
}
|
||||
fileprivate var privIconForScene: (ProHUD.Alert.Scene) -> UIImage? = {
|
||||
return { (scene) in
|
||||
let imgStr: String
|
||||
switch vc.model.scene {
|
||||
switch scene {
|
||||
case .success:
|
||||
imgStr = "ProHUDSuccess"
|
||||
case .warning:
|
||||
|
@ -179,7 +141,14 @@ fileprivate var privReloadData: (ProHUD.Alert) -> Void = {
|
|||
default:
|
||||
imgStr = "ProHUDMessage"
|
||||
}
|
||||
let img = vc.model.icon ?? ProHUD.image(named: imgStr)
|
||||
return ProHUD.image(named: imgStr)
|
||||
}
|
||||
}()
|
||||
|
||||
fileprivate var privUpdateImage: (ProHUD.Alert) -> Void = {
|
||||
return { (vc) in
|
||||
let config = cfg.alert
|
||||
let img = vc.vm.icon ?? privIconForScene(vc.vm.scene)
|
||||
if let imgv = vc.imageView {
|
||||
imgv.image = img
|
||||
} else {
|
||||
|
@ -197,9 +166,13 @@ fileprivate var privReloadData: (ProHUD.Alert) -> Void = {
|
|||
vc.imageView = icon
|
||||
}
|
||||
vc.imageView?.layer.removeAllAnimations()
|
||||
}
|
||||
}()
|
||||
|
||||
// text
|
||||
if vc.model.title?.count ?? 0 > 0 || vc.model.message?.count ?? 0 > 0 {
|
||||
fileprivate var privUpdateTextStack: (ProHUD.Alert) -> Void = {
|
||||
return { (vc) in
|
||||
let config = cfg.alert
|
||||
if vc.vm.title?.count ?? 0 > 0 || vc.vm.message?.count ?? 0 > 0 {
|
||||
vc.contentStack.addArrangedSubview(vc.textStack)
|
||||
vc.textStack.snp.makeConstraints { (mk) in
|
||||
mk.top.greaterThanOrEqualTo(vc.contentView).offset(config.padding*1.75)
|
||||
|
@ -212,19 +185,19 @@ fileprivate var privReloadData: (ProHUD.Alert) -> Void = {
|
|||
mk.trailing.lessThanOrEqualTo(vc.contentView).offset(-config.padding*2)
|
||||
}
|
||||
}
|
||||
if vc.model.title?.count ?? 0 > 0 {
|
||||
if vc.vm.title?.count ?? 0 > 0 {
|
||||
if let lb = vc.titleLabel {
|
||||
lb.text = vc.model.title
|
||||
lb.text = vc.vm.title
|
||||
} else {
|
||||
let title = UILabel()
|
||||
title.textAlignment = .center
|
||||
title.numberOfLines = config.titleMaxLines
|
||||
title.textColor = cfg.primaryLabelColor
|
||||
title.text = vc.model.title
|
||||
title.text = vc.vm.title
|
||||
vc.textStack.addArrangedSubview(title)
|
||||
vc.titleLabel = title
|
||||
}
|
||||
if vc.model.message?.count ?? 0 > 0 {
|
||||
if vc.vm.message?.count ?? 0 > 0 {
|
||||
// 有message
|
||||
vc.titleLabel?.font = config.titleFont
|
||||
} else {
|
||||
|
@ -234,20 +207,20 @@ fileprivate var privReloadData: (ProHUD.Alert) -> Void = {
|
|||
} else {
|
||||
vc.titleLabel?.removeFromSuperview()
|
||||
}
|
||||
if vc.model.message?.count ?? 0 > 0 {
|
||||
if vc.vm.message?.count ?? 0 > 0 {
|
||||
if let lb = vc.bodyLabel {
|
||||
lb.text = vc.model.message
|
||||
lb.text = vc.vm.message
|
||||
} else {
|
||||
let body = UILabel()
|
||||
body.textAlignment = .center
|
||||
body.font = config.bodyFont
|
||||
body.numberOfLines = config.bodyMaxLines
|
||||
body.textColor = cfg.secondaryLabelColor
|
||||
body.text = vc.model.message
|
||||
body.text = vc.vm.message
|
||||
vc.textStack.addArrangedSubview(body)
|
||||
vc.bodyLabel = body
|
||||
}
|
||||
if vc.model.title?.count ?? 0 > 0 {
|
||||
if vc.vm.title?.count ?? 0 > 0 {
|
||||
// 有title
|
||||
vc.bodyLabel?.font = config.bodyFont
|
||||
} else {
|
||||
|
@ -260,18 +233,19 @@ fileprivate var privReloadData: (ProHUD.Alert) -> Void = {
|
|||
} else {
|
||||
vc.textStack.removeFromSuperview()
|
||||
}
|
||||
if vc.actionStack.superview != nil {
|
||||
if isFirstLayout {
|
||||
vc.contentStack.addArrangedSubview(vc.actionStack)
|
||||
} else {
|
||||
vc.actionStack.transform = .init(scaleX: 1, y: 0.001)
|
||||
UIView.animateForAlert {
|
||||
vc.contentStack.addArrangedSubview(vc.actionStack)
|
||||
vc.view.layoutIfNeeded()
|
||||
}
|
||||
vc.textStack.layoutIfNeeded()
|
||||
}
|
||||
}()
|
||||
|
||||
|
||||
fileprivate var privUpdateActionStack: (ProHUD.Alert) -> Void = {
|
||||
return { (vc) in
|
||||
let config = cfg.alert
|
||||
if vc.actionStack.arrangedSubviews.count > 0 {
|
||||
// 有按钮
|
||||
vc.contentStack.addArrangedSubview(vc.actionStack)
|
||||
// 适配横竖屏和iPad
|
||||
if isPortrait == false {
|
||||
if isPortrait == false && vc.actionStack.arrangedSubviews.count < 4 {
|
||||
vc.actionStack.axis = .horizontal
|
||||
vc.actionStack.alignment = .fill
|
||||
vc.actionStack.distribution = .fillEqually
|
||||
|
@ -280,28 +254,14 @@ fileprivate var privReloadData: (ProHUD.Alert) -> Void = {
|
|||
mk.width.greaterThanOrEqualTo(200)
|
||||
mk.leading.trailing.equalToSuperview()
|
||||
}
|
||||
if isFirstLayout == false {
|
||||
UIView.animateForAlert {
|
||||
vc.actionStack.transform = .identity
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if isFirstLayout {
|
||||
vc.view.layoutIfNeeded()
|
||||
vc.imageView?.transform = .init(scaleX: 0.75, y: 0.75)
|
||||
UIView.animateForAlert {
|
||||
vc.imageView?.transform = .identity
|
||||
vc.view.layoutIfNeeded()
|
||||
}
|
||||
} else {
|
||||
UIView.animateForAlert {
|
||||
vc.view.layoutIfNeeded()
|
||||
// 无按钮
|
||||
for v in vc.actionStack.arrangedSubviews {
|
||||
v.removeFromSuperview()
|
||||
}
|
||||
vc.actionStack.removeFromSuperview()
|
||||
}
|
||||
|
||||
|
||||
vc.actionStack.layoutIfNeeded()
|
||||
}
|
||||
}()
|
||||
|
||||
|
@ -311,7 +271,7 @@ fileprivate var privLoadForceQuitButton: (ProHUD.Alert) -> Void = {
|
|||
let config = cfg.alert
|
||||
let btn = ProHUD.Alert.Button.forceQuitButton()
|
||||
btn.setTitle(cfg.alert.forceQuitTitle, for: .normal)
|
||||
let bg = ProHUD.BlurView()
|
||||
let bg = createBlurView()
|
||||
bg.layer.masksToBounds = true
|
||||
bg.layer.cornerRadius = config.cornerRadius
|
||||
if let last = vc.view.subviews.last {
|
||||
|
@ -335,12 +295,80 @@ fileprivate var privLoadForceQuitButton: (ProHUD.Alert) -> Void = {
|
|||
bg.transform = .identity
|
||||
}
|
||||
vc.addTouchUpAction(for: btn) { [weak vc] in
|
||||
debug("点击了隐藏")
|
||||
vc?.model.forceQuitCallback?()
|
||||
debug("点击了【\(config.forceQuitTitle)】")
|
||||
vc?.vm.forceQuitCallback?()
|
||||
vc?.pop()
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
/// 刷新数据和布局
|
||||
fileprivate var privReloadData: (ProHUD.Alert) -> Void = {
|
||||
return { (vc) in
|
||||
debug(vc, "reloadData")
|
||||
let config = cfg.alert
|
||||
let isFirstLayout: Bool
|
||||
if vc.contentView.superview == nil {
|
||||
isFirstLayout = true
|
||||
// 布局主容器视图
|
||||
privLayoutContentView(vc)
|
||||
} else {
|
||||
isFirstLayout = false
|
||||
}
|
||||
// 更新图片
|
||||
privUpdateImage(vc)
|
||||
|
||||
// 更新文本容器
|
||||
privUpdateTextStack(vc)
|
||||
|
||||
// 更新操作容器
|
||||
privUpdateActionStack(vc)
|
||||
vc.contentStack.layoutIfNeeded()
|
||||
vc.contentView.layoutIfNeeded()
|
||||
|
||||
// 动画
|
||||
if isFirstLayout {
|
||||
vc.view.layoutIfNeeded()
|
||||
vc.imageView?.transform = .init(scaleX: 0.75, y: 0.75)
|
||||
UIView.animateForAlert {
|
||||
vc.view.layoutIfNeeded()
|
||||
vc.imageView?.transform = .identity
|
||||
}
|
||||
} else {
|
||||
UIView.animateForAlert {
|
||||
vc.view.layoutIfNeeded()
|
||||
}
|
||||
}
|
||||
|
||||
// 设置持续时间
|
||||
vc.vm.updateDuration()
|
||||
|
||||
// 强制退出按钮
|
||||
vc.vm.forceQuitTimerBlock?.cancel()
|
||||
if vc.buttonEvents.count == 0 {
|
||||
vc.vm.forceQuitTimerBlock = DispatchWorkItem(block: { [weak vc] in
|
||||
if let vc = vc {
|
||||
if vc.buttonEvents.count == 0 {
|
||||
privLoadForceQuitButton(vc)
|
||||
}
|
||||
}
|
||||
})
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + config.forceQuitTimer, execute: vc.vm.forceQuitTimerBlock!)
|
||||
} else {
|
||||
vc.vm.forceQuitTimerBlock = nil
|
||||
}
|
||||
|
||||
}
|
||||
}()
|
||||
|
||||
|
||||
fileprivate var privDurationForScene: (ProHUD.Alert.Scene) -> TimeInterval? = {
|
||||
return { (scene) in
|
||||
switch scene {
|
||||
case .loading:
|
||||
return nil
|
||||
default:
|
||||
return 2
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
|
|
@ -19,7 +19,7 @@ public extension ProHUD {
|
|||
internal static var alertWindow: UIWindow?
|
||||
|
||||
/// 内容视图
|
||||
public var contentView = BlurView()
|
||||
public var contentView = createBlurView()
|
||||
|
||||
/// 内容容器(包括icon、textStack、actionStack)
|
||||
public var contentStack: StackContainer = {
|
||||
|
@ -53,10 +53,10 @@ public extension ProHUD {
|
|||
}()
|
||||
|
||||
/// 视图模型
|
||||
public var model = ViewModel()
|
||||
|
||||
public var vm = ViewModel()
|
||||
|
||||
// MARK: 生命周期
|
||||
private var isLoadFinished = false
|
||||
|
||||
/// 实例化
|
||||
/// - Parameter scene: 场景
|
||||
|
@ -65,16 +65,20 @@ public extension ProHUD {
|
|||
/// - Parameter icon: 图标
|
||||
public convenience init(scene: Scene = .default, title: String? = nil, message: String? = nil, icon: UIImage? = nil, actions: ((Alert) -> Void)? = nil) {
|
||||
self.init()
|
||||
view.tintColor = cfg.alert.tintColor
|
||||
model.scene = scene
|
||||
model.title = title
|
||||
model.message = message
|
||||
model.icon = icon
|
||||
vm.vc = self
|
||||
vm.scene = scene
|
||||
vm.title = title
|
||||
vm.message = message
|
||||
vm.icon = icon
|
||||
actions?(self)
|
||||
willLayoutSubviews()
|
||||
|
||||
}
|
||||
|
||||
public override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
view.tintColor = cfg.alert.tintColor
|
||||
cfg.alert.reloadData(self)
|
||||
isLoadFinished = true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -84,13 +88,10 @@ public extension ProHUD {
|
|||
|
||||
public extension Alert {
|
||||
|
||||
// MARK: 生命周期函数
|
||||
|
||||
/// 推入屏幕
|
||||
@discardableResult func push() -> Alert {
|
||||
let hud = ProHUD.shared
|
||||
if Alert.alerts.contains(self) == false {
|
||||
let window = Alert.getAlertWindow(self)
|
||||
let window = Alert.privGetAlertWindow(self)
|
||||
window.makeKeyAndVisible()
|
||||
window.resignKey()
|
||||
window.addSubview(view)
|
||||
|
@ -103,19 +104,15 @@ public extension Alert {
|
|||
}
|
||||
Alert.alerts.append(self)
|
||||
}
|
||||
Alert.updateAlertsLayout()
|
||||
|
||||
// setup duration
|
||||
if let _ = model.duration, model.durationBlock == nil {
|
||||
duration(model.duration)
|
||||
}
|
||||
Alert.privUpdateAlertsLayout()
|
||||
return self
|
||||
}
|
||||
|
||||
/// 弹出屏幕
|
||||
func pop() {
|
||||
let window = Alert.getAlertWindow(self)
|
||||
Alert.removeItemFromArray(alert: self)
|
||||
willDisappearCallback?()
|
||||
let window = Alert.privGetAlertWindow(self)
|
||||
Alert.privRemoveItemFromArray(alert: self)
|
||||
UIView.animateForAlertBuildOut(animations: {
|
||||
self.view.alpha = 0
|
||||
self.view.transform = .init(scaleX: 1.08, y: 1.08)
|
||||
|
@ -129,117 +126,51 @@ public extension Alert {
|
|||
UIView.animateForAlertBuildOut(animations: {
|
||||
window.backgroundColor = window.backgroundColor?.withAlphaComponent(0)
|
||||
}) { (done) in
|
||||
if Alert.alerts.count == 0 {
|
||||
Alert.alertWindow = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: 设置函数
|
||||
|
||||
/// 添加按钮
|
||||
/// - Parameter style: 样式
|
||||
/// - Parameter text: 标题
|
||||
/// - Parameter handler: 事件处理
|
||||
@discardableResult func add(action style: UIAlertAction.Style, title: String?, handler: (() -> Void)?) -> UIButton {
|
||||
let btn = privAddButton(custom: Button.actionButton(title: title), action: handler)
|
||||
if let b = btn as? Button {
|
||||
b.update(style: style)
|
||||
}
|
||||
return btn
|
||||
}
|
||||
|
||||
/// 最小化事件
|
||||
/// - Parameter callback: 事件回调
|
||||
@discardableResult func didForceQuit(_ callback: (() -> Void)?) -> Alert {
|
||||
model.forceQuitCallback = callback
|
||||
return self
|
||||
}
|
||||
|
||||
/// 消失事件
|
||||
/// - Parameter callback: 事件回调
|
||||
@discardableResult func didDisappear(_ callback: (() -> Void)?) -> Alert {
|
||||
disappearCallback = callback
|
||||
return self
|
||||
}
|
||||
|
||||
/// 设置持续时间
|
||||
/// - Parameter duration: 持续时间
|
||||
@discardableResult func duration(_ duration: TimeInterval?) -> Alert {
|
||||
model.setupDuration(duration: duration) { [weak self] in
|
||||
self?.pop()
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
/// 更新
|
||||
/// - Parameter scene: 场景
|
||||
/// - Parameter title: 标题
|
||||
/// - Parameter message: 正文
|
||||
@discardableResult func update(scene: Scene, title: String?, message: String?) -> Alert {
|
||||
model.scene = scene
|
||||
model.title = title
|
||||
model.message = message
|
||||
willLayoutSubviews()
|
||||
return self
|
||||
}
|
||||
|
||||
/// 更新图标
|
||||
/// - Parameter icon: 图标
|
||||
@discardableResult func update(icon: UIImage?) -> Alert {
|
||||
model.icon = icon
|
||||
/// - Parameter callback: 回调
|
||||
func update(_ callback: ((inout ViewModel) -> Void)? = nil) {
|
||||
callback?(&vm)
|
||||
cfg.alert.reloadData(self)
|
||||
return self
|
||||
}
|
||||
|
||||
@discardableResult func animate(rotate: Bool) -> Alert {
|
||||
if rotate {
|
||||
|
||||
/// 最小化事件
|
||||
/// - Parameter callback: 事件回调
|
||||
func didForceQuit(_ callback: (() -> Void)?) {
|
||||
vm.forceQuitCallback = callback
|
||||
}
|
||||
|
||||
|
||||
/// 图片旋转效果
|
||||
/// - Parameter flag: 是否开启
|
||||
func rotate(_ flag: Bool = true) {
|
||||
if flag {
|
||||
DispatchQueue.main.async {
|
||||
let ani = CABasicAnimation(keyPath: "transform.rotation.z")
|
||||
ani.toValue = M_PI*2.0
|
||||
ani.duration = 2
|
||||
ani.toValue = Double.pi * 2.0
|
||||
ani.duration = 3
|
||||
ani.repeatCount = 10000
|
||||
self.imageView?.layer.add(ani, forKey: "rotationAnimation")
|
||||
}
|
||||
} else {
|
||||
imageView?.layer.removeAllAnimations()
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
/// 更新按钮
|
||||
/// - Parameter index: 索引
|
||||
/// - Parameter style: 样式
|
||||
/// - Parameter title: 标题
|
||||
/// - Parameter action: 事件
|
||||
@discardableResult func update(action index: Int, style: UIAlertAction.Style, title: String?, action: (() -> Void)?) -> Alert {
|
||||
if index < self.actionStack.arrangedSubviews.count, let btn = self.actionStack.arrangedSubviews[index] as? UIButton {
|
||||
btn.setTitle(title, for: .normal)
|
||||
if let b = btn as? Button {
|
||||
b.update(style: style)
|
||||
}
|
||||
btn.layoutIfNeeded()
|
||||
if let ac = action {
|
||||
addTouchUpAction(for: btn, action: ac)
|
||||
}
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
/// 移除按钮
|
||||
/// - Parameter index: 索引
|
||||
@discardableResult func remove(action index: Int...) -> Alert {
|
||||
for (i, idx) in index.enumerated() {
|
||||
privRemoveAction(index: idx-i)
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
// MARK: 类函数
|
||||
|
||||
// MARK: - 实例管理器
|
||||
public extension Alert {
|
||||
|
||||
/// 推入屏幕
|
||||
|
@ -247,13 +178,13 @@ public extension Alert {
|
|||
/// - Parameter title: 标题
|
||||
/// - Parameter message: 正文
|
||||
/// - Parameter actions: 更多操作
|
||||
@discardableResult class func push(alert scene: Alert.Scene, title: String? = nil, message: String? = nil, actions: ((Alert) -> Void)? = nil) -> Alert {
|
||||
@discardableResult class func push(scene: Alert.Scene = .default, title: String? = nil, message: String? = nil, _ actions: ((Alert) -> Void)? = nil) -> Alert {
|
||||
return Alert(scene: scene, title: title, message: message, actions: actions).push()
|
||||
}
|
||||
|
||||
/// 获取指定的实例
|
||||
/// 查找指定的实例
|
||||
/// - Parameter identifier: 指定实例的标识
|
||||
class func alerts(_ identifier: String?) -> [Alert] {
|
||||
class func find(_ identifier: String?) -> [Alert] {
|
||||
var aa = [Alert]()
|
||||
for a in Alert.alerts {
|
||||
if a.identifier == identifier {
|
||||
|
@ -263,6 +194,18 @@ public extension Alert {
|
|||
return aa
|
||||
}
|
||||
|
||||
/// 查找指定的实例
|
||||
/// - Parameter identifier: 标识
|
||||
/// - Parameter last: 已经存在(获取最后一个)
|
||||
/// - Parameter none: 不存在
|
||||
class func find(_ identifier: String?, last: ((Alert) -> Void)? = nil, none: (() -> Void)? = nil) {
|
||||
if let t = find(identifier).last {
|
||||
last?(t)
|
||||
} else {
|
||||
none?()
|
||||
}
|
||||
}
|
||||
|
||||
/// 弹出屏幕
|
||||
/// - Parameter alert: 实例
|
||||
class func pop(_ alert: Alert) {
|
||||
|
@ -272,82 +215,101 @@ public extension Alert {
|
|||
/// 弹出屏幕
|
||||
/// - Parameter identifier: 指定实例的标识
|
||||
class func pop(_ identifier: String?) {
|
||||
for a in alerts(identifier) {
|
||||
for a in find(identifier) {
|
||||
a.pop()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - 私有
|
||||
|
||||
fileprivate extension Alert {
|
||||
// MARK: - 创建和设置
|
||||
internal extension Alert {
|
||||
|
||||
/// 插入一个按钮
|
||||
/// - Parameter style: 样式
|
||||
/// - Parameter title: 标题
|
||||
/// - Parameter action: 事件
|
||||
@discardableResult func insert(action index: Int?, style: UIAlertAction.Style, title: String?, handler: (() -> Void)?) -> UIButton {
|
||||
let btn = Button.actionButton(title: title)
|
||||
if let idx = index, idx < actionStack.arrangedSubviews.count {
|
||||
actionStack.insertArrangedSubview(btn, at: idx)
|
||||
} else {
|
||||
actionStack.addArrangedSubview(btn)
|
||||
}
|
||||
btn.update(style: style)
|
||||
if actionStack.superview == nil {
|
||||
contentStack.addArrangedSubview(actionStack)
|
||||
contentStack.layoutIfNeeded()
|
||||
}
|
||||
addTouchUpAction(for: btn) { [weak self] in
|
||||
handler?()
|
||||
if btn.tag == UIAlertAction.Style.cancel.rawValue {
|
||||
self?.pop()
|
||||
}
|
||||
}
|
||||
if isLoadFinished {
|
||||
actionStack.layoutIfNeeded()
|
||||
UIView.animateForAlert {
|
||||
self.view.layoutIfNeeded()
|
||||
}
|
||||
}
|
||||
return btn
|
||||
}
|
||||
|
||||
/// 更新按钮
|
||||
/// - Parameter index: 索引
|
||||
/// - Parameter style: 样式
|
||||
/// - Parameter title: 标题
|
||||
/// - Parameter handler: 事件
|
||||
func update(action index: Int, style: UIAlertAction.Style, title: String?, handler: (() -> Void)?) {
|
||||
if index < self.actionStack.arrangedSubviews.count, let btn = self.actionStack.arrangedSubviews[index] as? UIButton {
|
||||
btn.setTitle(title, for: .normal)
|
||||
if let b = btn as? Button {
|
||||
b.update(style: style)
|
||||
}
|
||||
if let _ = buttonEvents[btn] {
|
||||
buttonEvents.removeValue(forKey: btn)
|
||||
}
|
||||
addTouchUpAction(for: btn) { [weak self] in
|
||||
handler?()
|
||||
if btn.tag == UIAlertAction.Style.cancel.rawValue {
|
||||
self?.pop()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 移除按钮
|
||||
/// - Parameter index: 索引
|
||||
@discardableResult func privRemoveAction(index: Int) -> Alert {
|
||||
@discardableResult func remove(action index: Int) -> Alert {
|
||||
if index < 0 {
|
||||
for view in self.actionStack.arrangedSubviews {
|
||||
if let btn = view as? UIButton {
|
||||
btn.removeFromSuperview()
|
||||
if let _ = buttonEvents[btn] {
|
||||
buttonEvents.removeValue(forKey: btn)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if index < self.actionStack.arrangedSubviews.count, let btn = self.actionStack.arrangedSubviews[index] as? UIButton {
|
||||
btn.removeFromSuperview()
|
||||
if let _ = buttonEvents[btn] {
|
||||
buttonEvents.removeValue(forKey: btn)
|
||||
}
|
||||
}
|
||||
if self.actionStack.arrangedSubviews.count == 0 {
|
||||
self.actionStack.removeFromSuperview()
|
||||
}
|
||||
willLayoutSubviews()
|
||||
UIView.animateForAlert {
|
||||
self.view.layoutIfNeeded()
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func willLayoutSubviews() {
|
||||
model.setupWillLayout(duration: 0.001) { [weak self] in
|
||||
if let a = self {
|
||||
// 布局
|
||||
cfg.alert.loadSubviews(a)
|
||||
cfg.alert.reloadData(a)
|
||||
cfg.alert.setupDefaultDuration(a)
|
||||
// 强制退出按钮
|
||||
a.model.setupForceQuit(duration: cfg.alert.forceQuitTimer) { [weak self] in
|
||||
if let aa = self, aa.actionStack.superview == nil {
|
||||
cfg.alert.loadForceQuitButton(aa)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@discardableResult func privAddButton(custom button: UIButton, action: (() -> Void)?) -> UIButton {
|
||||
model.duration = nil
|
||||
if actionStack.superview == nil {
|
||||
contentStack.addArrangedSubview(actionStack)
|
||||
}
|
||||
self.view.layoutIfNeeded()
|
||||
button.transform = .init(scaleX: 1, y: 0.001)
|
||||
actionStack.addArrangedSubview(button)
|
||||
UIView.animateForAlert {
|
||||
button.transform = .identity
|
||||
self.view.layoutIfNeeded()
|
||||
}
|
||||
addTouchUpAction(for: button) { [weak self] in
|
||||
action?()
|
||||
if button.tag == UIAlertAction.Style.cancel.rawValue {
|
||||
self?.pop()
|
||||
}
|
||||
}
|
||||
willLayoutSubviews()
|
||||
return button
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fileprivate extension Alert {
|
||||
class func updateAlertsLayout() {
|
||||
class func privUpdateAlertsLayout() {
|
||||
for (i, a) in alerts.reversed().enumerated() {
|
||||
let scale = CGFloat(pow(0.7, CGFloat(i)))
|
||||
UIView.animate(withDuration: 1.8, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 0.5, options: [.allowUserInteraction, .curveEaseInOut], animations: {
|
||||
|
@ -358,7 +320,7 @@ fileprivate extension Alert {
|
|||
}
|
||||
}
|
||||
}
|
||||
class func getAlertWindow(_ vc: UIViewController) -> UIWindow {
|
||||
class func privGetAlertWindow(_ vc: UIViewController) -> UIWindow {
|
||||
if let w = alertWindow {
|
||||
return w
|
||||
}
|
||||
|
@ -370,7 +332,7 @@ fileprivate extension Alert {
|
|||
return w
|
||||
}
|
||||
|
||||
class func removeItemFromArray(alert: Alert) {
|
||||
class func privRemoveItemFromArray(alert: Alert) {
|
||||
if alerts.count > 1 {
|
||||
for (i, a) in alerts.enumerated() {
|
||||
if a == alert {
|
||||
|
@ -379,7 +341,7 @@ fileprivate extension Alert {
|
|||
}
|
||||
}
|
||||
}
|
||||
updateAlertsLayout()
|
||||
privUpdateAlertsLayout()
|
||||
} else if alerts.count == 1 {
|
||||
alerts.removeAll()
|
||||
} else {
|
||||
|
|
|
@ -33,22 +33,42 @@ public extension Alert {
|
|||
|
||||
}
|
||||
|
||||
struct ViewModel {
|
||||
class ViewModel {
|
||||
|
||||
/// 使用场景
|
||||
public var scene = Scene.default
|
||||
|
||||
/// 标题
|
||||
public var title: String?
|
||||
public var title: String? {
|
||||
didSet {
|
||||
vc?.titleLabel?.text = title
|
||||
}
|
||||
}
|
||||
|
||||
/// 正文
|
||||
public var message: String?
|
||||
public var message: String? {
|
||||
didSet {
|
||||
vc?.bodyLabel?.text = message
|
||||
}
|
||||
}
|
||||
|
||||
/// 图标
|
||||
public var icon: UIImage?
|
||||
public var icon: UIImage? {
|
||||
didSet {
|
||||
vc?.imageView?.image = icon
|
||||
}
|
||||
}
|
||||
|
||||
/// 持续时间
|
||||
internal var duration: TimeInterval?
|
||||
/// 持续时间(为空代表根据场景不同的默认配置,为0代表无穷大)
|
||||
public var duration: TimeInterval? {
|
||||
didSet {
|
||||
updateDuration()
|
||||
}
|
||||
}
|
||||
|
||||
public weak var vc: Alert?
|
||||
|
||||
// MARK: 私有
|
||||
|
||||
/// 持续时间
|
||||
internal var durationBlock: DispatchWorkItem?
|
||||
|
@ -59,40 +79,59 @@ public extension Alert {
|
|||
/// 强制退出代码
|
||||
internal var forceQuitCallback: (() -> Void)?
|
||||
|
||||
internal var willLayoutBlock: DispatchWorkItem?
|
||||
|
||||
internal mutating func setupDuration(duration: TimeInterval?, callback: @escaping () -> Void) {
|
||||
self.duration = duration
|
||||
internal func updateDuration() {
|
||||
durationBlock?.cancel()
|
||||
if let t = duration, t > 0 {
|
||||
durationBlock = DispatchWorkItem(block: callback)
|
||||
if let t = duration ?? cfg.alert.durationForScene(scene), t > 0 {
|
||||
durationBlock = DispatchWorkItem(block: { [weak self] in
|
||||
self?.vc?.pop()
|
||||
})
|
||||
DispatchQueue.main.asyncAfter(deadline: .now()+t, execute: durationBlock!)
|
||||
} else {
|
||||
durationBlock = nil
|
||||
}
|
||||
}
|
||||
|
||||
internal mutating func setupForceQuit(duration: TimeInterval?, callback: @escaping () -> Void) {
|
||||
forceQuitTimerBlock?.cancel()
|
||||
if let t = duration, t > 0 {
|
||||
forceQuitTimerBlock = DispatchWorkItem(block: callback)
|
||||
DispatchQueue.main.asyncAfter(deadline: .now()+t, execute: forceQuitTimerBlock!)
|
||||
} else {
|
||||
forceQuitTimerBlock = nil
|
||||
}
|
||||
}
|
||||
|
||||
internal mutating func setupWillLayout(duration: TimeInterval?, callback: @escaping () -> Void) {
|
||||
willLayoutBlock?.cancel()
|
||||
if let t = duration, t > 0 {
|
||||
willLayoutBlock = DispatchWorkItem(block: callback)
|
||||
DispatchQueue.main.asyncAfter(deadline: .now()+t, execute: willLayoutBlock!)
|
||||
} else {
|
||||
willLayoutBlock = nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public extension Alert.ViewModel {
|
||||
|
||||
/// 添加按钮
|
||||
/// - Parameter style: 样式
|
||||
/// - Parameter text: 标题
|
||||
/// - Parameter handler: 事件处理
|
||||
@discardableResult func add(action style: UIAlertAction.Style, title: String?, handler: (() -> Void)?) -> UIButton {
|
||||
duration = 0
|
||||
return vc!.insert(action: nil, style: style, title: title, handler: handler)
|
||||
}
|
||||
|
||||
/// 插入按钮
|
||||
/// - Parameter index: 索引
|
||||
/// - Parameter style: 样式
|
||||
/// - Parameter title: 标题
|
||||
/// - Parameter handler: 事件处理
|
||||
@discardableResult func insert(action index: Int, style: UIAlertAction.Style, title: String?, handler: (() -> Void)?) -> UIButton {
|
||||
duration = 0
|
||||
return vc!.insert(action: index, style: style, title: title, handler: handler)
|
||||
}
|
||||
|
||||
/// 更新按钮
|
||||
/// - Parameter index: 索引
|
||||
/// - Parameter style: 样式
|
||||
/// - Parameter title: 标题
|
||||
/// - Parameter handler: 事件处理
|
||||
func update(action index: Int, style: UIAlertAction.Style, title: String?, handler: (() -> Void)?) {
|
||||
vc?.update(action: index, style: style, title: title, handler: handler)
|
||||
}
|
||||
|
||||
/// 移除按钮
|
||||
/// - Parameter index: 索引
|
||||
func remove(action index: Int...) {
|
||||
for (i, idx) in index.enumerated() {
|
||||
vc?.remove(action: idx-i)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
//
|
||||
// AlertView.swift
|
||||
// ProHUD
|
||||
//
|
||||
// Created by xaoxuu on 2019/7/29.
|
||||
// Copyright © 2019 Titan Studio. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
internal extension Alert {
|
||||
class Button: UIButton {
|
||||
class func actionButton(title: String?) -> UIButton {
|
||||
let btn = Button(type: .system)
|
||||
btn.setTitle(title, for: .normal)
|
||||
btn.layer.cornerRadius = cfg.alert.cornerRadius / 2
|
||||
btn.titleLabel?.font = cfg.alert.buttonFont
|
||||
return btn
|
||||
}
|
||||
|
||||
func update(style: UIAlertAction.Style) {
|
||||
let pd = CGFloat(8)
|
||||
if style != .cancel {
|
||||
backgroundColor = cfg.dynamicColor.withAlphaComponent(0.04)
|
||||
contentEdgeInsets = .init(top: pd*1.5, left: pd*1.5, bottom: pd*1.5, right: pd*1.5)
|
||||
} else {
|
||||
contentEdgeInsets = .init(top: pd*0.5, left: pd*1.5, bottom: pd*0.5, right: pd*1.5)
|
||||
}
|
||||
switch style {
|
||||
case .default:
|
||||
setTitleColor(tintColor, for: .normal)
|
||||
case .destructive:
|
||||
setTitleColor(.init(red: 244/255, green: 67/255, blue: 54/255, alpha: 1), for: .normal)
|
||||
case .cancel:
|
||||
setTitleColor(cfg.secondaryLabelColor, for: .normal)
|
||||
@unknown default:
|
||||
break
|
||||
}
|
||||
tag = style.rawValue
|
||||
}
|
||||
|
||||
class func forceQuitButton() -> UIButton {
|
||||
let btn = Button(type: .system)
|
||||
let pd = cfg.alert.padding/2
|
||||
btn.contentEdgeInsets = .init(top: pd*1.5, left: pd*1.5, bottom: pd*1.5, right: pd*1.5)
|
||||
btn.imageEdgeInsets.right = pd*1.5
|
||||
btn.setTitleColor(UIColor(red:1.00, green:0.55, blue:0.21, alpha:1.00), for: .normal)
|
||||
btn.titleLabel?.font = cfg.alert.buttonFont
|
||||
return btn
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -22,7 +22,6 @@ public extension ProHUD.Configuration {
|
|||
/// 填充:元素内部控件距离元素边界的距离
|
||||
public var padding = CGFloat(16)
|
||||
|
||||
// MARK: 图标样式
|
||||
/// 颜色
|
||||
public var tintColor: UIColor?
|
||||
|
||||
|
@ -41,12 +40,6 @@ public extension ProHUD.Configuration {
|
|||
/// 按钮圆角半径
|
||||
public var buttonCornerRadius = CGFloat(12)
|
||||
|
||||
/// 加载视图
|
||||
/// - Parameter callback: 回调代码
|
||||
public mutating func loadSubviews(_ callback: @escaping (ProHUD.Guard) -> Void) {
|
||||
privLoadSubviews = callback
|
||||
}
|
||||
|
||||
/// 更新视图
|
||||
/// - Parameter callback: 回调代码
|
||||
public mutating func reloadData(_ callback: @escaping (ProHUD.Guard) -> Void) {
|
||||
|
@ -56,15 +49,14 @@ public extension ProHUD.Configuration {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - 默认实现
|
||||
|
||||
// MARK: - 内部调用
|
||||
internal extension ProHUD.Configuration.Guard {
|
||||
var loadSubviews: (ProHUD.Guard) -> Void {
|
||||
return privLoadSubviews
|
||||
}
|
||||
|
||||
var reloadData: (ProHUD.Guard) -> Void {
|
||||
return privReloadData
|
||||
}
|
||||
|
||||
var reloadStack: (ProHUD.Guard) -> Void {
|
||||
return { (vc) in
|
||||
if vc.textStack.arrangedSubviews.count > 0 {
|
||||
|
@ -82,22 +74,16 @@ internal extension ProHUD.Configuration.Guard {
|
|||
|
||||
}
|
||||
|
||||
fileprivate var privLoadSubviews: (ProHUD.Guard) -> Void = {
|
||||
// MARK: - 默认实现
|
||||
fileprivate var privReloadData: (ProHUD.Guard) -> Void = {
|
||||
return { (vc) in
|
||||
debug(vc, "loadSubviews")
|
||||
debug(vc, "reloadData")
|
||||
let config = cfg.guard
|
||||
// background
|
||||
vc.view.tintColor = config.tintColor
|
||||
vc.view.backgroundColor = UIColor(white: 0, alpha: 0)
|
||||
vc.view.addSubview(vc.contentView)
|
||||
vc.contentView.contentView.addSubview(vc.contentStack)
|
||||
}
|
||||
}()
|
||||
|
||||
fileprivate var privReloadData: (ProHUD.Guard) -> Void = {
|
||||
return { (vc) in
|
||||
debug(vc, "reloadData")
|
||||
let config = cfg.guard
|
||||
// 更新布局
|
||||
var width = UIScreen.main.bounds.width
|
||||
if width > config.cardMaxWidth {
|
||||
|
@ -111,12 +97,15 @@ fileprivate var privReloadData: (ProHUD.Guard) -> Void = {
|
|||
vc.contentView.layer.shadowOpacity = 0.12
|
||||
}
|
||||
vc.contentView.snp.makeConstraints { (mk) in
|
||||
if isPortrait && vc.isFullScreen {
|
||||
mk.top.equalToSuperview()
|
||||
}
|
||||
mk.centerX.equalToSuperview()
|
||||
if UIDevice.current.userInterfaceIdiom == .phone {
|
||||
if width == config.cardMaxWidth {
|
||||
mk.bottom.equalToSuperview().offset(-Inspire.shared.screen.safeAreaInsets.bottom)
|
||||
} else {
|
||||
if width < config.cardMaxWidth {
|
||||
mk.bottom.equalToSuperview()
|
||||
} else {
|
||||
mk.bottom.equalToSuperview().offset(-Inspire.shared.screen.safeAreaInsets.bottom)
|
||||
}
|
||||
} else if UIDevice.current.userInterfaceIdiom == .pad {
|
||||
mk.centerY.equalToSuperview()
|
||||
|
@ -125,12 +114,21 @@ fileprivate var privReloadData: (ProHUD.Guard) -> Void = {
|
|||
}
|
||||
// stack
|
||||
vc.contentStack.snp.makeConstraints { (mk) in
|
||||
mk.top.equalToSuperview().offset(config.padding + config.margin)
|
||||
if isPortrait && vc.isFullScreen {
|
||||
mk.top.equalToSuperview().offset(Inspire.shared.screen.safeAreaInsets.top)
|
||||
} else {
|
||||
mk.top.equalToSuperview().offset(config.padding)
|
||||
}
|
||||
mk.centerX.equalToSuperview()
|
||||
if width == config.cardMaxWidth {
|
||||
if width < config.cardMaxWidth {
|
||||
let bottom = Inspire.shared.screen.safeAreaInsets.bottom
|
||||
if bottom == 0 {
|
||||
mk.bottom.equalToSuperview().offset(-config.padding)
|
||||
} else {
|
||||
mk.bottom.equalToSuperview().offset(-config.padding-Inspire.shared.screen.safeAreaInsets.bottom)
|
||||
mk.bottom.equalToSuperview().offset(-config.padding/2 - bottom)
|
||||
}
|
||||
} else {
|
||||
mk.bottom.equalToSuperview().offset(-config.padding)
|
||||
}
|
||||
if isPortrait {
|
||||
mk.width.equalToSuperview().offset(-config.padding * 2)
|
||||
|
@ -138,6 +136,6 @@ fileprivate var privReloadData: (ProHUD.Guard) -> Void = {
|
|||
mk.width.equalToSuperview().offset(-config.padding * 4)
|
||||
}
|
||||
}
|
||||
|
||||
config.reloadStack(vc)
|
||||
}
|
||||
}()
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
//
|
||||
|
||||
import SnapKit
|
||||
import Inspire
|
||||
|
||||
public typealias Guard = ProHUD.Guard
|
||||
|
||||
|
@ -15,7 +16,7 @@ public extension ProHUD {
|
|||
class Guard: HUDController {
|
||||
|
||||
/// 内容视图
|
||||
public var contentView = BlurView()
|
||||
public var contentView = createBlurView()
|
||||
|
||||
/// 内容容器(包括textStack、actionStack,可以自己插入需要的控件)
|
||||
public var contentStack: StackContainer = {
|
||||
|
@ -42,15 +43,21 @@ public extension ProHUD {
|
|||
}()
|
||||
|
||||
/// 是否是强制性的(点击空白处是否可以消失)
|
||||
public var force = false
|
||||
public var isForce = false
|
||||
|
||||
/// 是否是全屏的(仅手机竖屏有效)
|
||||
public var isFullScreen = false
|
||||
|
||||
/// 是否正在显示
|
||||
private var displaying = false
|
||||
private var isDisplaying = false
|
||||
|
||||
/// 背景颜色
|
||||
public var backgroundColor: UIColor? = UIColor(white: 0, alpha: 0.5)
|
||||
public var backgroundColor: UIColor? = UIColor(white: 0, alpha: 0.4)
|
||||
|
||||
public var vm = ViewModel()
|
||||
|
||||
// MARK: 生命周期
|
||||
private var isLoadFinished = false
|
||||
|
||||
/// 实例化
|
||||
/// - Parameter title: 标题
|
||||
|
@ -58,8 +65,7 @@ public extension ProHUD {
|
|||
/// - Parameter actions: 更多操作
|
||||
public convenience init(title: String? = nil, message: String? = nil, actions: ((Guard) -> Void)? = nil) {
|
||||
self.init()
|
||||
|
||||
view.tintColor = cfg.guard.tintColor
|
||||
vm.vc = self
|
||||
if let _ = title {
|
||||
add(title: title)
|
||||
}
|
||||
|
@ -67,31 +73,31 @@ public extension ProHUD {
|
|||
add(message: message)
|
||||
}
|
||||
actions?(self)
|
||||
cfg.guard.loadSubviews(self)
|
||||
cfg.guard.reloadData(self)
|
||||
cfg.guard.reloadStack(self)
|
||||
|
||||
// 点击空白处
|
||||
let tap = UITapGestureRecognizer(target: self, action: #selector(privDidTapped(_:)))
|
||||
view.addGestureRecognizer(tap)
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
view.tintColor = cfg.guard.tintColor
|
||||
cfg.guard.reloadData(self)
|
||||
isLoadFinished = true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - 实例函数
|
||||
|
||||
public extension Guard {
|
||||
|
||||
// MARK: 生命周期函数
|
||||
|
||||
/// 推入某个视图控制器
|
||||
/// - Parameter viewController: 视图控制器
|
||||
func push(to viewController: UIViewController? = nil) -> Guard {
|
||||
@discardableResult func push(to viewController: UIViewController? = nil) -> Guard {
|
||||
func f(_ vc: UIViewController) {
|
||||
view.layoutIfNeeded()
|
||||
vc.addChild(self)
|
||||
|
@ -100,12 +106,12 @@ public extension Guard {
|
|||
view.snp.makeConstraints { (mk) in
|
||||
mk.edges.equalToSuperview()
|
||||
}
|
||||
if displaying == false {
|
||||
translateOut()
|
||||
if isDisplaying == false {
|
||||
privTranslateOut()
|
||||
}
|
||||
displaying = true
|
||||
isDisplaying = true
|
||||
UIView.animateForGuard {
|
||||
self.translateIn()
|
||||
self.privTranslateIn()
|
||||
}
|
||||
}
|
||||
if let vc = viewController ?? cfg.rootViewController {
|
||||
|
@ -118,114 +124,33 @@ public extension Guard {
|
|||
|
||||
/// 从父视图控制器弹出
|
||||
func pop() {
|
||||
if displaying {
|
||||
if isDisplaying {
|
||||
debug("pop")
|
||||
willDisappearCallback?()
|
||||
displaying = false
|
||||
isDisplaying = false
|
||||
view.isUserInteractionEnabled = false
|
||||
self.removeFromParent()
|
||||
UIView.animateForGuard(animations: {
|
||||
self.translateOut()
|
||||
self.privTranslateOut()
|
||||
}) { (done) in
|
||||
if self.displaying == false {
|
||||
if self.isDisplaying == false {
|
||||
self.view.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: 设置函数
|
||||
|
||||
/// 加载一个标题
|
||||
/// - Parameter text: 文本
|
||||
@discardableResult func add(title: String?) -> UILabel {
|
||||
let lb = UILabel()
|
||||
lb.font = cfg.guard.titleFont
|
||||
lb.textColor = cfg.primaryLabelColor
|
||||
lb.numberOfLines = 0
|
||||
lb.textAlignment = .justified
|
||||
lb.text = title
|
||||
textStack.addArrangedSubview(lb)
|
||||
if #available(iOS 11.0, *) {
|
||||
let count = textStack.arrangedSubviews.count
|
||||
if count > 1 {
|
||||
textStack.setCustomSpacing(cfg.guard.margin * 2, after: textStack.arrangedSubviews[count-2])
|
||||
}
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
}
|
||||
cfg.guard.reloadStack(self)
|
||||
return lb
|
||||
}
|
||||
|
||||
/// 加载一个副标题
|
||||
/// - Parameter text: 文本
|
||||
@discardableResult func add(subTitle: String?) -> UILabel {
|
||||
let lb = add(title: subTitle)
|
||||
lb.font = cfg.guard.subTitleFont
|
||||
return lb
|
||||
}
|
||||
|
||||
/// 加载一段正文
|
||||
/// - Parameter text: 文本
|
||||
@discardableResult func add(message: String?) -> UILabel {
|
||||
let lb = UILabel()
|
||||
lb.font = cfg.guard.bodyFont
|
||||
lb.textColor = cfg.secondaryLabelColor
|
||||
lb.numberOfLines = 0
|
||||
lb.textAlignment = .justified
|
||||
lb.text = message
|
||||
textStack.addArrangedSubview(lb)
|
||||
cfg.guard.reloadStack(self)
|
||||
return lb
|
||||
}
|
||||
|
||||
/// 加载一个按钮
|
||||
/// - Parameter style: 样式
|
||||
/// - Parameter title: 标题
|
||||
/// - Parameter action: 事件
|
||||
@discardableResult func add(action style: UIAlertAction.Style, title: String?, handler: (() -> Void)?) -> UIButton {
|
||||
let btn = Button.actionButton(title: title)
|
||||
btn.titleLabel?.font = cfg.guard.buttonFont
|
||||
actionStack.addArrangedSubview(btn)
|
||||
cfg.guard.reloadStack(self)
|
||||
btn.update(style: style)
|
||||
addTouchUpAction(for: btn) { [weak self] in
|
||||
handler?()
|
||||
if btn.tag == UIAlertAction.Style.cancel.rawValue {
|
||||
self?.pop()
|
||||
}
|
||||
}
|
||||
return btn
|
||||
}
|
||||
|
||||
/// 移除按钮
|
||||
/// - Parameter index: 索引
|
||||
@discardableResult func remove(action index: Int...) -> Guard {
|
||||
for (i, idx) in index.enumerated() {
|
||||
privRemoveAction(index: idx-i)
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
/// 消失事件
|
||||
/// - Parameter callback: 事件回调
|
||||
@discardableResult func willDisappear(_ callback: (() -> Void)?) -> Guard {
|
||||
willDisappearCallback = callback
|
||||
return self
|
||||
}
|
||||
/// 消失事件
|
||||
/// - Parameter callback: 事件回调
|
||||
@discardableResult func didDisappear(_ callback: (() -> Void)?) -> Guard {
|
||||
disappearCallback = callback
|
||||
return self
|
||||
}
|
||||
|
||||
/// 更新
|
||||
/// - Parameter callback: 回调
|
||||
func update(_ callback: ((inout ViewModel) -> Void)? = nil) {
|
||||
callback?(&vm)
|
||||
cfg.guard.reloadData(self)
|
||||
}
|
||||
|
||||
|
||||
// MARK: 类函数
|
||||
}
|
||||
|
||||
// MARK: - 实例管理器
|
||||
public extension Guard {
|
||||
|
||||
/// 推入屏幕
|
||||
|
@ -233,13 +158,13 @@ public extension Guard {
|
|||
/// - Parameter title: 标题
|
||||
/// - Parameter message: 正文
|
||||
/// - Parameter icon: 图标
|
||||
@discardableResult class func push(to viewController: UIViewController? = nil, actions: ((Guard) -> Void)? = nil) -> Guard {
|
||||
@discardableResult class func push(to viewController: UIViewController? = nil, _ actions: ((Guard) -> Void)? = nil) -> Guard {
|
||||
return Guard(actions: actions).push(to: viewController)
|
||||
}
|
||||
|
||||
/// 获取指定的实例
|
||||
/// 查找指定的实例
|
||||
/// - Parameter identifier: 指定实例的标识
|
||||
class func guards(_ identifier: String? = nil, from viewController: UIViewController? = nil) -> [Guard] {
|
||||
class func find(_ identifier: String?, from viewController: UIViewController? = nil) -> [Guard] {
|
||||
var gg = [Guard]()
|
||||
if let vc = viewController ?? cfg.rootViewController {
|
||||
for child in vc.children {
|
||||
|
@ -259,16 +184,29 @@ public extension Guard {
|
|||
return gg
|
||||
}
|
||||
|
||||
/// 查找指定的实例
|
||||
/// - Parameter identifier: 标识
|
||||
/// - Parameter last: 已经存在(获取最后一个)
|
||||
/// - Parameter none: 不存在
|
||||
class func find(_ identifier: String?, from viewController: UIViewController? = nil, last: ((Guard) -> Void)? = nil, none: (() -> Void)? = nil) {
|
||||
if let t = find(identifier, from: viewController).last {
|
||||
last?(t)
|
||||
} else {
|
||||
none?()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 弹出屏幕
|
||||
/// - Parameter alert: 实例
|
||||
class func pop(_ guard: Guard) {
|
||||
`guard`.pop()
|
||||
}
|
||||
|
||||
/// 弹出屏幕
|
||||
/// 弹出所有实例
|
||||
/// - Parameter identifier: 指定实例的标识
|
||||
class func pop(from viewController: UIViewController?) {
|
||||
for g in guards(from: viewController) {
|
||||
class func pop(_ identifier: String?, from viewController: UIViewController?) {
|
||||
for g in find(identifier, from: viewController) {
|
||||
g.pop()
|
||||
}
|
||||
}
|
||||
|
@ -277,44 +215,123 @@ public extension Guard {
|
|||
|
||||
|
||||
|
||||
// MARK: - 私有
|
||||
// MARK: - 创建和设置
|
||||
internal extension Guard {
|
||||
|
||||
fileprivate extension Guard {
|
||||
|
||||
/// 点击事件
|
||||
/// - Parameter sender: 手势
|
||||
@objc func privDidTapped(_ sender: UITapGestureRecognizer) {
|
||||
let point = sender.location(in: contentView)
|
||||
if point.x < 0 || point.y < 0 {
|
||||
if force == false {
|
||||
// 点击到操作区域外部
|
||||
pop()
|
||||
/// 加载一个标题
|
||||
/// - Parameter text: 文本
|
||||
@discardableResult func add(title: String?) -> UILabel {
|
||||
let lb = UILabel()
|
||||
lb.font = cfg.guard.titleFont
|
||||
lb.textColor = cfg.primaryLabelColor
|
||||
lb.numberOfLines = 0
|
||||
lb.textAlignment = .center
|
||||
lb.text = title
|
||||
textStack.addArrangedSubview(lb)
|
||||
if #available(iOS 11.0, *) {
|
||||
let count = textStack.arrangedSubviews.count
|
||||
if count > 1 {
|
||||
textStack.setCustomSpacing(cfg.guard.margin * 2, after: textStack.arrangedSubviews[count-2])
|
||||
}
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
}
|
||||
cfg.guard.reloadStack(self)
|
||||
return lb
|
||||
}
|
||||
|
||||
/// 加载一个副标题
|
||||
/// - Parameter text: 文本
|
||||
@discardableResult func add(subTitle: String?) -> UILabel {
|
||||
let lb = add(title: subTitle)
|
||||
lb.font = cfg.guard.subTitleFont
|
||||
lb.textAlignment = .justified
|
||||
return lb
|
||||
}
|
||||
|
||||
func translateIn() {
|
||||
view.backgroundColor = backgroundColor
|
||||
contentView.transform = .identity
|
||||
/// 加载一段正文
|
||||
/// - Parameter text: 文本
|
||||
@discardableResult func add(message: String?) -> UILabel {
|
||||
let lb = UILabel()
|
||||
lb.font = cfg.guard.bodyFont
|
||||
lb.textColor = cfg.secondaryLabelColor
|
||||
lb.numberOfLines = 0
|
||||
lb.textAlignment = .justified
|
||||
lb.text = message
|
||||
textStack.addArrangedSubview(lb)
|
||||
cfg.guard.reloadStack(self)
|
||||
return lb
|
||||
}
|
||||
|
||||
func translateOut() {
|
||||
view.backgroundColor = UIColor(white: 0, alpha: 0)
|
||||
contentView.transform = .init(translationX: 0, y: view.frame.size.height - contentView.frame.minY + cfg.guard.margin)
|
||||
/// 插入一个按钮
|
||||
/// - Parameter index: 索引
|
||||
/// - Parameter style: 样式
|
||||
/// - Parameter title: 标题
|
||||
/// - Parameter action: 事件
|
||||
@discardableResult func insert(action index: Int?, style: UIAlertAction.Style, title: String?, handler: (() -> Void)?) -> UIButton {
|
||||
let btn = Button.actionButton(title: title)
|
||||
btn.titleLabel?.font = cfg.guard.buttonFont
|
||||
if let idx = index, idx < actionStack.arrangedSubviews.count {
|
||||
actionStack.insertArrangedSubview(btn, at: idx)
|
||||
} else {
|
||||
actionStack.addArrangedSubview(btn)
|
||||
}
|
||||
btn.update(style: style)
|
||||
cfg.guard.reloadStack(self)
|
||||
addTouchUpAction(for: btn) { [weak self] in
|
||||
handler?()
|
||||
if btn.tag == UIAlertAction.Style.cancel.rawValue {
|
||||
self?.pop()
|
||||
}
|
||||
}
|
||||
if isLoadFinished {
|
||||
UIView.animateForGuard {
|
||||
self.view.layoutIfNeeded()
|
||||
}
|
||||
}
|
||||
return btn
|
||||
}
|
||||
|
||||
/// 更新按钮
|
||||
/// - Parameter index: 索引
|
||||
/// - Parameter style: 样式
|
||||
/// - Parameter title: 标题
|
||||
/// - Parameter handler: 事件
|
||||
func update(action index: Int, style: UIAlertAction.Style, title: String?, handler: (() -> Void)?) {
|
||||
if index < self.actionStack.arrangedSubviews.count, let btn = self.actionStack.arrangedSubviews[index] as? UIButton {
|
||||
btn.setTitle(title, for: .normal)
|
||||
if let b = btn as? Button {
|
||||
b.update(style: style)
|
||||
}
|
||||
if let _ = buttonEvents[btn] {
|
||||
buttonEvents.removeValue(forKey: btn)
|
||||
}
|
||||
addTouchUpAction(for: btn) { [weak self] in
|
||||
handler?()
|
||||
if btn.tag == UIAlertAction.Style.cancel.rawValue {
|
||||
self?.pop()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 移除按钮
|
||||
/// - Parameter index: 索引
|
||||
@discardableResult func privRemoveAction(index: Int) -> Guard {
|
||||
@discardableResult func remove(index: Int) -> Guard {
|
||||
if index < 0 {
|
||||
for view in self.actionStack.arrangedSubviews {
|
||||
if let btn = view as? UIButton {
|
||||
btn.removeFromSuperview()
|
||||
if let _ = buttonEvents[btn] {
|
||||
buttonEvents.removeValue(forKey: btn)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if index < self.actionStack.arrangedSubviews.count, let btn = self.actionStack.arrangedSubviews[index] as? UIButton {
|
||||
btn.removeFromSuperview()
|
||||
if let _ = buttonEvents[btn] {
|
||||
buttonEvents.removeValue(forKey: btn)
|
||||
}
|
||||
}
|
||||
cfg.guard.reloadStack(self)
|
||||
UIView.animateForAlert {
|
||||
|
@ -323,7 +340,31 @@ fileprivate extension Guard {
|
|||
return self
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fileprivate extension Guard {
|
||||
|
||||
/// 点击事件
|
||||
/// - Parameter sender: 手势
|
||||
@objc func privDidTapped(_ sender: UITapGestureRecognizer) {
|
||||
let point = sender.location(in: contentView)
|
||||
if point.x < 0 || point.y < 0 {
|
||||
if isForce == false {
|
||||
// 点击到操作区域外部
|
||||
pop()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func privTranslateIn() {
|
||||
view.backgroundColor = backgroundColor
|
||||
contentView.transform = .identity
|
||||
}
|
||||
|
||||
func privTranslateOut() {
|
||||
view.backgroundColor = UIColor(white: 0, alpha: 0)
|
||||
contentView.transform = .init(translationX: 0, y: view.frame.size.height - contentView.frame.minY + cfg.guard.margin)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
//
|
||||
// GuardModel.swift
|
||||
// ProHUD
|
||||
//
|
||||
// Created by xaoxuu on 2019/8/9.
|
||||
// Copyright © 2019 Titan Studio. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
public extension Guard {
|
||||
|
||||
struct ViewModel {
|
||||
|
||||
internal weak var vc: Guard?
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public extension Guard.ViewModel {
|
||||
|
||||
/// 加载一个标题
|
||||
/// - Parameter text: 文本
|
||||
@discardableResult func add(title: String?) -> UILabel {
|
||||
return vc!.add(title: title)
|
||||
}
|
||||
|
||||
/// 加载一个副标题
|
||||
/// - Parameter text: 文本
|
||||
@discardableResult func add(subTitle: String?) -> UILabel {
|
||||
return vc!.add(subTitle: subTitle)
|
||||
}
|
||||
|
||||
/// 加载一段正文
|
||||
/// - Parameter text: 文本
|
||||
@discardableResult func add(message: String?) -> UILabel {
|
||||
return vc!.add(message: message)
|
||||
}
|
||||
|
||||
/// 加载一个按钮
|
||||
/// - Parameter style: 样式
|
||||
/// - Parameter title: 标题
|
||||
/// - Parameter action: 事件
|
||||
@discardableResult func add(action style: UIAlertAction.Style, title: String?, handler: (() -> Void)?) -> UIButton {
|
||||
return vc!.insert(action: nil, style: style, title: title, handler: handler)
|
||||
}
|
||||
|
||||
/// 插入一个按钮
|
||||
/// - Parameter index: 索引
|
||||
/// - Parameter style: 样式
|
||||
/// - Parameter title: 标题
|
||||
/// - Parameter handler: 事件处理
|
||||
@discardableResult func insert(action index: Int, style: UIAlertAction.Style, title: String?, handler: (() -> Void)?) -> UIButton {
|
||||
return vc!.insert(action: index, style: style, title: title, handler: handler)
|
||||
}
|
||||
|
||||
/// 更新按钮
|
||||
/// - Parameter index: 索引
|
||||
/// - Parameter style: 样式
|
||||
/// - Parameter title: 标题
|
||||
/// - Parameter handler: 事件处理
|
||||
func update(action index: Int, style: UIAlertAction.Style, title: String?, handler: (() -> Void)?) {
|
||||
vc?.update(action: index, style: style, title: title, handler: handler)
|
||||
}
|
||||
|
||||
/// 移除按钮
|
||||
/// - Parameter index: 索引
|
||||
func remove(action index: Int...) {
|
||||
for (i, idx) in index.enumerated() {
|
||||
vc?.remove(index: idx-i)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
//
|
||||
// GuardView.swift
|
||||
// ProHUD
|
||||
//
|
||||
// Created by xaoxuu on 2019/7/31.
|
||||
// Copyright © 2019 Titan Studio. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
internal extension Guard {
|
||||
|
||||
class Button: UIButton {
|
||||
class func actionButton(title: String?) -> Button {
|
||||
let btn = Button(type: .system)
|
||||
btn.setTitle(title, for: .normal)
|
||||
btn.layer.cornerRadius = cfg.guard.buttonCornerRadius
|
||||
btn.titleLabel?.font = cfg.guard.buttonFont
|
||||
return btn
|
||||
}
|
||||
|
||||
func update(style: UIAlertAction.Style) {
|
||||
let pd = CGFloat(8)
|
||||
if style != .cancel {
|
||||
contentEdgeInsets = .init(top: pd*1.5+2, left: pd*1.5, bottom: pd*1.5+2, right: pd*1.5)
|
||||
} else {
|
||||
contentEdgeInsets = .init(top: pd*1+2, left: pd*1.5, bottom: pd*1+2, right: pd*1.5)
|
||||
}
|
||||
switch style {
|
||||
case .default:
|
||||
backgroundColor = tintColor
|
||||
setTitleColor(.white, for: .normal)
|
||||
case .destructive:
|
||||
backgroundColor = .init(red: 244/255, green: 67/255, blue: 54/255, alpha: 1)
|
||||
setTitleColor(.white, for: .normal)
|
||||
case .cancel:
|
||||
setTitleColor(cfg.secondaryLabelColor, for: .normal)
|
||||
@unknown default:
|
||||
break
|
||||
}
|
||||
tag = style.rawValue
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -19,19 +19,19 @@ public extension ProHUD {
|
|||
|
||||
/// 动态颜色(适配iOS13)
|
||||
public lazy var dynamicColor: UIColor = {
|
||||
// if #available(iOS 13.0, *) {
|
||||
// let color = UIColor { (traitCollection: UITraitCollection) -> UIColor in
|
||||
// if traitCollection.userInterfaceStyle == .dark {
|
||||
// return .white
|
||||
// } else {
|
||||
// return .black
|
||||
// }
|
||||
// }
|
||||
// return color
|
||||
// } else {
|
||||
// // Fallback on earlier versions
|
||||
// }
|
||||
return .init(white: 0.15, alpha: 1)
|
||||
if #available(iOS 13.0, *) {
|
||||
let color = UIColor { (traitCollection: UITraitCollection) -> UIColor in
|
||||
if traitCollection.userInterfaceStyle == .dark {
|
||||
return .init(white: 1, alpha: 1)
|
||||
} else {
|
||||
return .init(white: 0.1, alpha: 1)
|
||||
}
|
||||
}
|
||||
return color
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
}
|
||||
return .init(white: 0.1, alpha: 1)
|
||||
}()
|
||||
|
||||
/// 主标签文本颜色
|
||||
|
@ -41,9 +41,13 @@ public extension ProHUD {
|
|||
|
||||
/// 次级标签文本颜色
|
||||
public lazy var secondaryLabelColor: UIColor = {
|
||||
return dynamicColor.withAlphaComponent(0.6)
|
||||
return dynamicColor.withAlphaComponent(0.66)
|
||||
}()
|
||||
|
||||
public func blurView(_ callback: @escaping () -> UIVisualEffectView) {
|
||||
createBlurView = callback
|
||||
}
|
||||
|
||||
|
||||
public var toast = Toast()
|
||||
public var alert = Alert()
|
||||
|
@ -78,3 +82,19 @@ public extension ProHUD {
|
|||
config(&cfg)
|
||||
}
|
||||
}
|
||||
|
||||
internal var createBlurView: () -> UIVisualEffectView = {
|
||||
return {
|
||||
let vev = UIVisualEffectView()
|
||||
if #available(iOS 13.0, *) {
|
||||
vev.effect = UIBlurEffect(style: .systemMaterial)
|
||||
// vev.effect = UIBlurEffect(style: .extraLight)
|
||||
} else if #available(iOS 11.0, *) {
|
||||
vev.effect = UIBlurEffect(style: .extraLight)
|
||||
} else {
|
||||
vev.effect = .none
|
||||
vev.backgroundColor = .white
|
||||
}
|
||||
return vev
|
||||
}
|
||||
}()
|
||||
|
|
|
@ -13,9 +13,10 @@ public class HUDController: UIViewController {
|
|||
/// ID标识
|
||||
public var identifier = String(Date().timeIntervalSince1970)
|
||||
|
||||
internal var willAppearCallback: (() -> Void)?
|
||||
internal var didAppearCallback: (() -> Void)?
|
||||
internal var willDisappearCallback: (() -> Void)?
|
||||
/// 消失回调
|
||||
internal var disappearCallback: (() -> Void)?
|
||||
internal var didDisappearCallback: (() -> Void)?
|
||||
|
||||
/// 按钮事件
|
||||
internal var buttonEvents = [UIButton:() -> Void]()
|
||||
|
@ -40,10 +41,31 @@ public class HUDController: UIViewController {
|
|||
// Do any additional setup after loading the view.
|
||||
}
|
||||
|
||||
|
||||
public override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
willAppearCallback?()
|
||||
}
|
||||
public override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
didAppearCallback?()
|
||||
}
|
||||
public override func viewDidDisappear(_ animated: Bool) {
|
||||
super.viewDidDisappear(animated)
|
||||
disappearCallback?()
|
||||
didDisappearCallback?()
|
||||
}
|
||||
|
||||
public func viewWillAppear(_ callback: (() -> Void)?) {
|
||||
willAppearCallback = callback
|
||||
}
|
||||
public func viewDidAppear(_ callback: (() -> Void)?) {
|
||||
didAppearCallback = callback
|
||||
}
|
||||
|
||||
public func viewWillDisappear(_ callback: (() -> Void)?) {
|
||||
willDisappearCallback = callback
|
||||
}
|
||||
public func viewDidDisappear(_ callback: (() -> Void)?) {
|
||||
didDisappearCallback = callback
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -27,34 +27,94 @@ public extension ProHUD {
|
|||
|
||||
}
|
||||
|
||||
class BlurView: UIVisualEffectView {
|
||||
}
|
||||
|
||||
init() {
|
||||
internal extension Alert {
|
||||
class Button: UIButton {
|
||||
class func actionButton(title: String?) -> Button {
|
||||
let btn = Button(type: .system)
|
||||
btn.setTitle(title, for: .normal)
|
||||
btn.layer.cornerRadius = cfg.alert.cornerRadius / 2
|
||||
btn.titleLabel?.font = cfg.alert.buttonFont
|
||||
return btn
|
||||
}
|
||||
|
||||
if #available(iOS 13.0, *) {
|
||||
// super.init(effect: UIBlurEffect(style: .systemMaterial))
|
||||
super.init(effect: UIBlurEffect(style: .extraLight))
|
||||
} else if #available(iOS 11.0, *) {
|
||||
super.init(effect: UIBlurEffect(style: .extraLight))
|
||||
func update(style: UIAlertAction.Style) {
|
||||
let pd = CGFloat(8)
|
||||
if style != .cancel {
|
||||
backgroundColor = cfg.dynamicColor.withAlphaComponent(0.04)
|
||||
contentEdgeInsets = .init(top: pd*1.5, left: pd*1.5, bottom: pd*1.5, right: pd*1.5)
|
||||
} else {
|
||||
super.init(effect: .none)
|
||||
backgroundColor = .white
|
||||
backgroundColor = .clear
|
||||
contentEdgeInsets = .init(top: pd*0.5, left: pd*1.5, bottom: pd*0.5, right: pd*1.5)
|
||||
}
|
||||
switch style {
|
||||
case .default:
|
||||
setTitleColor(tintColor, for: .normal)
|
||||
case .destructive:
|
||||
setTitleColor(.init(red: 244/255, green: 67/255, blue: 54/255, alpha: 1), for: .normal)
|
||||
case .cancel:
|
||||
setTitleColor(cfg.secondaryLabelColor, for: .normal)
|
||||
@unknown default:
|
||||
break
|
||||
}
|
||||
tag = style.rawValue
|
||||
}
|
||||
|
||||
class func forceQuitButton() -> UIButton {
|
||||
let btn = Button(type: .system)
|
||||
let pd = cfg.alert.padding/2
|
||||
btn.contentEdgeInsets = .init(top: pd*1.5, left: pd*1.5, bottom: pd*1.5, right: pd*1.5)
|
||||
btn.imageEdgeInsets.right = pd*1.5
|
||||
btn.setTitleColor(UIColor(red:1.00, green:0.55, blue:0.21, alpha:1.00), for: .normal)
|
||||
btn.titleLabel?.font = cfg.alert.buttonFont
|
||||
return btn
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
internal extension Guard {
|
||||
|
||||
class Button: UIButton {
|
||||
class func actionButton(title: String?) -> Button {
|
||||
let btn = Button(type: .system)
|
||||
btn.setTitle(title, for: .normal)
|
||||
btn.layer.cornerRadius = cfg.guard.buttonCornerRadius
|
||||
btn.titleLabel?.font = cfg.guard.buttonFont
|
||||
return btn
|
||||
}
|
||||
|
||||
func update(style: UIAlertAction.Style) {
|
||||
let pd = CGFloat(8)
|
||||
if style != .cancel {
|
||||
contentEdgeInsets = .init(top: pd*1.5+2, left: pd*1.5, bottom: pd*1.5+2, right: pd*1.5)
|
||||
} else {
|
||||
contentEdgeInsets = .init(top: pd*1+2, left: pd*1.5, bottom: pd*1+2, right: pd*1.5)
|
||||
}
|
||||
switch style {
|
||||
case .default:
|
||||
backgroundColor = tintColor
|
||||
setTitleColor(.white, for: .normal)
|
||||
case .destructive:
|
||||
backgroundColor = .init(red: 244/255, green: 67/255, blue: 54/255, alpha: 1)
|
||||
setTitleColor(.white, for: .normal)
|
||||
case .cancel:
|
||||
backgroundColor = .clear
|
||||
setTitleColor(cfg.secondaryLabelColor, for: .normal)
|
||||
@unknown default:
|
||||
break
|
||||
}
|
||||
tag = style.rawValue
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
internal extension UIView {
|
||||
|
||||
class func animateEaseOut(duration: TimeInterval = 1, delay: TimeInterval = 0, animations: @escaping () -> Void, completion: ((Bool) -> Void)? = nil) {
|
||||
private class func animateEaseOut(duration: TimeInterval = 1, delay: TimeInterval = 0, animations: @escaping () -> Void, completion: ((Bool) -> Void)? = nil) {
|
||||
animate(withDuration: duration, delay: delay, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: [.allowUserInteraction, .curveEaseOut], animations: animations, completion: completion)
|
||||
}
|
||||
|
||||
|
@ -64,27 +124,26 @@ internal extension UIView {
|
|||
class func animateForAlertBuildOut(animations: @escaping () -> Void, completion: ((Bool) -> Void)? = nil) {
|
||||
animateEaseOut(duration: 0.38, delay: 0, animations: animations, completion: completion)
|
||||
}
|
||||
class func animateForAlert(animations: @escaping () -> Void) {
|
||||
animateEaseOut(duration: 1, delay: 0, animations: animations, completion: nil)
|
||||
}
|
||||
|
||||
class func animateForAlert(animations: @escaping () -> Void, completion: ((Bool) -> Void)? = nil) {
|
||||
animateEaseOut(duration: 1, delay: 0, animations: animations, completion: completion)
|
||||
}
|
||||
|
||||
class func animateForToast(animations: @escaping () -> Void) {
|
||||
animateEaseOut(duration: 1, delay: 0, animations: animations, completion: nil)
|
||||
class func animateForAlert(animations: @escaping () -> Void) {
|
||||
animateForAlert(animations: animations, completion: nil)
|
||||
}
|
||||
|
||||
class func animateForToast(animations: @escaping () -> Void, completion: ((Bool) -> Void)? = nil) {
|
||||
animateEaseOut(duration: 1, delay: 0, animations: animations, completion: completion)
|
||||
animateEaseOut(duration: 1.2, delay: 0, animations: animations, completion: completion)
|
||||
}
|
||||
|
||||
class func animateForGuard(animations: @escaping () -> Void) {
|
||||
animateForGuard(animations: animations, completion: nil)
|
||||
class func animateForToast(animations: @escaping () -> Void) {
|
||||
animateForToast(animations: animations, completion: nil)
|
||||
}
|
||||
|
||||
class func animateForGuard(animations: @escaping () -> Void, completion: ((Bool) -> Void)? = nil) {
|
||||
animateEaseOut(duration: 0.6, delay: 0, animations: animations, completion: completion)
|
||||
}
|
||||
class func animateForGuard(animations: @escaping () -> Void) {
|
||||
animateForGuard(animations: animations, completion: nil)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,10 +21,13 @@ public extension ProHUD.Configuration {
|
|||
public var padding = CGFloat(16)
|
||||
|
||||
// MARK: 图标样式
|
||||
/// 图标、default按钮的颜色
|
||||
public var tintColor: UIColor?
|
||||
/// 图标尺寸
|
||||
public var iconSize = CGSize(width: 48, height: 48)
|
||||
/// 某个场景的默认图片
|
||||
/// - Parameter callback: 回调
|
||||
public func iconForScene(_ callback: @escaping (ProHUD.Toast.Scene) -> UIImage?) {
|
||||
privIconForScene = callback
|
||||
}
|
||||
|
||||
// MARK: 文本样式
|
||||
/// 标题字体
|
||||
|
@ -37,71 +40,56 @@ public extension ProHUD.Configuration {
|
|||
/// 正文最多行数
|
||||
public var bodyMaxLines = Int(10)
|
||||
|
||||
/// 加载视图(如果需要完全自定义整个View,可以重写这个)
|
||||
/// - Parameter callback: 回调代码
|
||||
public mutating func loadSubviews(_ callback: @escaping (ProHUD.Toast) -> Void) {
|
||||
privLoadSubviews = callback
|
||||
}
|
||||
|
||||
/// 更新视图
|
||||
/// - Parameter callback: 回调代码
|
||||
public mutating func reloadData(_ callback: @escaping (ProHUD.Toast) -> Void) {
|
||||
privReloadData = callback
|
||||
}
|
||||
|
||||
/// 默认持续时间(当viewmodel的duration为nil时,会从这里获取)
|
||||
public mutating func durationForScene(_ callback: @escaping (ProHUD.Toast.Scene) -> TimeInterval?) {
|
||||
privDurationForScene = callback
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - 默认实现
|
||||
|
||||
// MARK: - 内部调用
|
||||
internal extension ProHUD.Configuration.Toast {
|
||||
var loadSubviews: (ProHUD.Toast) -> Void {
|
||||
return privLoadSubviews
|
||||
}
|
||||
|
||||
var reloadData: (ProHUD.Toast) -> Void {
|
||||
return privReloadData
|
||||
}
|
||||
|
||||
var durationForScene: (ProHUD.Toast.Scene) -> TimeInterval? {
|
||||
return privDurationForScene
|
||||
}
|
||||
|
||||
fileprivate var privLoadSubviews: (ProHUD.Toast) -> Void = {
|
||||
return { (vc) in
|
||||
debug(vc, "loadSubviews")
|
||||
vc.view.tintColor = cfg.toast.tintColor
|
||||
vc.view.addSubview(vc.titleLabel)
|
||||
vc.view.addSubview(vc.bodyLabel)
|
||||
vc.view.addSubview(vc.imageView)
|
||||
}
|
||||
}()
|
||||
|
||||
// MARK: - 默认实现
|
||||
fileprivate var privReloadData: (ProHUD.Toast) -> Void = {
|
||||
return { (vc) in
|
||||
debug(vc, "reloadData")
|
||||
let config = cfg.toast
|
||||
let scene = vc.model.scene
|
||||
// 设置数据
|
||||
let imgStr: String
|
||||
switch vc.model.scene {
|
||||
case .success:
|
||||
imgStr = "ProHUDSuccess"
|
||||
case .warning:
|
||||
imgStr = "ProHUDWarning"
|
||||
case .error:
|
||||
imgStr = "ProHUDError"
|
||||
case .loading:
|
||||
imgStr = "ProHUDLoading"
|
||||
case .confirm:
|
||||
imgStr = "ProHUDMessage"
|
||||
case .delete:
|
||||
imgStr = "ProHUDTrash"
|
||||
default:
|
||||
imgStr = "ProHUDMessage"
|
||||
let scene = vc.vm.scene
|
||||
if vc.titleLabel.superview == nil {
|
||||
vc.view.addSubview(vc.titleLabel)
|
||||
}
|
||||
let img = vc.model.icon ?? ProHUD.image(named: imgStr)
|
||||
vc.imageView.image = img
|
||||
if vc.bodyLabel.superview == nil {
|
||||
vc.view.addSubview(vc.bodyLabel)
|
||||
}
|
||||
if vc.imageView.superview == nil {
|
||||
vc.view.addSubview(vc.imageView)
|
||||
}
|
||||
// 设置数据
|
||||
vc.imageView.image = vc.vm.icon ?? privIconForScene(vc.vm.scene)
|
||||
vc.imageView.layer.removeAllAnimations()
|
||||
vc.titleLabel.textColor = cfg.primaryLabelColor
|
||||
vc.titleLabel.text = vc.model.title
|
||||
vc.titleLabel.text = vc.vm.title
|
||||
vc.bodyLabel.textColor = cfg.secondaryLabelColor
|
||||
vc.bodyLabel.text = vc.model.message
|
||||
vc.bodyLabel.text = vc.vm.message
|
||||
|
||||
// 更新布局
|
||||
vc.imageView.snp.makeConstraints { (mk) in
|
||||
|
@ -123,13 +111,45 @@ fileprivate var privReloadData: (ProHUD.Toast) -> Void = {
|
|||
}
|
||||
|
||||
vc.view.layoutIfNeeded()
|
||||
switch vc.model.scene {
|
||||
case .loading:
|
||||
vc.duration(nil)
|
||||
default:
|
||||
vc.duration(3)
|
||||
}
|
||||
|
||||
// 设置持续时间
|
||||
vc.vm.updateDuration()
|
||||
|
||||
}
|
||||
|
||||
}()
|
||||
|
||||
|
||||
|
||||
fileprivate var privIconForScene: (ProHUD.Toast.Scene) -> UIImage? = {
|
||||
return { (scene) in
|
||||
let imgStr: String
|
||||
switch scene {
|
||||
case .success:
|
||||
imgStr = "ProHUDSuccess"
|
||||
case .warning:
|
||||
imgStr = "ProHUDWarning"
|
||||
case .error:
|
||||
imgStr = "ProHUDError"
|
||||
case .loading:
|
||||
imgStr = "ProHUDLoading"
|
||||
default:
|
||||
imgStr = "ProHUDMessage"
|
||||
}
|
||||
return ProHUD.image(named: imgStr)
|
||||
}
|
||||
}()
|
||||
|
||||
|
||||
fileprivate var privDurationForScene: (ProHUD.Toast.Scene) -> TimeInterval? = {
|
||||
return { (scene) in
|
||||
switch scene {
|
||||
case .loading:
|
||||
return nil
|
||||
case .error, .warning:
|
||||
return 5
|
||||
default:
|
||||
return 3
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
|
|
@ -47,23 +47,16 @@ public extension ProHUD {
|
|||
|
||||
/// 背景层
|
||||
public var backgroundView: UIVisualEffectView = {
|
||||
let vev = UIVisualEffectView()
|
||||
if #available(iOS 13.0, *) {
|
||||
// vev.effect = UIBlurEffect.init(style: .systemMaterial))
|
||||
vev.effect = UIBlurEffect.init(style: .extraLight)
|
||||
} else if #available(iOS 11.0, *) {
|
||||
vev.effect = UIBlurEffect.init(style: .extraLight)
|
||||
} else {
|
||||
vev.effect = .none
|
||||
vev.backgroundColor = .white
|
||||
}
|
||||
let vev = createBlurView()
|
||||
vev.layer.masksToBounds = true
|
||||
vev.layer.cornerRadius = cfg.toast.cornerRadius
|
||||
return vev
|
||||
}()
|
||||
|
||||
/// 视图模型
|
||||
public var model = ViewModel()
|
||||
public var vm = ViewModel()
|
||||
|
||||
internal var maxY = CGFloat(0)
|
||||
|
||||
// MARK: 生命周期
|
||||
|
||||
|
@ -72,17 +65,16 @@ public extension ProHUD {
|
|||
/// - Parameter title: 标题
|
||||
/// - Parameter message: 内容
|
||||
/// - Parameter icon: 图标
|
||||
public convenience init(scene: Scene = .default, title: String? = nil, message: String? = nil, icon: UIImage? = nil, actions: ((Toast) -> Void)? = nil) {
|
||||
public convenience init(scene: Scene = .default, title: String? = nil, message: String? = nil, icon: UIImage? = nil, duration: TimeInterval? = nil, actions: ((Toast) -> Void)? = nil) {
|
||||
self.init()
|
||||
vm.vc = self
|
||||
|
||||
model.scene = scene
|
||||
model.title = title
|
||||
model.message = message
|
||||
model.icon = icon
|
||||
vm.scene = scene
|
||||
vm.title = title
|
||||
vm.message = message
|
||||
vm.icon = icon
|
||||
vm.duration = duration
|
||||
actions?(self)
|
||||
// 布局
|
||||
cfg.toast.loadSubviews(self)
|
||||
cfg.toast.reloadData(self)
|
||||
|
||||
// 点击
|
||||
let tap = UITapGestureRecognizer(target: self, action: #selector(privDidTapped(_:)))
|
||||
|
@ -94,16 +86,21 @@ public extension ProHUD {
|
|||
}
|
||||
|
||||
|
||||
public override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
cfg.toast.reloadData(self)
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - 实例函数
|
||||
|
||||
public extension Toast {
|
||||
|
||||
// MARK: 生命周期函数
|
||||
|
||||
/// 推入屏幕
|
||||
@discardableResult func push() -> Toast {
|
||||
let config = cfg.toast
|
||||
|
@ -145,7 +142,7 @@ public extension Toast {
|
|||
if Toast.toasts.contains(self) == false {
|
||||
Toast.toasts.append(self)
|
||||
}
|
||||
Toast.updateToastsLayout()
|
||||
Toast.privUpdateToastsLayout()
|
||||
if isNew {
|
||||
window.transform = .init(translationX: 0, y: -window.frame.maxY)
|
||||
UIView.animateForToast {
|
||||
|
@ -159,83 +156,58 @@ public extension Toast {
|
|||
|
||||
/// 弹出屏幕
|
||||
func pop() {
|
||||
Toast.removeItemFromArray(toast: self)
|
||||
UIView.animateForToast(animations: {
|
||||
let frame = self.window?.frame ?? .zero
|
||||
self.window?.transform = .init(translationX: 0, y: -200-frame.maxY)
|
||||
}) { (done) in
|
||||
self.view.removeFromSuperview()
|
||||
self.removeFromParent()
|
||||
self.window = nil
|
||||
}
|
||||
Toast.pop(self)
|
||||
}
|
||||
|
||||
// MARK: 设置函数
|
||||
|
||||
/// 设置持续时间
|
||||
/// - Parameter duration: 持续时间
|
||||
@discardableResult func duration(_ duration: TimeInterval?) -> Toast {
|
||||
model.setupDuration(duration: duration) { [weak self] in
|
||||
self?.pop()
|
||||
}
|
||||
return self
|
||||
/// 更新
|
||||
/// - Parameter callback: 回调
|
||||
func update(_ callback: ((inout ViewModel) -> Void)? = nil) {
|
||||
callback?(&vm)
|
||||
cfg.toast.reloadData(self)
|
||||
}
|
||||
|
||||
/// 点击事件
|
||||
/// - Parameter callback: 事件回调
|
||||
@discardableResult func didTapped(_ callback: (() -> Void)?) -> Toast {
|
||||
model.tapCallback = callback
|
||||
return self
|
||||
func didTapped(_ callback: (() -> Void)?) {
|
||||
vm.tapCallback = callback
|
||||
}
|
||||
|
||||
/// 消失事件
|
||||
/// - Parameter callback: 事件回调
|
||||
@discardableResult func didDisappear(_ callback: (() -> Void)?) -> Toast {
|
||||
disappearCallback = callback
|
||||
return self
|
||||
/// 图片旋转效果
|
||||
/// - Parameter flag: 是否开启
|
||||
func rotate(_ flag: Bool = true) {
|
||||
if flag {
|
||||
DispatchQueue.main.async {
|
||||
let ani = CABasicAnimation(keyPath: "transform.rotation.z")
|
||||
ani.toValue = Double.pi * 2.0
|
||||
ani.duration = 3
|
||||
ani.repeatCount = 10000
|
||||
self.imageView.layer.add(ani, forKey: "rotationAnimation")
|
||||
}
|
||||
} else {
|
||||
imageView.layer.removeAllAnimations()
|
||||
}
|
||||
}
|
||||
|
||||
/// 更新
|
||||
/// - Parameter scene: 场景
|
||||
/// - Parameter title: 标题
|
||||
/// - Parameter message: 内容
|
||||
@discardableResult func update(scene: Scene, title: String?, message: String?) -> Toast {
|
||||
model.scene = scene
|
||||
model.title = title
|
||||
model.message = message
|
||||
cfg.toast.reloadData(self)
|
||||
return self
|
||||
}
|
||||
/// 脉冲效果
|
||||
func pulse() {
|
||||
DispatchQueue.main.async {
|
||||
UIView.animate(withDuration: 0.2, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: [.allowUserInteraction, .curveEaseOut], animations: {
|
||||
self.window?.transform = .init(scaleX: 1.04, y: 1.04)
|
||||
}) { (done) in
|
||||
UIView.animate(withDuration: 1, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 1, options: [.allowUserInteraction, .curveEaseIn], animations: {
|
||||
self.window?.transform = .identity
|
||||
}) { (done) in
|
||||
|
||||
/// 更新标题
|
||||
/// - Parameter title: 标题
|
||||
@discardableResult func update(title: String?) -> Toast {
|
||||
model.title = title
|
||||
cfg.toast.reloadData(self)
|
||||
return self
|
||||
}
|
||||
|
||||
/// 更新文本
|
||||
/// - Parameter message: 消息
|
||||
@discardableResult func update(message: String?) -> Toast {
|
||||
model.message = message
|
||||
cfg.toast.reloadData(self)
|
||||
return self
|
||||
}
|
||||
|
||||
/// 更新图标
|
||||
/// - Parameter icon: 图标
|
||||
@discardableResult func update(icon: UIImage?) -> Toast {
|
||||
model.icon = icon
|
||||
cfg.toast.reloadData(self)
|
||||
return self
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// MARK: 类函数
|
||||
|
||||
// MARK: - 实例管理器
|
||||
public extension Toast {
|
||||
|
||||
/// 推入屏幕
|
||||
|
@ -243,13 +215,13 @@ public extension Toast {
|
|||
/// - Parameter title: 标题
|
||||
/// - Parameter message: 内容
|
||||
/// - Parameter actions: 更多操作
|
||||
@discardableResult class func push(toast scene: Toast.Scene, title: String? = nil, message: String? = nil, actions: ((Toast) -> Void)? = nil) -> Toast {
|
||||
return Toast(scene: scene, title: title, message: message, actions: actions).push()
|
||||
@discardableResult class func push(scene: Toast.Scene = .default, title: String? = nil, message: String? = nil, duration: TimeInterval? = nil, _ actions: ((Toast) -> Void)? = nil) -> Toast {
|
||||
return Toast(scene: scene, title: title, message: message, duration: duration, actions: actions).push()
|
||||
}
|
||||
|
||||
/// 获取指定的toast
|
||||
/// 查找指定的实例
|
||||
/// - Parameter identifier: 标识
|
||||
class func toasts(_ identifier: String?) -> [Toast] {
|
||||
class func find(_ identifier: String?) -> [Toast] {
|
||||
var tt = [Toast]()
|
||||
for t in toasts {
|
||||
if t.identifier == identifier {
|
||||
|
@ -259,73 +231,87 @@ public extension Toast {
|
|||
return tt
|
||||
}
|
||||
|
||||
/// 查找指定的实例
|
||||
/// - Parameter identifier: 标识
|
||||
/// - Parameter last: 已经存在(获取最后一个)
|
||||
/// - Parameter none: 不存在
|
||||
class func find(_ identifier: String?, last: ((Toast) -> Void)? = nil, none: (() -> Void)? = nil) {
|
||||
if let t = find(identifier).last {
|
||||
last?(t)
|
||||
} else {
|
||||
none?()
|
||||
}
|
||||
}
|
||||
|
||||
/// 弹出屏幕
|
||||
/// - Parameter toast: 实例
|
||||
class func pop(_ toast: Toast) {
|
||||
toast.pop()
|
||||
}
|
||||
|
||||
/// 弹出屏幕
|
||||
/// - Parameter identifier: 指定实例的标识
|
||||
class func pop(_ identifier: String?) {
|
||||
for t in toasts(identifier) {
|
||||
t.pop()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: 私有
|
||||
|
||||
fileprivate var willUpdateToastsLayout: DispatchWorkItem?
|
||||
|
||||
fileprivate extension Toast {
|
||||
|
||||
/// 点击事件
|
||||
/// - Parameter sender: 手势
|
||||
@objc func privDidTapped(_ sender: UITapGestureRecognizer) {
|
||||
model.tapCallback?()
|
||||
}
|
||||
|
||||
/// 拖拽事件
|
||||
/// - Parameter sender: 手势
|
||||
@objc func privDidPan(_ sender: UIPanGestureRecognizer) {
|
||||
model.durationBlock?.cancel()
|
||||
let point = sender.translation(in: sender.view)
|
||||
window?.transform = .init(translationX: 0, y: point.y)
|
||||
if sender.state == .recognized {
|
||||
let v = sender.velocity(in: sender.view)
|
||||
if model.removable == true && (((window?.frame.origin.y ?? 0) < 0 && v.y < 0) || v.y < -1200) {
|
||||
// 移除
|
||||
self.pop()
|
||||
} else {
|
||||
UIView.animateForToast(animations: {
|
||||
self.window?.transform = .identity
|
||||
}) { (done) in
|
||||
// FIXME: 重置计时器
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 从数组中移除
|
||||
/// - Parameter toast: 实例
|
||||
class func removeItemFromArray(toast: Toast) {
|
||||
toast.willDisappearCallback?()
|
||||
if toasts.count > 1 {
|
||||
for (i, t) in toasts.enumerated() {
|
||||
if t == toast {
|
||||
toasts.remove(at: i)
|
||||
}
|
||||
}
|
||||
updateToastsLayout()
|
||||
privUpdateToastsLayout()
|
||||
} else if toasts.count == 1 {
|
||||
toasts.removeAll()
|
||||
} else {
|
||||
debug("漏洞:已经没有toast了")
|
||||
}
|
||||
UIView.animateForToast(animations: {
|
||||
toast.window?.transform = .init(translationX: 0, y: -20-toast.maxY)
|
||||
}) { (done) in
|
||||
toast.view.removeFromSuperview()
|
||||
toast.removeFromParent()
|
||||
toast.window = nil
|
||||
}
|
||||
class func updateToastsLayout() {
|
||||
}
|
||||
|
||||
/// 弹出屏幕
|
||||
/// - Parameter identifier: 指定实例的标识
|
||||
class func pop(_ identifier: String?) {
|
||||
for t in find(identifier) {
|
||||
t.pop()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - 创建和设置
|
||||
fileprivate var willprivUpdateToastsLayout: DispatchWorkItem?
|
||||
|
||||
fileprivate extension Toast {
|
||||
|
||||
/// 点击事件
|
||||
/// - Parameter sender: 手势
|
||||
@objc func privDidTapped(_ sender: UITapGestureRecognizer) {
|
||||
vm.tapCallback?()
|
||||
}
|
||||
|
||||
/// 拖拽事件
|
||||
/// - Parameter sender: 手势
|
||||
@objc func privDidPan(_ sender: UIPanGestureRecognizer) {
|
||||
vm.durationBlock?.cancel()
|
||||
let point = sender.translation(in: sender.view)
|
||||
window?.transform = .init(translationX: 0, y: point.y)
|
||||
if sender.state == .recognized {
|
||||
let v = sender.velocity(in: sender.view)
|
||||
if vm.isRemovable == true && (((window?.frame.origin.y ?? 0) < 0 && v.y < 0) || v.y < -1200) {
|
||||
// 移除
|
||||
self.pop()
|
||||
} else {
|
||||
UIView.animateForToast(animations: {
|
||||
self.window?.transform = .identity
|
||||
}) { (done) in
|
||||
let d = self.vm.duration
|
||||
self.vm.duration = d
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class func privUpdateToastsLayout() {
|
||||
func f() {
|
||||
let top = Inspire.shared.screen.updatedSafeAreaInsets.top
|
||||
for (i, e) in toasts.enumerated() {
|
||||
|
@ -342,18 +328,20 @@ fileprivate extension Toast {
|
|||
let lastY = toasts[i-1].window?.frame.maxY ?? .zero
|
||||
y = lastY + config.margin
|
||||
}
|
||||
e.maxY = y + window.frame.size.height
|
||||
UIView.animateForToast {
|
||||
e.window?.frame.origin.y = y
|
||||
window.frame.origin.y = y
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
willUpdateToastsLayout?.cancel()
|
||||
willUpdateToastsLayout = DispatchWorkItem(block: {
|
||||
willprivUpdateToastsLayout?.cancel()
|
||||
willprivUpdateToastsLayout = DispatchWorkItem(block: {
|
||||
f()
|
||||
})
|
||||
DispatchQueue.main.asyncAfter(deadline: .now()+0.001, execute: willUpdateToastsLayout!)
|
||||
DispatchQueue.main.asyncAfter(deadline: .now()+0.001, execute: willprivUpdateToastsLayout!)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -16,12 +16,6 @@ public extension Toast {
|
|||
/// 加载中场景
|
||||
case loading
|
||||
|
||||
/// 确认场景
|
||||
case confirm
|
||||
|
||||
/// 删除场景
|
||||
case delete
|
||||
|
||||
/// 成功场景
|
||||
case success
|
||||
|
||||
|
@ -33,46 +27,65 @@ public extension Toast {
|
|||
|
||||
}
|
||||
|
||||
struct ViewModel {
|
||||
class ViewModel {
|
||||
|
||||
/// 使用场景
|
||||
public var scene = Scene.default
|
||||
|
||||
/// 标题
|
||||
public var title: String?
|
||||
public var title: String? {
|
||||
didSet {
|
||||
vc?.titleLabel.text = title
|
||||
}
|
||||
}
|
||||
|
||||
/// 正文
|
||||
public var message: String?
|
||||
public var message: String? {
|
||||
didSet {
|
||||
vc?.bodyLabel.text = message
|
||||
}
|
||||
}
|
||||
|
||||
/// 图标
|
||||
public var icon: UIImage?
|
||||
public var icon: UIImage? {
|
||||
didSet {
|
||||
vc?.imageView.image = icon
|
||||
}
|
||||
}
|
||||
|
||||
/// 持续时间
|
||||
internal var duration: TimeInterval?
|
||||
public var duration: TimeInterval? {
|
||||
didSet {
|
||||
updateDuration()
|
||||
}
|
||||
}
|
||||
|
||||
public weak var vc: Toast?
|
||||
|
||||
/// 是否可以通过手势移除(向上滑出屏幕)
|
||||
public var isRemovable = true
|
||||
|
||||
|
||||
// MARK: 私有
|
||||
|
||||
/// 持续时间
|
||||
internal var durationBlock: DispatchWorkItem?
|
||||
|
||||
/// 是否可以通过手势移除(向上划)
|
||||
public var removable = true
|
||||
|
||||
/// 点击事件回调
|
||||
internal var tapCallback: (() -> Void)?
|
||||
|
||||
internal mutating func setupDuration(duration: TimeInterval?, callback: @escaping () -> Void) {
|
||||
self.duration = duration
|
||||
internal func updateDuration() {
|
||||
durationBlock?.cancel()
|
||||
if let t = duration, t > 0 {
|
||||
durationBlock = DispatchWorkItem(block: callback)
|
||||
if let t = duration ?? cfg.toast.durationForScene(scene), t > 0 {
|
||||
durationBlock = DispatchWorkItem(block: { [weak self] in
|
||||
self?.vc?.pop()
|
||||
})
|
||||
DispatchQueue.main.asyncAfter(deadline: .now()+t, execute: durationBlock!)
|
||||
} else {
|
||||
durationBlock = nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
//
|
||||
// ToastView.swift
|
||||
// ProHUD
|
||||
//
|
||||
// Created by xaoxuu on 2019/7/31.
|
||||
// Copyright © 2019 Titan Studio. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
Loading…
Reference in New Issue