├── .githooks
└── pre-commit
├── .github
└── workflows
│ └── jazzy.yml
├── .gitignore
├── .swift-version
├── .swiftformat
├── .swiftpm
└── xcode
│ ├── package.xcworkspace
│ └── contents.xcworkspacedata
│ └── xcshareddata
│ └── xcschemes
│ ├── LTXiOSUtils-Package.xcscheme
│ ├── LTXiOSUtils.xcscheme
│ └── LTXiOSUtilsCoreExtension.xcscheme
├── Documentation
└── .gitkeep
├── LICENSE
├── LTXiOSUtils.podspec
├── LTXiOSUtilsDemo
├── .jazzy.yaml
├── Doc
│ └── jazzy-theme
│ │ ├── assets
│ │ ├── css
│ │ │ ├── highlight.css.scss
│ │ │ └── jazzy.css.scss
│ │ ├── img
│ │ │ ├── carat.png
│ │ │ ├── dash.png
│ │ │ └── gh.png
│ │ └── js
│ │ │ ├── jazzy.js
│ │ │ └── jquery.min.js
│ │ └── templates
│ │ ├── doc.mustache
│ │ ├── footer.mustache
│ │ ├── header.mustache
│ │ ├── nav.mustache
│ │ ├── parameter.mustache
│ │ ├── task.mustache
│ │ └── tasks.mustache
├── Gemfile
├── Gemfile.lock
├── LTXiOSUtilsDemo.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── LTXiOSUtilsDemo.xcscheme
├── LTXiOSUtilsDemo.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── LTXiOSUtilsDemo
│ ├── Application
│ │ ├── AppConfigService.swift
│ │ └── AppDelegate.swift
│ ├── Assets.xcassets
│ │ ├── AccentColor.colorset
│ │ │ └── Contents.json
│ │ ├── AppIcon.appiconset
│ │ │ └── Contents.json
│ │ └── Contents.json
│ ├── BaseViewController.swift
│ ├── Demo
│ │ └── View
│ │ │ └── ClickableLabelDemoViewController.swift
│ ├── Info.plist
│ ├── LibExport.swift
│ ├── Model
│ │ └── PropertyWrapper.swift
│ ├── Resources
│ │ ├── LaunchScreen.storyboard
│ │ └── LaunchScreen.storyboardbak
│ └── ViewController.swift
├── Podfile
├── Podfile.lock
└── Shell
│ └── podInit.sh
├── Package.swift
├── README.md
├── Sources
└── LTXiOSUtils
│ └── Classes
│ ├── Component
│ ├── Class
│ │ ├── BadgeView
│ │ │ ├── BadgeControl.swift
│ │ │ ├── NCSizeConfig+Badge.swift
│ │ │ ├── UIBarButtonItem+BadgeView.swift
│ │ │ ├── UITabBarItem+BadgeView.swift
│ │ │ └── UIView+BadgeView.swift
│ │ ├── CSPaddingLabel.swift
│ │ ├── Checkbox.swift
│ │ ├── ClickableLabel.swift
│ │ ├── CommonShowTextView.swift
│ │ ├── ExpandableLabel.swift
│ │ ├── FoldTableView
│ │ │ ├── FoldAbstractions.swift
│ │ │ └── FoldTableView.swift
│ │ ├── GridMenuView
│ │ │ ├── DefaultGridMenuCell.swift
│ │ │ ├── GridMenuItem.swift
│ │ │ ├── GridMenuView.swift
│ │ │ ├── PageControl.swift
│ │ │ └── ScrollPageControlView.swift
│ │ ├── GrowingTextView.swift
│ │ ├── ImagePickGridView.swift
│ │ ├── MaskPopupView.swift
│ │ ├── RollingNoticeView.swift
│ │ ├── SpinnerButton.swift
│ │ └── TreeTableView
│ │ │ ├── TreeData.swift
│ │ │ ├── TreeNode.swift
│ │ │ ├── TreeTableView.swift
│ │ │ ├── TreeTableViewCell.swift
│ │ │ └── TreeTableViewSearchBar.swift
│ └── Resources
│ │ ├── LTXiOSUtilsResource.swift
│ │ └── Resource
│ │ ├── Assets.xcassets
│ │ ├── Contents.json
│ │ ├── ImagePickGridView
│ │ │ ├── Contents.json
│ │ │ ├── ImagePickGridView_addImage.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ ├── addImage.png
│ │ │ │ ├── addImage@2x.png
│ │ │ │ └── addImage@3x.png
│ │ │ └── ImagePickGridView_deleteImage.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ └── 删除.png
│ │ └── TreeTableView
│ │ │ ├── Contents.json
│ │ │ ├── TreeTableView_arrow.imageset
│ │ │ ├── Contents.json
│ │ │ ├── arrow.png
│ │ │ ├── arrow@2x.png
│ │ │ └── arrow@3x.png
│ │ │ ├── TreeTableView_checkbox_checked.imageset
│ │ │ ├── Contents.json
│ │ │ ├── checkbox-checked.png
│ │ │ ├── checkbox-checked@2x.png
│ │ │ └── checkbox-checked@3x.png
│ │ │ ├── TreeTableView_checkbox_halfchecked.imageset
│ │ │ ├── Contents.json
│ │ │ ├── checkbox-partial.png
│ │ │ ├── checkbox-partial@2x.png
│ │ │ └── checkbox-partial@3x.png
│ │ │ ├── TreeTableView_checkbox_uncheck.imageset
│ │ │ ├── Contents.json
│ │ │ ├── checkbox-uncheck.png
│ │ │ ├── checkbox-uncheck@2x.png
│ │ │ └── checkbox-uncheck@3x.png
│ │ │ └── TreeTableView_search.imageset
│ │ │ ├── Contents.json
│ │ │ ├── search.png
│ │ │ ├── search@2x.png
│ │ │ └── search@3x.png
│ │ ├── en.lproj
│ │ └── Localizable.strings
│ │ └── zh-Hans.lproj
│ │ └── Localizable.strings
│ ├── Extension
│ ├── Core
│ │ └── TxExtensionWrapper.swift
│ ├── Dispatch
│ │ ├── DispatchExtensions.swift
│ │ └── DispatchTimeExtensions.swift
│ ├── Foundation
│ │ ├── BundleExtensions.swift
│ │ ├── DataExtensions.swift
│ │ ├── DateExtensions.swift
│ │ ├── NSObjectExtensions.swift
│ │ └── URLExtensions.swift
│ ├── SwiftStdlib
│ │ ├── CollectionExtensions.swift
│ │ ├── DoubleExtensions.swift
│ │ ├── IntExtensions.swift
│ │ ├── OptionalExtensions.swift
│ │ ├── SequenceExtensions.swift
│ │ └── StringExtensions.swift
│ ├── UIKit
│ │ ├── NSMutableAttributedStringExtensions.swift
│ │ ├── UIButtonExtensions.swift
│ │ ├── UICollectionViewCellExtensions.swift
│ │ ├── UIColorExtensions.swift
│ │ ├── UIFontExtensions.swift
│ │ ├── UIImageExtensions.swift
│ │ ├── UIImageViewExtensions.swift
│ │ ├── UILabelExtensions.swift
│ │ ├── UINavigationControllerExtensions.swift
│ │ ├── UITableViewCellExtensions.swift
│ │ ├── UITapGestureRecognizerExtensions.swift
│ │ ├── UITextFieldExtensions.swift
│ │ ├── UITextViewExtensions.swift
│ │ ├── UIViewControllerExtensions.swift
│ │ └── UIViewExtensions.swift
│ └── WebKit
│ │ └── WKWebViewExtensions.swift
│ ├── PropertyWrapper
│ ├── Atomic.swift
│ └── UserDefaultWrapper.swift
│ └── Util
│ ├── AsyncOperation.swift
│ ├── BackupUtils.swift
│ ├── ChainGrammar.swift
│ ├── Debouncer+Throttler.swift
│ ├── DebugUtils.swift
│ ├── DeviceInfo.swift
│ ├── LaunchImageUtils.swift
│ ├── LaunchMonitor.swift
│ ├── LockUtils.swift
│ ├── Log
│ └── Log.swift
│ ├── ModuleManager.swift
│ ├── NCRuntimeUtils.h
│ ├── NCRuntimeUtils.m
│ ├── NotificationToken.swift
│ ├── ResourceUtils.swift
│ ├── RuntimeUtils.swift
│ ├── SwiftDemangle.swift
│ ├── UIFeedbackGeneratorUtils.swift
│ ├── UserDefaultsProtocol.swift
│ ├── WeakProxy.swift
│ └── WeakWKScriptMessageHandler.swift
└── docs
├── Classes.html
├── Classes
├── ApplicationServiceManagerDelegate.html
├── AsyncOperation.html
├── Atomic.html
├── BadgeControl.html
├── CSPaddingLabel.html
├── Checkbox.html
├── Checkbox
│ ├── BorderStyle.html
│ └── CheckmarkStyle.html
├── CommonShowTextView.html
├── DefaultGridMenuCell.html
├── FoldTableView.html
├── GridMenuView.html
├── GrowingTextView.html
├── ImagePickGridView.html
├── ImagePickGridViewCell.html
├── MaskBackgroundView.html
├── MaskPopupView.html
├── MaskPopupViewBaseAnimator.html
├── MaskPopupViewDownwardAnimator.html
├── MaskPopupViewFadeInOutAnimator.html
├── MaskPopupViewLeftwardAnimator.html
├── MaskPopupViewRightwardAnimator.html
├── MaskPopupViewSpringDownwardAnimator.html
├── MaskPopupViewUpwardAnimator.html
├── MaskPopupViewZoomInOutAnimator.html
├── PageControl.html
├── PickImageModel.html
├── RollingNoticeCell.html
├── RollingNoticeView.html
├── ScrollPageControlView.html
├── SpinnerButton.html
├── TreeData.html
├── TreeNode.html
├── TreeTableView.html
├── TreeTableViewCell.html
├── TreeTableViewSearchBar.html
├── UserDefaultsObservation.html
├── WeakProxy.html
└── WeakWKScriptMessageHandler.html
├── Enums.html
├── Enums
├── AnimationType.html
├── BadgeViewFlexMode.html
├── CommonShowTextViewUrlType.html
├── CornerMarkType.html
├── DateFormateType.html
├── FeedbackType.html
├── FoldActionType.html
├── FoldState.html
├── FontStyle.html
├── GridMenuViewMode.html
├── ImageCompressType.html
├── LogLevel.html
├── MaskPopupViewBackgroundStyle.html
├── PageControlStyle.html
├── ResourcePlistType.html
├── ResourceType.html
├── TreeNodeCheckState.html
├── TreeNodeSordType.html
└── TreeTableViewCellTextStyle.html
├── Extensions.html
├── Extensions
├── Array.html
├── Collection.html
├── Dictionary.html
├── DispatchTime.html
├── NSObject.html
├── NotificationCenter.html
├── Optional.html
├── ProcessInfo.html
├── String.html
├── String
│ └── StringInterpolation.html
├── UIButton.html
├── UIButton
│ └── RepeatButtonClickType.html
├── UIColor.html
├── UIImageView.html
├── UILabel.html
├── UITapGestureRecognizer.html
├── UITextField.html
├── UITextView.html
├── UIView.html
└── WKUserContentController.html
├── Protocols.html
├── Protocols
├── ApplicationService.html
├── FoldTableViewDataSource.html
├── FoldTableViewDelegate.html
├── FoldTableViewHeaderCell.html
├── GridMenuViewItemDelegate.html
├── GrowingTextViewDelegate.html
├── ImagePickGridViewDelegte.html
├── MaskPopupViewAnimationProtocol.html
├── RollingNoticeViewDataSource.html
├── RollingNoticeViewDelegate.html
├── Then.html
├── TreeTableViewDelegate.html
├── TreeTableViewSearchBarDelegate.html
├── TxExtensionWrapperCompatible.html
├── TxExtensionWrapperCompatibleValue.html
└── UserDefaultsProtocol.html
├── Structs.html
├── Structs
├── BackupUtils.html
├── DebugUtils.html
├── DeviceInfo.html
├── GridMenuItem.html
├── Log.html
├── ResourceUtils.html
├── RuntimeUtils.html
├── TxExtensionWrapper.html
├── UIFeedbackGeneratorUtils.html
└── UserDefaultsWrapper.html
├── badge.svg
├── css
├── highlight.css
└── jazzy.css
├── docsets
├── LTXiOSUtils.docset
│ └── Contents
│ │ ├── Info.plist
│ │ └── Resources
│ │ ├── Documents
│ │ ├── Classes.html
│ │ ├── Classes
│ │ │ ├── ApplicationServiceManagerDelegate.html
│ │ │ ├── AsyncOperation.html
│ │ │ ├── Atomic.html
│ │ │ ├── BadgeControl.html
│ │ │ ├── CSPaddingLabel.html
│ │ │ ├── Checkbox.html
│ │ │ ├── Checkbox
│ │ │ │ ├── BorderStyle.html
│ │ │ │ └── CheckmarkStyle.html
│ │ │ ├── CommonShowTextView.html
│ │ │ ├── DefaultGridMenuCell.html
│ │ │ ├── FoldTableView.html
│ │ │ ├── GridMenuView.html
│ │ │ ├── GrowingTextView.html
│ │ │ ├── ImagePickGridView.html
│ │ │ ├── ImagePickGridViewCell.html
│ │ │ ├── MaskBackgroundView.html
│ │ │ ├── MaskPopupView.html
│ │ │ ├── MaskPopupViewBaseAnimator.html
│ │ │ ├── MaskPopupViewDownwardAnimator.html
│ │ │ ├── MaskPopupViewFadeInOutAnimator.html
│ │ │ ├── MaskPopupViewLeftwardAnimator.html
│ │ │ ├── MaskPopupViewRightwardAnimator.html
│ │ │ ├── MaskPopupViewSpringDownwardAnimator.html
│ │ │ ├── MaskPopupViewUpwardAnimator.html
│ │ │ ├── MaskPopupViewZoomInOutAnimator.html
│ │ │ ├── PageControl.html
│ │ │ ├── PickImageModel.html
│ │ │ ├── RollingNoticeCell.html
│ │ │ ├── RollingNoticeView.html
│ │ │ ├── ScrollPageControlView.html
│ │ │ ├── SpinnerButton.html
│ │ │ ├── TreeData.html
│ │ │ ├── TreeNode.html
│ │ │ ├── TreeTableView.html
│ │ │ ├── TreeTableViewCell.html
│ │ │ ├── TreeTableViewSearchBar.html
│ │ │ ├── UserDefaultsObservation.html
│ │ │ ├── WeakProxy.html
│ │ │ └── WeakWKScriptMessageHandler.html
│ │ ├── Enums.html
│ │ ├── Enums
│ │ │ ├── AnimationType.html
│ │ │ ├── BadgeViewFlexMode.html
│ │ │ ├── CommonShowTextViewUrlType.html
│ │ │ ├── CornerMarkType.html
│ │ │ ├── DateFormateType.html
│ │ │ ├── FeedbackType.html
│ │ │ ├── FoldActionType.html
│ │ │ ├── FoldState.html
│ │ │ ├── FontStyle.html
│ │ │ ├── GridMenuViewMode.html
│ │ │ ├── ImageCompressType.html
│ │ │ ├── LogLevel.html
│ │ │ ├── MaskPopupViewBackgroundStyle.html
│ │ │ ├── PageControlStyle.html
│ │ │ ├── ResourcePlistType.html
│ │ │ ├── ResourceType.html
│ │ │ ├── TreeNodeCheckState.html
│ │ │ ├── TreeNodeSordType.html
│ │ │ └── TreeTableViewCellTextStyle.html
│ │ ├── Extensions.html
│ │ ├── Extensions
│ │ │ ├── Array.html
│ │ │ ├── Collection.html
│ │ │ ├── Dictionary.html
│ │ │ ├── DispatchTime.html
│ │ │ ├── NSObject.html
│ │ │ ├── NotificationCenter.html
│ │ │ ├── Optional.html
│ │ │ ├── ProcessInfo.html
│ │ │ ├── String.html
│ │ │ ├── String
│ │ │ │ └── StringInterpolation.html
│ │ │ ├── UIButton.html
│ │ │ ├── UIButton
│ │ │ │ └── RepeatButtonClickType.html
│ │ │ ├── UIColor.html
│ │ │ ├── UIImageView.html
│ │ │ ├── UILabel.html
│ │ │ ├── UITapGestureRecognizer.html
│ │ │ ├── UITextField.html
│ │ │ ├── UITextView.html
│ │ │ ├── UIView.html
│ │ │ └── WKUserContentController.html
│ │ ├── Protocols.html
│ │ ├── Protocols
│ │ │ ├── ApplicationService.html
│ │ │ ├── FoldTableViewDataSource.html
│ │ │ ├── FoldTableViewDelegate.html
│ │ │ ├── FoldTableViewHeaderCell.html
│ │ │ ├── GridMenuViewItemDelegate.html
│ │ │ ├── GrowingTextViewDelegate.html
│ │ │ ├── ImagePickGridViewDelegte.html
│ │ │ ├── MaskPopupViewAnimationProtocol.html
│ │ │ ├── RollingNoticeViewDataSource.html
│ │ │ ├── RollingNoticeViewDelegate.html
│ │ │ ├── Then.html
│ │ │ ├── TreeTableViewDelegate.html
│ │ │ ├── TreeTableViewSearchBarDelegate.html
│ │ │ ├── TxExtensionWrapperCompatible.html
│ │ │ ├── TxExtensionWrapperCompatibleValue.html
│ │ │ └── UserDefaultsProtocol.html
│ │ ├── Structs.html
│ │ ├── Structs
│ │ │ ├── BackupUtils.html
│ │ │ ├── DebugUtils.html
│ │ │ ├── DeviceInfo.html
│ │ │ ├── GridMenuItem.html
│ │ │ ├── Log.html
│ │ │ ├── ResourceUtils.html
│ │ │ ├── RuntimeUtils.html
│ │ │ ├── TxExtensionWrapper.html
│ │ │ ├── UIFeedbackGeneratorUtils.html
│ │ │ └── UserDefaultsWrapper.html
│ │ ├── css
│ │ │ ├── highlight.css
│ │ │ └── jazzy.css
│ │ ├── img
│ │ │ ├── carat.png
│ │ │ ├── dash.png
│ │ │ └── gh.png
│ │ ├── index.html
│ │ ├── js
│ │ │ ├── jazzy.js
│ │ │ └── jquery.min.js
│ │ └── search.json
│ │ └── docSet.dsidx
└── LTXiOSUtils.tgz
├── img
├── carat.png
├── dash.png
└── gh.png
├── index.html
├── js
├── jazzy.js
└── jquery.min.js
├── search.json
└── undocumented.json
/.githooks/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | CURRENT_PATH=$(cd "$(dirname "$0")";pwd) || exit
4 | PROJECT_PATH=$(dirname "$CURRENT_PATH")
5 | "${PROJECT_PATH}"/LTXiOSUtilsDemo/Pods/SwiftFormat/CommandLineTool/swiftformat --lint "${PROJECT_PATH}"
--------------------------------------------------------------------------------
/.github/workflows/jazzy.yml:
--------------------------------------------------------------------------------
1 | # This is a basic workflow to help you get started with Actions
2 |
3 | name: jazzy
4 |
5 | on:
6 | push:
7 | branches: [ master ]
8 | pull_request:
9 | branches: [ master ]
10 | workflow_dispatch:
11 |
12 | jobs:
13 | build:
14 | runs-on: ubuntu-latest
15 | steps:
16 | - uses: actions/checkout@v2
17 | - name: Run a one-line script
18 | run: echo Hello, world!
19 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by https://www.gitignore.io/api/xcode,swift,macos,objective-c
2 | # Edit at https://www.gitignore.io/?templates=xcode,swift,macos,objective-c
3 |
4 | ### macOS ###
5 | # General
6 | .DS_Store
7 | .AppleDouble
8 | .LSOverride
9 |
10 | # Icon must end with two \r
11 | Icon
12 |
13 | # Thumbnails
14 | ._*
15 |
16 | # Files that might appear in the root of a volume
17 | .DocumentRevisions-V100
18 | .fseventsd
19 | .Spotlight-V100
20 | .TemporaryItems
21 | .Trashes
22 | .VolumeIcon.icns
23 | .com.apple.timemachine.donotpresent
24 |
25 | # Directories potentially created on remote AFP share
26 | .AppleDB
27 | .AppleDesktop
28 | Network Trash Folder
29 | Temporary Items
30 | .apdisk
31 |
32 | ### Objective-C ###
33 | # Xcode
34 | #
35 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
36 |
37 | ## Build generated
38 | build/
39 | DerivedData/
40 |
41 | ## Various settings
42 | *.pbxuser
43 | !default.pbxuser
44 | *.mode1v3
45 | !default.mode1v3
46 | *.mode2v3
47 | !default.mode2v3
48 | *.perspectivev3
49 | !default.perspectivev3
50 | xcuserdata/
51 |
52 | ## Other
53 | *.moved-aside
54 | *.xccheckout
55 | *.xcscmblueprint
56 |
57 | ## Obj-C/Swift specific
58 | *.hmap
59 | *.ipa
60 | *.dSYM.zip
61 | *.dSYM
62 |
63 | # CocoaPods
64 | # We recommend against adding the Pods directory to your .gitignore. However
65 | # you should judge for yourself, the pros and cons are mentioned at:
66 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
67 | # Pods/
68 | # Add this line if you want to avoid checking in source code from the Xcode workspace
69 | # *.xcworkspace
70 |
71 | # Carthage
72 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
73 | # Carthage/Checkouts
74 |
75 | Carthage/Build
76 |
77 | # fastlane
78 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
79 | # screenshots whenever they are needed.
80 | # For more information about the recommended setup visit:
81 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
82 |
83 | fastlane/report.xml
84 | fastlane/Preview.html
85 | fastlane/screenshots/**/*.png
86 | fastlane/test_output
87 |
88 | # Code Injection
89 | # After new code Injection tools there's a generated folder /iOSInjectionProject
90 | # https://github.com/johnno1962/injectionforxcode
91 |
92 | iOSInjectionProject/
93 |
94 | ### Objective-C Patch ###
95 |
96 | ### Swift ###
97 | # Xcode
98 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
99 |
100 |
101 |
102 |
103 |
104 | ## Playgrounds
105 | timeline.xctimeline
106 | playground.xcworkspace
107 |
108 | # Swift Package Manager
109 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
110 | # Packages/
111 | # Package.pins
112 | # Package.resolved
113 | .build/
114 |
115 | # CocoaPods
116 | # We recommend against adding the Pods directory to your .gitignore. However
117 | # you should judge for yourself, the pros and cons are mentioned at:
118 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
119 | Pods/
120 | # Add this line if you want to avoid checking in source code from the Xcode workspace
121 | # *.xcworkspace
122 |
123 | # Carthage
124 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
125 | # Carthage/Checkouts
126 |
127 |
128 | # Accio dependency management
129 | Dependencies/
130 | .accio/
131 |
132 | # fastlane
133 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
134 | # screenshots whenever they are needed.
135 | # For more information about the recommended setup visit:
136 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
137 |
138 |
139 | # Code Injection
140 | # After new code Injection tools there's a generated folder /iOSInjectionProject
141 | # https://github.com/johnno1962/injectionforxcode
142 |
143 |
144 | ### Xcode ###
145 | # Xcode
146 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
147 |
148 | ## User settings
149 |
150 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
151 |
152 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
153 |
154 | ## Xcode Patch
155 | *.xcodeproj/*
156 | !*.xcodeproj/project.pbxproj
157 | !*.xcodeproj/xcshareddata/
158 | !*.xcworkspace/contents.xcworkspacedata
159 | /*.gcno
160 |
161 | ### Xcode Patch ###
162 | **/xcshareddata/WorkspaceSettings.xcsettings
163 |
164 | # End of https://www.gitignore.io/api/xcode,swift,macos,objective-c
165 | LTXiOSUtils推送证书.p12
166 |
--------------------------------------------------------------------------------
/.swift-version:
--------------------------------------------------------------------------------
1 | 5.0
--------------------------------------------------------------------------------
/.swiftformat:
--------------------------------------------------------------------------------
1 | --exclude LTXiOSUtilsDemo/Pods
2 |
3 |
4 | --allman false
5 | --assetliterals visual-width
6 | --beforemarks
7 | --binarygrouping ignore
8 | --categorymark "MARK: %c"
9 | --classthreshold 0
10 | --closingparen balanced
11 | --commas always
12 | --conflictmarkers reject
13 | --decimalgrouping ignore
14 | --elseposition same-line
15 | --enumthreshold 0
16 | --exponentcase lowercase
17 | --exponentgrouping disabled
18 | --extensionacl on-declarations
19 | --extensionlength 0
20 | --extensionmark "MARK: - %t + %c"
21 | --fractiongrouping disabled
22 | --fragment false
23 | --funcattributes preserve
24 | --groupedextension "MARK: %c"
25 | --guardelse auto
26 | --header ignore
27 | --hexgrouping ignore
28 | --hexliteralcase uppercase
29 | --ifdef indent
30 | --importgrouping alpha
31 | --indent 4
32 | --indentcase false
33 | --lifecycle
34 | --linebreaks lf
35 | --markextensions always
36 | --marktypes always
37 | --maxwidth none
38 | --modifierorder
39 | --nevertrailing
40 | --nospaceoperators
41 | --nowrapoperators
42 | --octalgrouping ignore
43 | --operatorfunc spaced
44 | --organizetypes class,enum,struct
45 | --patternlet hoist
46 | --ranges spaced
47 | --redundanttype inferred
48 | --self remove
49 | --selfrequired
50 | --semicolons inline
51 | --shortoptionals always
52 | --smarttabs enabled
53 | --stripunusedargs closure-only
54 | --structthreshold 0
55 | --swiftversion 5.0
56 | --tabwidth unspecified
57 | --trailingclosures
58 | --trimwhitespace always
59 | --typeattributes preserve
60 | --typemark "MARK: - %t"
61 | --varattributes preserve
62 | --voidtype void
63 | --wraparguments preserve
64 | --wrapcollections preserve
65 | --wrapconditions preserve
66 | --wrapparameters preserve
67 | --wrapreturntype preserve
68 | --xcodeindentation disabled
69 | --yodaswap always
70 | --disable enumNamespaces,initCoderUnavailable,modifierOrder,strongOutlets,wrapMultilineStatementBraces
71 | --enable wrapEnumCases
72 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/xcshareddata/xcschemes/LTXiOSUtils.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
44 |
50 |
51 |
57 |
58 |
59 |
60 |
62 |
63 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/xcshareddata/xcschemes/LTXiOSUtilsCoreExtension.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
44 |
50 |
51 |
57 |
58 |
59 |
60 |
62 |
63 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/Documentation/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Coder-Star/LTXiOSUtils/8ede2e39e3d20ae396f3cadf8799665e085329e9/Documentation/.gitkeep
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 CoderStar
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/LTXiOSUtils.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = "LTXiOSUtils"
3 | s.version = "0.0.3"
4 | s.platform = :ios, "10.0" # iOS平台最低版本 10.0
5 | s.summary = "通用工具类以及组件的整合、封装以及使用介绍"
6 | s.homepage = "https://github.com/Coder-Star/LTXiOSUtils"
7 | s.license = { :type => "MIT", :file => "LICENSE" }
8 | s.author = { "CoderStar" => "1340529758@qq.com" }
9 |
10 | # s.source = { :git => "https://github.com/Coder-Star/LTXiOSUtils.git", :tag => s.version } # 发布时启用
11 | s.source = { :git => 'local', :tag => s.version} # 本地开发,local是随便起的名字
12 |
13 | s.requires_arc = true
14 | s.swift_version = ["5","4.2"]
15 | # s.static_framework = true
16 |
17 | s.pod_target_xcconfig = {
18 | 'DEFINES_MODULE' => 'YES'
19 | }
20 |
21 | s.subspec 'Extension' do |extension|
22 | extension.source_files = 'Sources/LTXiOSUtils/Classes/Extension/*.swift'
23 |
24 | extension.subspec 'Core' do |core|
25 | core.source_files = 'Sources/LTXiOSUtils/Classes/Extension/Core/*.swift'
26 | end
27 |
28 | extension.subspec 'SwiftStdlib' do |swift|
29 | swift.dependency "LTXiOSUtils/Extension/Core"
30 | swift.source_files = 'Sources/LTXiOSUtils/Classes/Extension/SwiftStdlib/*.swift'
31 | end
32 |
33 | extension.subspec 'Foundation' do |foundation|
34 | foundation.dependency "LTXiOSUtils/Extension/Core"
35 | foundation.source_files = 'Sources/LTXiOSUtils/Classes/Extension/Foundation/*.swift'
36 | end
37 |
38 |
39 | extension.subspec 'UIKit' do |uiKit|
40 | uiKit.dependency "LTXiOSUtils/Extension/Core"
41 | uiKit.source_files = 'Sources/LTXiOSUtils/Classes/Extension/UIKit/*.swift'
42 | end
43 |
44 | extension.subspec 'WebKit' do |webKit|
45 | webKit.dependency "LTXiOSUtils/Extension/Core"
46 | webKit.source_files = 'Sources/LTXiOSUtils/Classes/Extension/WebKit/*.swift'
47 | end
48 |
49 | extension.subspec 'Dispatch' do |dispatch|
50 | dispatch.dependency "LTXiOSUtils/Extension/Core"
51 | dispatch.source_files = 'Sources/LTXiOSUtils/Classes/Extension/Dispatch/*.swift'
52 | end
53 |
54 | end
55 |
56 | # 工具类
57 | s.subspec 'Util' do |util|
58 | util.source_files = 'Sources/LTXiOSUtils/Classes/Util/**/*'
59 | util.subspec 'Log' do |log|
60 | log.source_files = 'Sources/LTXiOSUtils/Classes/Util/Log/*.swift'
61 | end
62 | end
63 |
64 | # PropertyWrapper
65 | s.subspec 'PropertyWrapper' do |util|
66 | util.source_files = 'Sources/LTXiOSUtils/Classes/PropertyWrapper/**/*.swift'
67 | end
68 |
69 | # UI组件
70 | s.subspec 'Component' do |component|
71 | component.source_files = 'Sources/LTXiOSUtils/Classes/Component/**/*.swift'
72 |
73 | component.subspec 'Resources' do |resources|
74 | # LTXiOSUtilsComponent是bundle的名称
75 | resources.resource_bundle = { "LTXiOSUtilsComponent" => "Sources/LTXiOSUtils/Classes/Component/Resources/Resource/*" }
76 | end
77 | end
78 | end
79 |
80 |
81 | # fastlane release_pod project:"LTXiOSUtils" version:"s.version" desc:"tag desc"
82 | # fastlane release_pod project:"LTXiOSUtils" version:"s.version"
83 | # fastlane release_pod repo:"coder-star" project:"LTXiOSUtils" version:"s.version" desc:"tag desc"
84 |
--------------------------------------------------------------------------------
/LTXiOSUtilsDemo/.jazzy.yaml:
--------------------------------------------------------------------------------
1 | clean: true
2 | sdk: iphone
3 | author: CoderStar
4 | author_url: https://coder-star.github.io/
5 | github_url: https://github.com/Coder-Star/LTXiOSUtils
6 |
7 | module: LTXiOSUtils # 模块名称
8 | module_version: 1.0.0 # 文档版本号
9 |
10 | readme: ../README.md # readme路径
11 | output: ../docs # 输出路径
12 |
13 | #xcodebuild_arguments: [clean,build,-project,CSPickerView.xcodeproj,-scheme,CSPickerView]
14 | xcodebuild_arguments: [clean,build,-workspace,LTXiOSUtilsDemo.xcworkspace,-scheme,LTXiOSUtilsDemo]
15 |
16 | min_acl: public # 最小权限
17 |
18 | theme: Doc/jazzy-theme
19 |
20 |
21 |
--------------------------------------------------------------------------------
/LTXiOSUtilsDemo/Doc/jazzy-theme/assets/css/highlight.css.scss:
--------------------------------------------------------------------------------
1 | /* Credit to https://gist.github.com/wataru420/2048287 */
2 |
3 | .highlight {
4 | .c { color: #999988; font-style: italic } /* Comment */
5 | .err { color: #a61717; background-color: #e3d2d2 } /* Error */
6 | .k { color: #000000; font-weight: bold } /* Keyword */
7 | .o { color: #000000; font-weight: bold } /* Operator */
8 | .cm { color: #999988; font-style: italic } /* Comment.Multiline */
9 | .cp { color: #999999; font-weight: bold } /* Comment.Preproc */
10 | .c1 { color: #999988; font-style: italic } /* Comment.Single */
11 | .cs { color: #999999; font-weight: bold; font-style: italic } /* Comment.Special */
12 | .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
13 | .gd .x { color: #000000; background-color: #ffaaaa } /* Generic.Deleted.Specific */
14 | .ge { color: #000000; font-style: italic } /* Generic.Emph */
15 | .gr { color: #aa0000 } /* Generic.Error */
16 | .gh { color: #999999 } /* Generic.Heading */
17 | .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
18 | .gi .x { color: #000000; background-color: #aaffaa } /* Generic.Inserted.Specific */
19 | .go { color: #888888 } /* Generic.Output */
20 | .gp { color: #555555 } /* Generic.Prompt */
21 | .gs { font-weight: bold } /* Generic.Strong */
22 | .gu { color: #aaaaaa } /* Generic.Subheading */
23 | .gt { color: #aa0000 } /* Generic.Traceback */
24 | .kc { color: #000000; font-weight: bold } /* Keyword.Constant */
25 | .kd { color: #000000; font-weight: bold } /* Keyword.Declaration */
26 | .kp { color: #000000; font-weight: bold } /* Keyword.Pseudo */
27 | .kr { color: #000000; font-weight: bold } /* Keyword.Reserved */
28 | .kt { color: #445588; } /* Keyword.Type */
29 | .m { color: #009999 } /* Literal.Number */
30 | .s { color: #d14 } /* Literal.String */
31 | .na { color: #008080 } /* Name.Attribute */
32 | .nb { color: #0086B3 } /* Name.Builtin */
33 | .nc { color: #445588; font-weight: bold } /* Name.Class */
34 | .no { color: #008080 } /* Name.Constant */
35 | .ni { color: #800080 } /* Name.Entity */
36 | .ne { color: #990000; font-weight: bold } /* Name.Exception */
37 | .nf { color: #990000; } /* Name.Function */
38 | .nn { color: #555555 } /* Name.Namespace */
39 | .nt { color: #000080 } /* Name.Tag */
40 | .nv { color: #008080 } /* Name.Variable */
41 | .ow { color: #000000; font-weight: bold } /* Operator.Word */
42 | .w { color: #bbbbbb } /* Text.Whitespace */
43 | .mf { color: #009999 } /* Literal.Number.Float */
44 | .mh { color: #009999 } /* Literal.Number.Hex */
45 | .mi { color: #009999 } /* Literal.Number.Integer */
46 | .mo { color: #009999 } /* Literal.Number.Oct */
47 | .sb { color: #d14 } /* Literal.String.Backtick */
48 | .sc { color: #d14 } /* Literal.String.Char */
49 | .sd { color: #d14 } /* Literal.String.Doc */
50 | .s2 { color: #d14 } /* Literal.String.Double */
51 | .se { color: #d14 } /* Literal.String.Escape */
52 | .sh { color: #d14 } /* Literal.String.Heredoc */
53 | .si { color: #d14 } /* Literal.String.Interpol */
54 | .sx { color: #d14 } /* Literal.String.Other */
55 | .sr { color: #009926 } /* Literal.String.Regex */
56 | .s1 { color: #d14 } /* Literal.String.Single */
57 | .ss { color: #990073 } /* Literal.String.Symbol */
58 | .bp { color: #999999 } /* Name.Builtin.Pseudo */
59 | .vc { color: #008080 } /* Name.Variable.Class */
60 | .vg { color: #008080 } /* Name.Variable.Global */
61 | .vi { color: #008080 } /* Name.Variable.Instance */
62 | .il { color: #009999 } /* Literal.Number.Integer.Long */
63 | }
64 |
--------------------------------------------------------------------------------
/LTXiOSUtilsDemo/Doc/jazzy-theme/assets/img/carat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Coder-Star/LTXiOSUtils/8ede2e39e3d20ae396f3cadf8799665e085329e9/LTXiOSUtilsDemo/Doc/jazzy-theme/assets/img/carat.png
--------------------------------------------------------------------------------
/LTXiOSUtilsDemo/Doc/jazzy-theme/assets/img/dash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Coder-Star/LTXiOSUtils/8ede2e39e3d20ae396f3cadf8799665e085329e9/LTXiOSUtilsDemo/Doc/jazzy-theme/assets/img/dash.png
--------------------------------------------------------------------------------
/LTXiOSUtilsDemo/Doc/jazzy-theme/assets/img/gh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Coder-Star/LTXiOSUtils/8ede2e39e3d20ae396f3cadf8799665e085329e9/LTXiOSUtilsDemo/Doc/jazzy-theme/assets/img/gh.png
--------------------------------------------------------------------------------
/LTXiOSUtilsDemo/Doc/jazzy-theme/assets/js/jazzy.js:
--------------------------------------------------------------------------------
1 | window.jazzy = {'docset': false};
2 |
3 | if (typeof window.dash != 'undefined') {
4 | document.documentElement.className += ' dash';
5 | window.jazzy.docset = true;
6 | }
7 |
8 | if (navigator.userAgent.match(/xcode/i)) {
9 | document.documentElement.className += ' xcode';
10 | window.jazzy.docset = true;
11 | }
12 |
13 | if (!window.jazzy.docset) {
14 | $(function() {
15 | var filename = window.location.pathname.split('/').pop();
16 | $('a[href="Documentation.html"]').attr('href', 'index.html');
17 | $('.navigation a[href$="/' + filename + '"]').addClass('current-page');
18 | $('.navigation a[href="' + filename + '"]').addClass('current-page');
19 | });
20 | }
21 |
--------------------------------------------------------------------------------
/LTXiOSUtilsDemo/Doc/jazzy-theme/templates/doc.mustache:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{name}} {{kind}} Reference
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | {{#dash_type}}
14 |
15 | {{/dash_type}}
16 |
17 |
18 |
19 | {{> header}}
20 |
21 |
22 | {{module_name}} Reference
23 |
24 | {{name}} {{kind}} Reference
25 |
26 |
27 |
28 | {{> nav}}
29 |
30 |
31 |
32 |
33 | {{^hide_name}}
{{name}}
{{/hide_name}}
34 | {{#declaration}}
35 |
36 |
37 | {{{declaration}}}
38 |
39 |
40 | {{/declaration}}
41 | {{{overview}}}
42 |
43 |
44 |
45 | {{> tasks}}
46 |
47 |
48 |
49 | {{> footer}}
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/LTXiOSUtilsDemo/Doc/jazzy-theme/templates/footer.mustache:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/LTXiOSUtilsDemo/Doc/jazzy-theme/templates/header.mustache:
--------------------------------------------------------------------------------
1 |
27 |
--------------------------------------------------------------------------------
/LTXiOSUtilsDemo/Doc/jazzy-theme/templates/nav.mustache:
--------------------------------------------------------------------------------
1 |
17 |
--------------------------------------------------------------------------------
/LTXiOSUtilsDemo/Doc/jazzy-theme/templates/parameter.mustache:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{name}}
5 |
6 | |
7 |
8 |
9 | {{{discussion}}}
10 |
11 | |
12 |
13 |
--------------------------------------------------------------------------------
/LTXiOSUtilsDemo/Doc/jazzy-theme/templates/task.mustache:
--------------------------------------------------------------------------------
1 |
2 | {{#name}}
3 |
10 | {{/name}}
11 |
12 | {{#items}}
13 | -
14 |
15 |
16 |
17 |
18 | {{name}}
19 |
20 | {{#default_impl_abstract}}
21 |
22 | Default implementation
23 |
24 | {{/default_impl_abstract}}
25 | {{#from_protocol_extension}}
26 |
27 | Extension method
28 |
29 | {{/from_protocol_extension}}
30 |
31 |
32 |
33 |
34 |
35 | {{#abstract}}
36 |
37 | {{{abstract}}}
38 |
39 | {{/abstract}}
40 | {{#default_impl_abstract}}
41 | Default Implementation
42 |
43 | {{{default_impl_abstract}}}
44 |
45 | {{/default_impl_abstract}}
46 | {{#declaration}}
47 |
48 |
Declaration
49 |
50 |
{{language}}
51 | {{{declaration}}}
52 |
53 |
54 | {{/declaration}}
55 | {{#parameters.count}}
56 |
57 |
Parameters
58 |
59 |
60 | {{#parameters}}
61 | {{> parameter}}
62 | {{/parameters}}
63 |
64 |
65 |
66 | {{/parameters.count}}
67 | {{#return}}
68 |
69 |
Return Value
70 | {{{return}}}
71 |
72 | {{/return}}
73 |
74 | {{#abstract}}{{#url}}
75 |
See more
76 | {{/url}}{{/abstract}}
77 | {{#github_token_url}}
78 |
Show on GitHub
79 | {{/github_token_url}}
80 |
81 |
82 |
83 |
84 | {{/items}}
85 |
86 |
87 |
--------------------------------------------------------------------------------
/LTXiOSUtilsDemo/Doc/jazzy-theme/templates/tasks.mustache:
--------------------------------------------------------------------------------
1 | {{#tasks.count}}
2 |
3 |
4 | {{#tasks}}
5 | {{> task}}
6 | {{/tasks}}
7 |
8 |
9 | {{/tasks.count}}
10 |
--------------------------------------------------------------------------------
/LTXiOSUtilsDemo/Gemfile:
--------------------------------------------------------------------------------
1 | source "https://gems.ruby-china.com"
2 |
3 | gem "cocoapods", "1.11.2"
4 | gem "jazzy", "0.13.7"
--------------------------------------------------------------------------------
/LTXiOSUtilsDemo/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | remote: https://gems.ruby-china.com/
3 | specs:
4 | CFPropertyList (3.0.3)
5 | activesupport (5.2.5)
6 | concurrent-ruby (~> 1.0, >= 1.0.2)
7 | i18n (>= 0.7, < 2)
8 | minitest (~> 5.1)
9 | tzinfo (~> 1.1)
10 | addressable (2.8.0)
11 | public_suffix (>= 2.0.2, < 5.0)
12 | algoliasearch (1.27.5)
13 | httpclient (~> 2.8, >= 2.8.3)
14 | json (>= 1.5.1)
15 | atomos (0.1.3)
16 | claide (1.0.3)
17 | cocoapods (1.11.2)
18 | addressable (~> 2.8)
19 | claide (>= 1.0.2, < 2.0)
20 | cocoapods-core (= 1.11.2)
21 | cocoapods-deintegrate (>= 1.0.3, < 2.0)
22 | cocoapods-downloader (>= 1.4.0, < 2.0)
23 | cocoapods-plugins (>= 1.0.0, < 2.0)
24 | cocoapods-search (>= 1.0.0, < 2.0)
25 | cocoapods-trunk (>= 1.4.0, < 2.0)
26 | cocoapods-try (>= 1.1.0, < 2.0)
27 | colored2 (~> 3.1)
28 | escape (~> 0.0.4)
29 | fourflusher (>= 2.3.0, < 3.0)
30 | gh_inspector (~> 1.0)
31 | molinillo (~> 0.8.0)
32 | nap (~> 1.0)
33 | ruby-macho (>= 1.0, < 3.0)
34 | xcodeproj (>= 1.21.0, < 2.0)
35 | cocoapods-core (1.11.2)
36 | activesupport (>= 5.0, < 7)
37 | addressable (~> 2.8)
38 | algoliasearch (~> 1.0)
39 | concurrent-ruby (~> 1.1)
40 | fuzzy_match (~> 2.0.4)
41 | nap (~> 1.0)
42 | netrc (~> 0.11)
43 | public_suffix (~> 4.0)
44 | typhoeus (~> 1.0)
45 | cocoapods-deintegrate (1.0.4)
46 | cocoapods-downloader (1.4.0)
47 | cocoapods-plugins (1.0.0)
48 | nap
49 | cocoapods-search (1.0.0)
50 | cocoapods-trunk (1.5.0)
51 | nap (>= 0.8, < 2.0)
52 | netrc (~> 0.11)
53 | cocoapods-try (1.2.0)
54 | colored2 (3.1.2)
55 | concurrent-ruby (1.1.8)
56 | escape (0.0.4)
57 | ethon (0.12.0)
58 | ffi (>= 1.3.0)
59 | ffi (1.15.0)
60 | fourflusher (2.3.1)
61 | fuzzy_match (2.0.4)
62 | gh_inspector (1.1.3)
63 | httpclient (2.8.3)
64 | i18n (1.8.10)
65 | concurrent-ruby (~> 1.0)
66 | jazzy (0.13.7)
67 | cocoapods (~> 1.5)
68 | mustache (~> 1.1)
69 | open4
70 | redcarpet (~> 3.4)
71 | rouge (>= 2.0.6, < 4.0)
72 | sassc (~> 2.1)
73 | sqlite3 (~> 1.3)
74 | xcinvoke (~> 0.3.0)
75 | json (2.5.1)
76 | liferaft (0.0.6)
77 | minitest (5.11.3)
78 | molinillo (0.8.0)
79 | mustache (1.1.1)
80 | nanaimo (0.3.0)
81 | nap (1.1.0)
82 | netrc (0.11.0)
83 | open4 (1.3.4)
84 | public_suffix (4.0.6)
85 | redcarpet (3.5.1)
86 | rexml (3.2.5)
87 | rouge (2.0.7)
88 | ruby-macho (1.4.0)
89 | sassc (2.4.0)
90 | ffi (~> 1.9)
91 | sqlite3 (1.4.2)
92 | thread_safe (0.3.6)
93 | typhoeus (1.4.0)
94 | ethon (>= 0.9.0)
95 | tzinfo (1.2.9)
96 | thread_safe (~> 0.1)
97 | xcinvoke (0.3.0)
98 | liferaft (~> 0.0.6)
99 | xcodeproj (1.21.0)
100 | CFPropertyList (>= 2.3.3, < 4.0)
101 | atomos (~> 0.1.3)
102 | claide (>= 1.0.2, < 2.0)
103 | colored2 (~> 3.1)
104 | nanaimo (~> 0.3.0)
105 | rexml (~> 3.2.4)
106 |
107 | PLATFORMS
108 | x86_64-darwin-19
109 |
110 | DEPENDENCIES
111 | cocoapods (= 1.11.2)
112 | jazzy (= 0.13.7)
113 |
114 | BUNDLED WITH
115 | 2.2.21
116 |
--------------------------------------------------------------------------------
/LTXiOSUtilsDemo/LTXiOSUtilsDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/LTXiOSUtilsDemo/LTXiOSUtilsDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/LTXiOSUtilsDemo/LTXiOSUtilsDemo.xcodeproj/xcshareddata/xcschemes/LTXiOSUtilsDemo.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
45 |
51 |
52 |
53 |
54 |
60 |
62 |
68 |
69 |
70 |
71 |
73 |
74 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/LTXiOSUtilsDemo/LTXiOSUtilsDemo.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/LTXiOSUtilsDemo/LTXiOSUtilsDemo.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/LTXiOSUtilsDemo/LTXiOSUtilsDemo/Application/AppConfigService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppConfigApplicationService.swift
3 | // LTXiOSUtilsDemo
4 | //
5 | // Created by CoderStar on 2022/10/17.
6 | //
7 |
8 | import Foundation
9 | import LTXiOSUtils
10 |
11 | class AppConfigService: NSObject, ModuleService {
12 | required public override init() {}
13 |
14 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
15 | initConfig()
16 |
17 | let rootViewController = UINavigationController(rootViewController: ViewController())
18 | window?.rootViewController = rootViewController
19 |
20 | window?.makeKeyAndVisible()
21 |
22 | if let delegate = UIApplication.shared.managerDelegate {
23 | Log.d(delegate)
24 | }
25 | return true
26 | }
27 |
28 | private func initConfig() {
29 | Log.enabled = true
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/LTXiOSUtilsDemo/LTXiOSUtilsDemo/Application/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // LTXiOSUtilsDemo
4 | //
5 | // Created by CoderStar on 2021/8/9.
6 | //
7 |
8 | @_exported import LTXiOSUtils
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: ModuleAppDelegate {
13 | override var services: [ModuleService] {
14 | let filterClasses: [AnyClass] = [
15 | AppDelegate.self,
16 | ModuleAppDelegate.self,
17 | ]
18 |
19 | let allClasses = RuntimeUtils.allClasses
20 |
21 | let resultClasses: [ModuleService] = allClasses.compactMap { item in
22 | if !filterClasses.compactMap({ "\($0)" }).contains("\(item)"),
23 | let clazz = item as? ModuleService.Type {
24 | return clazz.init()
25 | } else {
26 | return nil
27 | }
28 | }
29 | return resultClasses
30 | }
31 |
32 | required init() {
33 | super.init()
34 | if window == nil {
35 | window = UIWindow(frame: UIScreen.main.bounds)
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/LTXiOSUtilsDemo/LTXiOSUtilsDemo/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/LTXiOSUtilsDemo/LTXiOSUtilsDemo/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "scale" : "2x",
6 | "size" : "20x20"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "scale" : "3x",
11 | "size" : "20x20"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "scale" : "2x",
16 | "size" : "29x29"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "scale" : "3x",
21 | "size" : "29x29"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "scale" : "2x",
26 | "size" : "40x40"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "scale" : "3x",
31 | "size" : "40x40"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "scale" : "2x",
36 | "size" : "60x60"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "scale" : "3x",
41 | "size" : "60x60"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "scale" : "1x",
46 | "size" : "20x20"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "scale" : "2x",
51 | "size" : "20x20"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "scale" : "1x",
56 | "size" : "29x29"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "scale" : "2x",
61 | "size" : "29x29"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "scale" : "1x",
66 | "size" : "40x40"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "scale" : "2x",
71 | "size" : "40x40"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "scale" : "1x",
76 | "size" : "76x76"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "scale" : "2x",
81 | "size" : "76x76"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "scale" : "2x",
86 | "size" : "83.5x83.5"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "scale" : "1x",
91 | "size" : "1024x1024"
92 | }
93 | ],
94 | "info" : {
95 | "author" : "xcode",
96 | "version" : 1
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/LTXiOSUtilsDemo/LTXiOSUtilsDemo/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/LTXiOSUtilsDemo/LTXiOSUtilsDemo/BaseViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BaseViewController.swift
3 | // LTXiOSUtilsDemo
4 | //
5 | // Created by CoderStar on 2022/3/18.
6 | //
7 |
8 | import Foundation
9 |
10 | class BaseViewController: UIViewController {
11 | override func viewDidLoad() {
12 | super.viewDidLoad()
13 | view.backgroundColor = .white
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/LTXiOSUtilsDemo/LTXiOSUtilsDemo/Demo/View/ClickableLabelDemoViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ClickableLabelDemoViewController.swift
3 | // LTXiOSUtilsDemo
4 | //
5 | // Created by CoderStar on 2022/8/13.
6 | //
7 |
8 | import Foundation
9 | import UIKit
10 |
11 | class ClickableLabelDemoViewController: BaseViewController {
12 | override func viewDidLoad() {
13 | super.viewDidLoad()
14 |
15 | let labelBackgroundView = LabelBackgroundView()
16 | labelBackgroundView.backgroundColor = .yellow
17 | view.addSubview(labelBackgroundView)
18 | labelBackgroundView.snp.makeConstraints { make in
19 | make.center.equalToSuperview()
20 | make.height.equalTo(50)
21 | make.width.equalTo(200)
22 | }
23 |
24 | let label = ClickableLabel()
25 |
26 | label.text = "111#你好#111高亮高亮高亮高亮高亮高亮高亮"
27 | label.clickTextColor = .red
28 | label.numberOfLines = 2
29 | label.clickBackgroundColor = .blue
30 | label.clickTextPatterns = ["#你好#"]
31 | label.delegate = self
32 | labelBackgroundView.addSubviews(label)
33 | label.snp.makeConstraints { make in
34 | make.center.equalToSuperview()
35 | make.width.equalTo(100)
36 | }
37 | }
38 | }
39 |
40 | extension ClickableLabelDemoViewController: ClickableLabelDelegate {
41 | func clickableLabel(label: ClickableLabel, clickText text: String) {
42 | Log.d(text)
43 | }
44 | }
45 |
46 | class LabelBackgroundView: UIView {
47 | override func touchesBegan(_ touches: Set, with event: UIEvent?) {
48 | Log.d("LabelBackgroundView touchesBegan")
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/LTXiOSUtilsDemo/LTXiOSUtilsDemo/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | $(MARKETING_VERSION)
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UIApplicationSupportsIndirectInputEvents
24 |
25 | UIBackgroundModes
26 |
27 | UILaunchStoryboardName
28 | LaunchScreen
29 | UIRequiredDeviceCapabilities
30 |
31 | armv7
32 |
33 | UISupportedInterfaceOrientations
34 |
35 | UIInterfaceOrientationPortrait
36 | UIInterfaceOrientationLandscapeLeft
37 | UIInterfaceOrientationLandscapeRight
38 |
39 | UISupportedInterfaceOrientations~ipad
40 |
41 | UIInterfaceOrientationPortrait
42 | UIInterfaceOrientationPortraitUpsideDown
43 | UIInterfaceOrientationLandscapeLeft
44 | UIInterfaceOrientationLandscapeRight
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/LTXiOSUtilsDemo/LTXiOSUtilsDemo/LibExport.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LibExport.swift
3 | // LTXiOSUtilsDemo
4 | //
5 | // Created by CoderStar on 2021/8/20.
6 | //
7 |
8 | import Foundation
9 |
10 | @_exported import LTXiOSUtils
11 |
--------------------------------------------------------------------------------
/LTXiOSUtilsDemo/LTXiOSUtilsDemo/Model/PropertyWrapper.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PropertyWrapper.swift
3 | // LTXiOSUtilsDemo
4 | //
5 | // Created by CoderStar on 2022/3/2.
6 | //
7 |
8 | import Foundation
9 |
10 | ///
11 | @propertyWrapper
12 | public struct CodableToString: Codable {
13 | public var wrappedValue: String?
14 |
15 | public init(from decoder: Decoder) throws {
16 | let container = try decoder.singleValueContainer()
17 | var string: String?
18 | do {
19 | string = try container.decode(String.self)
20 | } catch {
21 | do {
22 | string = String(try container.decode(Int.self))
23 | } catch {
24 | do {
25 | string = String(try container.decode(Double.self))
26 | } catch {
27 | // 如果不想要 String? 可以在此处给 string 赋值 = “”
28 | string = nil
29 | }
30 | }
31 | }
32 | wrappedValue = string
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/LTXiOSUtilsDemo/LTXiOSUtilsDemo/Resources/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
28 |
34 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/LTXiOSUtilsDemo/LTXiOSUtilsDemo/Resources/LaunchScreen.storyboardbak:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
28 |
34 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/LTXiOSUtilsDemo/LTXiOSUtilsDemo/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // LTXiOSUtilsDemo
4 | //
5 | // Created by CoderStar on 2021/8/9.
6 | //
7 |
8 | import LTXiOSUtils
9 | import SnapKit
10 | import UIKit
11 | import WebKit
12 |
13 | class ViewController: BaseViewController {
14 | private var demoList: [String: [[String: Any]]] = [
15 | "UI组件": [
16 | ["title": "ClickableLabel", "subTitle": "可点击Label", "vc": ClickableLabelDemoViewController.self],
17 | ],
18 | ]
19 |
20 | private lazy var tableView: UITableView = {
21 | let tableView = UITableView()
22 | tableView.tableFooterView = UIView()
23 | tableView.dataSource = self
24 | tableView.delegate = self
25 | return tableView
26 | }()
27 |
28 | override func viewDidLoad() {
29 | super.viewDidLoad()
30 | title = "功能列表"
31 |
32 | view.addSubview(tableView)
33 | tableView.frame = view.frame
34 | }
35 | }
36 |
37 | extension ViewController: UITableViewDataSource {
38 | func numberOfSections(in tableView: UITableView) -> Int {
39 | return demoList.count
40 | }
41 |
42 | func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
43 | return Array(demoList.keys)[section]
44 | }
45 |
46 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
47 | return Array(demoList.values)[section].count
48 | }
49 |
50 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
51 | let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "cell")
52 | cell.selectionStyle = .none
53 | cell.accessoryType = .disclosureIndicator
54 | cell.textLabel?.text = Array(demoList.values)[indexPath.section][indexPath.row]["title"] as? String
55 | cell.detailTextLabel?.text = Array(demoList.values)[indexPath.section][indexPath.row]["subTitle"] as? String
56 | return cell
57 | }
58 | }
59 |
60 | extension ViewController: UITableViewDelegate {
61 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
62 | guard let viewControllerType = Array(demoList.values)[indexPath.section][indexPath.row]["vc"] as? UIViewController.Type else {
63 | return
64 | }
65 | let viewController = viewControllerType.init()
66 | viewController.title = Array(demoList.values)[indexPath.section][indexPath.row]["title"] as? String
67 | navigationController?.pushViewController(viewController, animated: true)
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/LTXiOSUtilsDemo/Podfile:
--------------------------------------------------------------------------------
1 | install! 'cocoapods', :warn_for_unused_master_specs_repo => false
2 | platform :ios, '12.0'
3 |
4 | target 'LTXiOSUtilsDemo' do
5 |
6 | inhibit_all_warnings!
7 |
8 | pod 'SnapKit', '5.6.0'
9 | pod 'LTXiOSUtils', :path => '../'
10 |
11 | # 代码规范限制与自动纠正,嵌入到Build流程中
12 | pod 'SwiftFormat/CLI', '0.47.13', :configurations => ['Debug']
13 |
14 | end
15 |
16 | post_install do |installer|
17 | # 执行脚本
18 | system("chmod +x Shell/podInit.sh")
19 | system("sh Shell/podInit.sh")
20 |
21 | installer.pods_project.targets.each do |target|
22 | target.build_configurations.each do |config|
23 | if config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'].to_f < 12.0
24 | config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '12.0'
25 | end
26 |
27 | config.build_settings["EXCLUDED_ARCHS[sdk=iphonesimulator*]"] = "arm64"
28 |
29 | end
30 | end
31 | end
32 |
--------------------------------------------------------------------------------
/LTXiOSUtilsDemo/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - LTXiOSUtils (0.0.3):
3 | - LTXiOSUtils/Component (= 0.0.3)
4 | - LTXiOSUtils/Extension (= 0.0.3)
5 | - LTXiOSUtils/PropertyWrapper (= 0.0.3)
6 | - LTXiOSUtils/Util (= 0.0.3)
7 | - LTXiOSUtils/Component (0.0.3):
8 | - LTXiOSUtils/Component/Resources (= 0.0.3)
9 | - LTXiOSUtils/Component/Resources (0.0.3)
10 | - LTXiOSUtils/Extension (0.0.3):
11 | - LTXiOSUtils/Extension/Core (= 0.0.3)
12 | - LTXiOSUtils/Extension/Dispatch (= 0.0.3)
13 | - LTXiOSUtils/Extension/Foundation (= 0.0.3)
14 | - LTXiOSUtils/Extension/SwiftStdlib (= 0.0.3)
15 | - LTXiOSUtils/Extension/UIKit (= 0.0.3)
16 | - LTXiOSUtils/Extension/WebKit (= 0.0.3)
17 | - LTXiOSUtils/Extension/Core (0.0.3)
18 | - LTXiOSUtils/Extension/Dispatch (0.0.3):
19 | - LTXiOSUtils/Extension/Core
20 | - LTXiOSUtils/Extension/Foundation (0.0.3):
21 | - LTXiOSUtils/Extension/Core
22 | - LTXiOSUtils/Extension/SwiftStdlib (0.0.3):
23 | - LTXiOSUtils/Extension/Core
24 | - LTXiOSUtils/Extension/UIKit (0.0.3):
25 | - LTXiOSUtils/Extension/Core
26 | - LTXiOSUtils/Extension/WebKit (0.0.3):
27 | - LTXiOSUtils/Extension/Core
28 | - LTXiOSUtils/PropertyWrapper (0.0.3)
29 | - LTXiOSUtils/Util (0.0.3):
30 | - LTXiOSUtils/Util/Log (= 0.0.3)
31 | - LTXiOSUtils/Util/Log (0.0.3)
32 | - SnapKit (5.6.0)
33 | - SwiftFormat/CLI (0.47.13)
34 |
35 | DEPENDENCIES:
36 | - LTXiOSUtils (from `../`)
37 | - SnapKit (= 5.6.0)
38 | - SwiftFormat/CLI (= 0.47.13)
39 |
40 | SPEC REPOS:
41 | trunk:
42 | - SnapKit
43 | - SwiftFormat
44 |
45 | EXTERNAL SOURCES:
46 | LTXiOSUtils:
47 | :path: "../"
48 |
49 | SPEC CHECKSUMS:
50 | LTXiOSUtils: b2f2a0ab25a055b8327016c7f3f60e65e2e1c218
51 | SnapKit: e01d52ebb8ddbc333eefe2132acf85c8227d9c25
52 | SwiftFormat: 73573b89257437c550b03d934889725fbf8f75e5
53 |
54 | PODFILE CHECKSUM: 22b4fa9a0141ec7bf2eb5b85fa34371b43861711
55 |
56 | COCOAPODS: 1.16.2
57 |
--------------------------------------------------------------------------------
/LTXiOSUtilsDemo/Shell/podInit.sh:
--------------------------------------------------------------------------------
1 | # 是否是git repo
2 | RESULT=$(git rev-parse --is-inside-work-tree 2>&1)
3 | if [ "${RESULT}" = "true" ]; then
4 | git config core.hooksPath .git-hooks
5 | fi
6 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.3
2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
3 | import PackageDescription
4 |
5 | let package = Package(
6 | name: "LTXiOSUtils",
7 | platforms: [
8 | .macOS(.v10_12),
9 | .iOS(.v10),
10 | ],
11 | products: [
12 | .library(
13 | name: "LTXiOSUtils",
14 | targets: [
15 | "LTXiOSUtilsExtension",
16 | "LTXiOSUtilsUtil",
17 | "LTXiOSUtilsPropertyWrapper",
18 | ]
19 | ),
20 | .library(name: "LTXiOSUtilsExtension", type: .static, targets: ["LTXiOSUtilsExtension"]),
21 | .library(name: "LTXiOSUtilsUtil", type: .static, targets: ["LTXiOSUtilsUtil"]),
22 | .library(name: "LTXiOSUtilsPropertyWrapper", type: .static, targets: ["LTXiOSUtilsPropertyWrapper"]),
23 | ],
24 | dependencies: [
25 | ],
26 | targets: [
27 | .target(
28 | name: "LTXiOSUtilsExtension",
29 | path: "Sources/LTXiOSUtils/Classes/Extension"
30 | ),
31 | .target(
32 | name: "LTXiOSUtilsUtil",
33 | path: "Sources/LTXiOSUtils/Classes/Util"
34 | ),
35 | .target(
36 | name: "LTXiOSUtilsPropertyWrapper",
37 | path: "Sources/LTXiOSUtils/Classes/PropertyWrapper"
38 | ),
39 | ]
40 | )
41 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # LTXiOSUtil 介绍
2 |
3 | 
4 | 
5 | 
6 | 
7 | 
8 | [](https://coder-star.github.io/LTXiOSUtils/)
9 |
10 | ## 简介
11 |
12 | 该项目其实不是解决某一个问题而诞生,而是将一些功能、架构设计融入进去,不断的优化改进,算是一个开发的加速库,并且遵循严格的代码规范,注释完备。
13 |
14 | ## Core层
15 |
16 | ### 扩展
17 |
18 | 绝大多数扩展使用了`tx`命名空间,部分扩展因为语法限制或者使用考虑没有使用前缀。
19 |
20 | 列举一些未使用命名空间的情况:
21 |
22 | - 便利构造函数;
23 | - 扩展中使用了关联对象;
24 | - 使用了@IBInspectable等
25 |
26 | ### 核心工具类
27 |
28 | 目前主要包含日志输出以及语法糖两个工具类。
29 |
30 | ### 工具类
31 |
32 | - 日志工具
33 | - 网络请求
34 | - UserDafult 协议
35 |
36 | ## 关于代码规范
37 |
38 | 目前代码规范使用的是`SwiftFormat`工具,然后在Build过程中会调用脚本自动format,同时在git提交前也会通过`githook`使用`SwiftFormat`的lint模式校对代码的规范性(防止未build直接commit),注意这个阶段不会自动format。
39 |
40 | ## Features
41 |
42 | - 写demo
43 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Component/Class/BadgeView/BadgeControl.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BadgeControl.swift
3 | // NCAppUIiOS
4 | //
5 | // Created by CoderStar on 2020/1/12.
6 | //
7 |
8 | import UIKit
9 |
10 | /// 角标伸缩模式
11 | public enum BadgeViewFlexMode {
12 | /// 左伸缩 : <==●
13 | case head
14 | /// 右伸缩 : ●==>
15 | case tail
16 | /// 左右伸缩 : <=●=>
17 | case middle
18 | }
19 |
20 | public class BadgeControl: UIControl {
21 | /// 记录Badge的偏移量
22 | public var offset = CGPoint(x: 0, y: 0)
23 |
24 | /// Badge伸缩的方向
25 | public var flexMode: BadgeViewFlexMode = .tail
26 |
27 | /// 左右内边距
28 | public var sidePadding: (leading: CGFloat, trailing: CGFloat) = (5, -5) {
29 | didSet {
30 | addLayout(with: textLabel, leading: sidePadding.leading, trailing: sidePadding.trailing)
31 | }
32 | }
33 |
34 | /// 文字
35 | public var text: String? {
36 | didSet {
37 | textLabel.text = text
38 | }
39 | }
40 |
41 | /// 字体大小
42 | public var font: UIFont? {
43 | didSet {
44 | textLabel.font = font
45 | }
46 | }
47 |
48 | /// 富文本
49 | public var attributedText: NSAttributedString? {
50 | didSet {
51 | textLabel.attributedText = attributedText
52 | }
53 | }
54 |
55 | /// 设置背景图片,优先级比 backgroundColor 高
56 | /// 当设置为nil时,会显示成之前设置的背景色
57 | public var backgroundImage: UIImage? {
58 | didSet {
59 | imageView.image = backgroundImage
60 | if backgroundImage != nil {
61 | if let constraint = heightConstraint() {
62 | badgeViewHeightConstraint = constraint
63 | removeConstraint(constraint)
64 | }
65 | backgroundColor = UIColor.clear
66 | } else {
67 | if heightConstraint() == nil, let constraint = badgeViewHeightConstraint {
68 | addConstraint(constraint)
69 | }
70 | backgroundColor = badgeViewColor
71 | }
72 | }
73 | }
74 |
75 | private lazy var textLabel: UILabel = {
76 | let textLabel = UILabel()
77 | textLabel.textColor = UIColor.white
78 | textLabel.font = UIFont.systemFont(ofSize: 10)
79 | textLabel.textAlignment = .center
80 | return textLabel
81 | }()
82 |
83 | private lazy var imageView = UIImageView()
84 |
85 | /// 记录之前设置的背景色
86 | private var badgeViewColor: UIColor?
87 |
88 | /// 记录之前的宽度约束
89 | private var badgeViewHeightConstraint: NSLayoutConstraint?
90 |
91 | required override public init(frame: CGRect) {
92 | super.init(frame: frame)
93 | setupSubviews()
94 | }
95 |
96 | required public init?(coder aDecoder: NSCoder) {
97 | super.init(coder: aDecoder)
98 | setupSubviews()
99 | }
100 |
101 | open override var backgroundColor: UIColor? {
102 | didSet {
103 | super.backgroundColor = backgroundColor
104 | badgeViewColor = backgroundColor
105 | }
106 | }
107 | }
108 |
109 | extension BadgeControl {
110 | private func setupSubviews() {
111 | layer.masksToBounds = true
112 | backgroundColor = .red
113 | translatesAutoresizingMaskIntoConstraints = false
114 |
115 | addSubview(textLabel)
116 | addSubview(imageView)
117 |
118 | addLayout(with: imageView, leading: 0, trailing: 0)
119 | addLayout(with: textLabel, leading: sidePadding.leading, trailing: sidePadding.trailing)
120 | }
121 |
122 | private func addLayout(with view: UIView, leading: CGFloat, trailing: CGFloat) {
123 | view.removeConstraints(view.constraints)
124 |
125 | view.translatesAutoresizingMaskIntoConstraints = false
126 | let topConstraint = NSLayoutConstraint(item: view, attribute: .top, relatedBy: .equal, toItem: self, attribute: .top, multiplier: 1.0, constant: 0)
127 | let leadingConstraint = NSLayoutConstraint(item: view, attribute: .leading, relatedBy: .equal, toItem: self, attribute: .leading, multiplier: 1.0, constant: leading)
128 | let bottomConstraint = NSLayoutConstraint(item: view, attribute: .bottom, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1.0, constant: 0)
129 | let trailingConstraint = NSLayoutConstraint(item: view, attribute: .trailing, relatedBy: .equal, toItem: self, attribute: .trailing, multiplier: 1.0, constant: trailing)
130 | leadingConstraint.priority = UILayoutPriority(rawValue: 999)
131 | trailingConstraint.priority = UILayoutPriority(rawValue: 999)
132 | addConstraints([topConstraint, leadingConstraint, bottomConstraint, trailingConstraint])
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Component/Class/BadgeView/NCSizeConfig+Badge.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NCSizeConfig+Badge.swift
3 | // NCAppUIiOS
4 | //
5 | // Created by CoderStar on 2022/10/12.
6 | //
7 |
8 | import Foundation
9 |
10 | /// 徽标相关尺寸
11 | public struct BadgeSizeConfig {
12 | /// 徽标默认高度
13 | public static var defaultBadgeHeight: CGFloat = 16
14 |
15 | /// 点默认尺寸
16 | public static var defaultDotSize: CGFloat = 8
17 | }
18 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Component/Class/BadgeView/UIBarButtonItem+BadgeView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIBarButtonItem+BadgeView.swift
3 | // NCAppUIiOS
4 | //
5 | // Created by CoderStar on 2020/1/12.
6 | //
7 |
8 | import UIKit
9 |
10 | extension TxExtensionWrapper where Base: UIBarButtonItem {
11 | /// 通过Xcode视图调试工具找到UIBarButtonItem的Badge所在父视图为:UIImageView
12 | public var badgeContainerView: UIView {
13 | let navigationButton = (base.value(forKey: "_view") as? UIView) ?? UIView()
14 | let systemVersion = (UIDevice.current.systemVersion as NSString).doubleValue
15 | let controlName = systemVersion < 11.0 ? "UIImageView" : "UIButton"
16 | for subView in navigationButton.subviews {
17 | if subView.isKind(of: NSClassFromString(controlName)!) {
18 | subView.layer.masksToBounds = false
19 | return subView
20 | }
21 | }
22 | return navigationButton
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Component/Class/BadgeView/UITabBarItem+BadgeView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UITabBarItem+BadgeView.swift
3 | // NCAppUIiOS
4 | //
5 | // Created by CoderStar on 2020/1/12.
6 | //
7 |
8 | import UIKit
9 |
10 | extension TxExtensionWrapper where Base: UITabBarItem {
11 | /// 通过Xcode视图调试工具找到UITabBarItem原生Badge所在父视图
12 | public var containerView: UIView {
13 | let tabBarButton = (base.value(forKey: "_view") as? UIView) ?? UIView()
14 | for subView in tabBarButton.subviews {
15 | guard let superclass = subView.superclass else { return tabBarButton }
16 | if superclass == NSClassFromString("UIImageView") {
17 | return subView
18 | }
19 | }
20 | return tabBarButton
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Component/Class/CSPaddingLabel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CSPaddingLabel.swift
3 | // LTXiOSUtils
4 | //
5 | // Created by CoderStar on 2021/6/9.
6 | //
7 |
8 | import Foundation
9 | import UIKit
10 |
11 | /**
12 | 使用时可不使用高度和宽度约束,如果想使用,高度及宽度应设置文字宽高度+padding值,
13 | 如文字宽度是20,leftInset是5,rightInset是5,则宽度约束应设置为20 + 5 + 5
14 | */
15 |
16 | @IBDesignable
17 | open class CSPaddingLabel: UILabel {
18 | @IBInspectable
19 | open var topInset: CGFloat = 0.0
20 |
21 | @IBInspectable
22 | open var bottomInset: CGFloat = 0.0
23 |
24 | @IBInspectable
25 | open var leftInset: CGFloat = 0.0
26 |
27 | @IBInspectable
28 | open var rightInset: CGFloat = 0.0
29 |
30 | @IBInspectable
31 | open var edgeInsets: UIEdgeInsets {
32 | set {
33 | topInset = newValue.top
34 | bottomInset = newValue.bottom
35 | leftInset = newValue.left
36 | rightInset = newValue.right
37 | }
38 | get {
39 | return UIEdgeInsets(top: topInset, left: leftInset, bottom: bottomInset, right: rightInset)
40 | }
41 | }
42 |
43 | override open func textRect(forBounds bounds: CGRect, limitedToNumberOfLines numberOfLines: Int) -> CGRect {
44 | let insetRect = bounds.inset(by: edgeInsets)
45 | let textRect = super.textRect(forBounds: insetRect, limitedToNumberOfLines: numberOfLines)
46 | let invertedInsets = UIEdgeInsets(
47 | top: -edgeInsets.top,
48 | left: -edgeInsets.left,
49 | bottom: -edgeInsets.bottom,
50 | right: -edgeInsets.right
51 | )
52 | return textRect.inset(by: invertedInsets)
53 | }
54 |
55 | override open func drawText(in rect: CGRect) {
56 | super.drawText(in: rect.inset(by: edgeInsets))
57 | }
58 |
59 | override open var intrinsicContentSize: CGSize {
60 | let size = super.intrinsicContentSize
61 | return CGSize(width: size.width + leftInset + rightInset, height: size.height + topInset + bottomInset)
62 | }
63 |
64 | override open func sizeThatFits(_ size: CGSize) -> CGSize {
65 | let size = super.sizeThatFits(size)
66 | return CGSize(width: size.width + leftInset + rightInset, height: size.height + topInset + bottomInset)
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Component/Class/CommonShowTextView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CommonShowTextView.swift
3 | // LTXiOSUtils
4 | // 通用Label,支持复制功能,支持电话、邮箱、网络url等进行跳转
5 | // Created by CoderStar on 2020/1/19.
6 | //
7 |
8 | import Foundation
9 |
10 | /// CommonLabel支持的可点击跳转的类型
11 | public enum CommonShowTextViewUrlType {
12 | /// 电话
13 | case mobile
14 | /// 邮箱
15 | case email
16 | /// 网络url
17 | case networkUrl
18 | }
19 |
20 | public class CommonShowTextView: UITextView {
21 | /// 检查类型
22 | public var checkType: [CommonShowTextViewUrlType: UIColor] = [
23 | CommonShowTextViewUrlType.mobile: UIColor.blue,
24 | CommonShowTextViewUrlType.email: UIColor.blue,
25 | CommonShowTextViewUrlType.networkUrl: UIColor.blue,
26 | ] {
27 | didSet {
28 | setEvent()
29 | }
30 | }
31 |
32 | override public var attributedText: NSAttributedString? {
33 | didSet {
34 | setEvent()
35 | }
36 | }
37 |
38 | override public var text: String? {
39 | didSet {
40 | setEvent()
41 | }
42 | }
43 |
44 | override public init(frame: CGRect, textContainer: NSTextContainer?) {
45 | super.init(frame: frame, textContainer: textContainer)
46 | setupView()
47 | }
48 |
49 | required init?(coder: NSCoder) {
50 | super.init(coder: coder)
51 | setupView()
52 | }
53 |
54 | private func setupView() {
55 | textContainerInset = .zero
56 | textContainer.lineFragmentPadding = 0
57 | isScrollEnabled = false
58 | isEditable = false
59 | isSelectable = true
60 | font = UIFont.systemFont(ofSize: 17)
61 | textContainer.lineBreakMode = .byCharWrapping
62 | }
63 |
64 | deinit {
65 | NotificationCenter.default.removeObserver(self)
66 | }
67 | }
68 |
69 | extension CommonShowTextView {
70 | @objc
71 | private func setEvent() {
72 | guard let str = text else {
73 | return
74 | }
75 | let typeList = Array(checkType.keys)
76 |
77 | if str.tx.isMobile, typeList.contains(.mobile) {
78 | textColor = checkType[.mobile]
79 | setTapGesture(type: .mobile, str: str)
80 | } else if str.tx.isEmail, typeList.contains(.email) {
81 | textColor = checkType[.email]
82 | setTapGesture(type: .email, str: str)
83 | } else if str.tx.isNetworkUrl, typeList.contains(.networkUrl) {
84 | textColor = checkType[.networkUrl]
85 | setTapGesture(type: .networkUrl, str: str)
86 | } else {
87 | textColor = nil
88 | addTapGesture { _ in
89 | }
90 | }
91 | }
92 |
93 | private func setTapGesture(type: CommonShowTextViewUrlType, str: String) {
94 | /// 使点击手势只在点击文字时生效
95 | addTapGesture { tap in
96 | let size = self.sizeThatFits(.zero)
97 | let tapPoint = tap.location(in: self)
98 | if tapPoint.x > size.width || tapPoint.y > size.height {
99 | return
100 | }
101 | self.click(type: type, str: str)
102 | }
103 | }
104 |
105 | private func click(type: CommonShowTextViewUrlType, str: String) {
106 | var result = ""
107 | switch type {
108 | case .mobile:
109 | result = "tel://\(str)"
110 | case .email:
111 | result = "mailto:\(str)"
112 | case .networkUrl:
113 | result = str
114 | }
115 | if let url = URL(string: result), UIApplication.shared.canOpenURL(url) {
116 | UIApplication.shared.open(url, options: [:], completionHandler: nil)
117 | }
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Component/Class/FoldTableView/FoldAbstractions.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /// cell折叠状态
4 | public enum FoldState: Int {
5 | /// 即将展开
6 | case willExpand
7 | /// 即将折叠
8 | case willCollapse
9 | /// 展开完毕
10 | case didExpand
11 | /// 折叠完毕
12 | case didCollapse
13 | }
14 |
15 | /// 动作状态
16 | public enum FoldActionType {
17 | /// 展开
18 | case expand
19 | /// 折叠
20 | case collapse
21 | }
22 |
23 | /// 折叠Cell需要继承该协议
24 | public protocol FoldTableViewHeaderCell: AnyObject {
25 | ///
26 | /// - Parameters:
27 | /// - state: 折叠状态
28 | /// - cellReuse: 是否因为复用导致的折叠状态变化
29 | func changeState(_ state: FoldState, cellReuseStatus cellReuse: Bool)
30 | }
31 |
32 | public protocol FoldTableViewDataSource: UITableViewDataSource {
33 | func tableView(_ tableView: FoldTableView, canExpandSection section: Int) -> Bool
34 | func tableView(_ tableView: FoldTableView, expandableCellForSection section: Int) -> UITableViewCell
35 | }
36 |
37 | extension FoldTableViewDataSource {
38 | /// 是否允许展开协议默认实现,默认允许展开
39 | public func tableView(_ tableView: FoldTableView, canExpandSection section: Int) -> Bool {
40 | return true
41 | }
42 | }
43 |
44 | public protocol FoldTableViewDelegate: UITableViewDelegate {
45 | func tableView(_ tableView: FoldTableView, FoldState state: FoldState, changeForSection section: Int)
46 | }
47 |
48 | extension FoldTableViewDelegate {
49 | /// cell折叠状态改变默认实现
50 | public func tableView(_ tableView: FoldTableView, FoldState state: FoldState, changeForSection section: Int) {}
51 | }
52 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Component/Class/GridMenuView/DefaultGridMenuCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DefaultGridMenuCell.swift
3 | // LTXiOSUtils
4 | //
5 | // Created by CoderStar on 2020/1/12.
6 | //
7 |
8 | import Foundation
9 |
10 | /// 角标显示样式
11 | public enum CornerMarkType {
12 | /// 无
13 | case none
14 | /// 数字
15 | case number(number: Int)
16 | /// 红点
17 | case point(isShow: Bool)
18 | /// 文字
19 | case text(text: String)
20 | }
21 |
22 | open class DefaultGridMenuCell: UICollectionViewCell {
23 | /// 角标显示数字最大值,如果再比这个大,就显示99+的形式,为nil值不限制
24 | public static var maxNumber: Int? = 99
25 | /// 标题颜色
26 | public static var labelColor = UIColor.black.tx.adapt()
27 | /// 标题字体
28 | public static var labelFont = UIFont.systemFont(ofSize: 14)
29 |
30 | public var imageView = UIImageView()
31 |
32 | public var text = "" {
33 | didSet {
34 | label.text = text
35 | }
36 | }
37 |
38 | public var font: UIFont = DefaultGridMenuCell.labelFont {
39 | didSet {
40 | label.font = font
41 | }
42 | }
43 |
44 | public var textColor: UIColor = DefaultGridMenuCell.labelColor {
45 | didSet {
46 | label.textColor = textColor
47 | }
48 | }
49 |
50 | public var markType: CornerMarkType = .number(number: 0) {
51 | didSet {
52 | setBadge()
53 | }
54 | }
55 |
56 | private lazy var label: UILabel = {
57 | let label = UILabel()
58 | label.adjustsFontSizeToFitWidth = true
59 | label.textAlignment = .center
60 | label.font = font
61 | label.textColor = textColor
62 | return label
63 | }()
64 |
65 | override init(frame: CGRect) {
66 | super.init(frame: frame)
67 | let labelHeight: CGFloat = 20
68 | let imageHeight = frame.height - labelHeight - 20
69 | let imageWidth = imageHeight
70 | imageView.frame = CGRect(x: (frame.width - imageWidth) / 2, y: 10, width: imageWidth, height: imageHeight)
71 | imageView.layer.cornerRadius = 10
72 | imageView.layer.masksToBounds = true
73 | addSubview(imageView)
74 | label.frame = CGRect(x: 5, y: imageHeight + 15, width: frame.width - 10, height: labelHeight)
75 | addSubview(label)
76 | tx.set(flexMode: .middle)
77 | tx.move(x: -1 * imageView.frame.origin.x, y: imageView.frame.origin.y)
78 | }
79 |
80 | private func setBadge() {
81 | switch markType {
82 | case .none:
83 | tx.set(type: .none)
84 | case let .number(number):
85 | if let maxNumber = DefaultGridMenuCell.maxNumber, number > maxNumber {
86 | tx.set(height: 18)
87 | tx.set(type: .text(value: "\(maxNumber)+"))
88 | } else if number <= 0 {
89 | tx.set(type: .none)
90 | } else {
91 | tx.set(height: 18)
92 | tx.set(type: .number(value: number))
93 | }
94 | case let .point(isShow):
95 | if isShow {
96 | tx.set(type: .dot)
97 | tx.set(height: 12)
98 | } else {
99 | tx.set(type: .none)
100 | }
101 | case let .text(text):
102 | tx.set(height: 18)
103 | tx.set(type: .text(value: text))
104 | }
105 | }
106 |
107 | @available(*, unavailable)
108 | required public init?(coder aDecoder: NSCoder) {
109 | fatalError("init(coder:) has not been implemented")
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Component/Class/GridMenuView/GridMenuItem.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GridMenuModel.swift
3 | // LTXiOSUtils
4 | // 格式菜单子项实体
5 | // Created by CoderStar on 2020/1/11.
6 | //
7 |
8 | import Foundation
9 |
10 | /// 格子菜单实体
11 | public struct GridMenuItem {
12 | /// 编码
13 | public var code: String
14 | /// 标题
15 | public var title: String
16 | /// 图片
17 | public var image: UIImage?
18 | /// 角标类型,默认为无
19 | public var markType: CornerMarkType = .none
20 |
21 | /// 构造函数
22 | /// - Parameters:
23 | /// - code: 编码
24 | /// - title: 标题
25 | public init(code: String, title: String) {
26 | self.code = code
27 | self.title = title
28 | }
29 |
30 | /// 构造函数
31 | /// - Parameters:
32 | /// - code: 编码
33 | /// - title: 标题
34 | /// - image: 图标
35 | public init(code: String, title: String, image: UIImage?) {
36 | self.code = code
37 | self.title = title
38 | self.image = image
39 | }
40 |
41 | /// 构造函数
42 | /// - Parameters:
43 | /// - code: 编码
44 | /// - title: 标题
45 | /// - image: 图标
46 | public init(code: String, title: String, image: UIImage?, markType: CornerMarkType) {
47 | self.code = code
48 | self.title = title
49 | self.image = image
50 | self.markType = markType
51 | }
52 |
53 | /// /// 构造函数
54 | /// - Parameters:
55 | /// - code: 编码
56 | /// - title: 标题
57 | /// - markType: 角标类型
58 | public init(code: String, title: String, markType: CornerMarkType) {
59 | self.code = code
60 | self.title = title
61 | self.markType = markType
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Component/Class/GridMenuView/ScrollPageControlView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ScrollPageControlView.swift
3 | // LTXiOSUtils
4 | // 横向滚动分页控件
5 | // Created by CoderStar on 2020/1/15.
6 | //
7 |
8 | import Foundation
9 | import UIKit
10 |
11 | open class ScrollPageControlView: UIView {
12 | /// 偏移量
13 | public var progrss: CGFloat = 0 {
14 | didSet {
15 | var frame = currentIndicatorView.frame
16 | frame.origin.x = progrss
17 | currentIndicatorView.frame = frame
18 | }
19 | }
20 |
21 | /// 滑块宽度
22 | public var currentIndicatorWidth: CGFloat = 20 {
23 | didSet {
24 | var frame = currentIndicatorView.frame
25 | frame.size.width = currentIndicatorWidth
26 | currentIndicatorView.frame = frame
27 | }
28 | }
29 |
30 | /// 滑块颜色
31 | public var currentIndicatorColor = UIColor(hexString: "#4296d5") {
32 | didSet {
33 | currentIndicatorView.backgroundColor = currentIndicatorColor
34 | }
35 | }
36 |
37 | /// 计算偏移量
38 | public var offsetWidth: CGFloat {
39 | return frame.width - currentIndicatorWidth
40 | }
41 |
42 | private lazy var currentIndicatorView: UIView = {
43 | let currentIndicatorView = UIView()
44 | return currentIndicatorView
45 | }()
46 |
47 | override public init(frame: CGRect) {
48 | super.init(frame: frame)
49 | setupView()
50 | }
51 |
52 | required public init?(coder: NSCoder) {
53 | super.init(coder: coder)
54 | setupView()
55 | }
56 |
57 | private func setupView() {
58 | addSubview(currentIndicatorView)
59 | backgroundColor = UIColor(hexString: "#eeeeee")
60 | }
61 |
62 | override open func layoutSubviews() {
63 | layer.masksToBounds = true
64 | layer.cornerRadius = frame.height / 2
65 | currentIndicatorView.frame = CGRect(x: 0, y: 0, width: currentIndicatorWidth, height: frame.height)
66 | currentIndicatorView.backgroundColor = currentIndicatorColor
67 | currentIndicatorView.layer.masksToBounds = true
68 | currentIndicatorView.layer.cornerRadius = currentIndicatorView.frame.height / 2
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Component/Class/GrowingTextView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GrowingTextView.swift
3 | // LTXiOSUtils
4 | // 高度自适应textView
5 | // Created by CoderStar on 2020/3/14.
6 | //
7 |
8 | import Foundation
9 | import UIKit
10 |
11 | /// GrowingTextViewDelegate
12 | public protocol GrowingTextViewDelegate: AnyObject {
13 | /// 高度变化
14 | /// - Parameters:
15 | /// - growingTextView: GrowingTextView
16 | /// - height: 变化后高度
17 | func heightChange(growingTextView: GrowingTextView, height: CGFloat)
18 | }
19 |
20 | open class GrowingTextView: UITextView {
21 | override open var text: String! {
22 | didSet { setNeedsDisplay() }
23 | }
24 |
25 | override open var attributedText: NSAttributedString! {
26 | didSet { setNeedsDisplay() }
27 | }
28 |
29 | /// 最小高度
30 | /// 设置为0表示没有限制
31 | /// 如果想设置高度,请使用这个属性,如果使用布局约束设定高度,则需要实现代理手动更新约束高度
32 | open var minHeight: CGFloat = 0 {
33 | didSet { forceLayoutSubviews() }
34 | }
35 |
36 | /// 最大高度
37 | /// 设置为0表示没有限制
38 | open var maxHeight: CGFloat = 0 {
39 | didSet { forceLayoutSubviews() }
40 | }
41 |
42 | /// 高度变化闭包
43 | open var heightChangeCallBack: ((_ height: CGFloat) -> Void)?
44 | /// 代理
45 | open weak var growingTextViewDelegate: GrowingTextViewDelegate?
46 |
47 | private var heightConstraint: NSLayoutConstraint?
48 | private var oldText: String = ""
49 | private var oldSize: CGSize = .zero
50 | private var shouldScrollAfterHeightChanged = false
51 |
52 | override public init(frame: CGRect, textContainer: NSTextContainer?) {
53 | super.init(frame: frame, textContainer: textContainer)
54 | commonInit()
55 | }
56 |
57 | required public init?(coder aDecoder: NSCoder) {
58 | super.init(coder: aDecoder)
59 | commonInit()
60 | }
61 |
62 | private func commonInit() {
63 | contentMode = .redraw
64 | associateConstraints()
65 | NotificationCenter.default.addObserver(self, selector: #selector(textDidChange), name: UITextView.textDidChangeNotification, object: self)
66 | NotificationCenter.default.addObserver(self, selector: #selector(textDidEndEditing), name: UITextView.textDidEndEditingNotification, object: self)
67 | }
68 |
69 | deinit {
70 | NotificationCenter.default.removeObserver(self)
71 | }
72 |
73 | open override var intrinsicContentSize: CGSize {
74 | return CGSize(width: UIView.noIntrinsicMetric, height: 30)
75 | }
76 |
77 | private func associateConstraints() {
78 | for constraint in constraints where constraint.firstAttribute == .height && constraint.relation == .equal {
79 | heightConstraint = constraint
80 | }
81 | }
82 |
83 | private func forceLayoutSubviews() {
84 | oldSize = .zero
85 | setNeedsLayout()
86 | layoutIfNeeded()
87 | }
88 |
89 | override open func layoutSubviews() {
90 | super.layoutSubviews()
91 |
92 | if text == oldText, bounds.size == oldSize { return }
93 | oldText = text
94 | oldSize = bounds.size
95 |
96 | let size = sizeThatFits(CGSize(width: bounds.size.width, height: CGFloat.greatestFiniteMagnitude))
97 | var height = size.height
98 |
99 | height = minHeight > 0 ? max(height, minHeight) : height
100 | height = maxHeight > 0 ? min(height, maxHeight) : height
101 |
102 | if heightConstraint == nil {
103 | heightConstraint = NSLayoutConstraint(item: self, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: height)
104 | addConstraint(heightConstraint!)
105 | }
106 |
107 | if height != heightConstraint!.constant {
108 | shouldScrollAfterHeightChanged = true
109 | heightConstraint!.constant = height
110 | heightChangeCallBack?(height)
111 | growingTextViewDelegate?.heightChange(growingTextView: self, height: height)
112 | setNeedsDisplay()
113 | superview?.layoutIfNeeded()
114 | } else if shouldScrollAfterHeightChanged {
115 | shouldScrollAfterHeightChanged = false
116 | scrollToCorrectPosition()
117 | }
118 | }
119 |
120 | private func scrollToCorrectPosition() {
121 | if isFirstResponder {
122 | /// 滚动到底部
123 | scrollRangeToVisible(NSRange(location: -1, length: 0))
124 | } else {
125 | /// 滚动到顶部
126 | scrollRangeToVisible(NSRange(location: 0, length: 0))
127 | }
128 | }
129 |
130 | @objc
131 | func textDidEndEditing(notification: Notification) {
132 | if let sender = notification.object as? GrowingTextView, sender == self {
133 | scrollToCorrectPosition()
134 | }
135 | }
136 |
137 | @objc
138 | func textDidChange(notification: Notification) {
139 | if let sender = notification.object as? GrowingTextView, sender == self {
140 | setNeedsDisplay()
141 | layoutIfNeeded()
142 | }
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Component/Class/TreeTableView/TreeNode.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TreeNode.swift
3 | // LTXiOSUtils
4 | //
5 | // Created by CoderStar on 2020/2/1.
6 | //
7 |
8 | import Foundation
9 |
10 | /// 节点状态
11 | public enum TreeNodeCheckState {
12 | /// 未选中
13 | case uncheckd
14 | /// 选中
15 | case checked
16 | /// 半选中,当子节点有选中但未全部选中时
17 | case halfChecked
18 | }
19 |
20 | /// 排序类型
21 | public enum TreeNodeSordType {
22 | /// 正序
23 | case asc
24 | /// 倒序
25 | case desc
26 | }
27 |
28 | /// 节点实体
29 | public class TreeNode: NSObject {
30 | /// 名称,用于显示
31 | public var name: String
32 | /// 搜索名称,用于多条件搜索
33 | public var filterName: [String]
34 | /// ID
35 | public var ID: String
36 | /// parentID
37 | public var parentID: String
38 | /// 排序号
39 | public var sordCode: Int
40 | /// 数据类型
41 | public var type: String
42 | /// 是否顶级父节点
43 | public var isTop: Bool
44 | /// 是否叶子节点
45 | public var isLeaf: Bool
46 | /// 原始数据
47 | public var data: Any
48 |
49 | var level: Int = 0
50 | var isExpand: Bool = false
51 | var checkState: TreeNodeCheckState = .uncheckd
52 | var parentNode: TreeNode?
53 | var childNodes = [TreeNode]()
54 |
55 | public init(name: String, filterName: [String], ID: String, parentID: String, sordCode: Int, type: String, isTop: Bool, isLeaf: Bool, data: Any) {
56 | self.name = name
57 | self.filterName = filterName
58 | self.ID = ID
59 | self.parentID = parentID
60 | self.sordCode = sordCode
61 | self.type = type
62 | self.isTop = isTop
63 | self.isLeaf = isLeaf
64 | self.data = data
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Component/Class/TreeTableView/TreeTableViewCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TreeTableViewCell.swift
3 | // LTXiOSUtils
4 | //
5 | // Created by CoderStar on 2020/2/1.
6 | //
7 |
8 | import Foundation
9 |
10 | /// Cell文字显示样式
11 | public enum TreeTableViewCellTextStyle {
12 | /// 全部显示
13 | case all
14 | /// 自适应字体
15 | case adjustFont
16 | /// 冒号显示,前面冒号
17 | case truncatingHead
18 | /// 冒号显示,后面冒号
19 | case truncatingTail
20 | /// 冒号显示,中间冒号
21 | case truncatingMiddle
22 | }
23 |
24 | public class TreeTableViewCell: UITableViewCell {
25 | /// 节点数据
26 | public var treeNode: TreeNode! {
27 | didSet {
28 | setCellView()
29 | }
30 | }
31 |
32 | /// 是否单选
33 | public var isSingleCheck: Bool = true {
34 | didSet {
35 | if treeNode != nil {
36 | accessoryView = getCheckButton(isSingleCheck: isSingleCheck)
37 | }
38 | }
39 | }
40 |
41 | /// 点击闭包
42 | public var checkClick: ((_ treeNode: TreeNode) -> Void)?
43 |
44 | /// Cell文字显示样式
45 | public var cellTextStyle: TreeTableViewCellTextStyle = .truncatingTail {
46 | didSet {
47 | switch cellTextStyle {
48 | case .all:
49 | textLabel?.numberOfLines = 0
50 | textLabel?.lineBreakMode = .byCharWrapping
51 | case .adjustFont:
52 | textLabel?.numberOfLines = 1
53 | textLabel?.adjustsFontSizeToFitWidth = true
54 | case .truncatingHead:
55 | textLabel?.numberOfLines = 1
56 | textLabel?.lineBreakMode = .byTruncatingHead
57 | case .truncatingTail:
58 | textLabel?.numberOfLines = 1
59 | textLabel?.lineBreakMode = .byTruncatingTail
60 | case .truncatingMiddle:
61 | textLabel?.numberOfLines = 1
62 | textLabel?.lineBreakMode = .byTruncatingMiddle
63 | }
64 | }
65 | }
66 |
67 | /// 左边间距
68 | private let leftMargin: CGFloat = 15
69 | /// 字体大小
70 | private let fontSize: CGFloat = 15
71 |
72 | public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
73 | super.init(style: style, reuseIdentifier: reuseIdentifier)
74 | setupView()
75 | }
76 |
77 | required init?(coder: NSCoder) {
78 | super.init(coder: coder)
79 | setupView()
80 | }
81 |
82 | public override func layoutSubviews() {
83 | super.layoutSubviews()
84 | let minX = leftMargin + CGFloat(indentationLevel) * indentationWidth
85 | var imageViewFrame = imageView?.frame
86 | imageViewFrame?.origin.x = minX
87 | imageView?.frame = imageViewFrame!
88 | var textLabelFrame = textLabel?.frame
89 | textLabelFrame?.origin.x = minX + max(imageView!.bounds.size.width, leftMargin) + 2
90 | let textLabelX = textLabelFrame?.origin.x
91 | textLabelFrame?.size.width = (accessoryView?.frame.origin.x)! - textLabelX! - 5
92 | textLabel?.frame = textLabelFrame!
93 | }
94 |
95 | private func setupView() {
96 | textLabel?.font = UIFont.systemFont(ofSize: fontSize)
97 | selectionStyle = .none
98 | indentationWidth = leftMargin
99 | }
100 | }
101 |
102 | // MARK: - 公开属性
103 |
104 | extension TreeTableViewCell {
105 | /// 刷新箭头
106 | public func refreshArrow() {
107 | UIView.animate(withDuration: 0.25) {
108 | self.updateArrow()
109 | }
110 | }
111 | }
112 |
113 | // MARK: - 私有方法
114 |
115 | extension TreeTableViewCell {
116 | private func setCellView() {
117 | indentationLevel = treeNode.level
118 | textLabel?.text = treeNode.name
119 | if treeNode.childNodes.count > 0 {
120 | imageView?.image = "TreeTableView_arrow".imageOfLTXiOSUtilsComponent
121 | } else {
122 | imageView?.image = nil
123 | }
124 | accessoryView = getCheckButton(isSingleCheck: isSingleCheck)
125 | updateArrow()
126 | }
127 |
128 | private func getCheckImage() -> UIImage? {
129 | switch treeNode.checkState {
130 | case .uncheckd:
131 | return "TreeTableView_checkbox_uncheck".imageOfLTXiOSUtilsComponent
132 | case .checked:
133 | return "TreeTableView_checkbox_checked".imageOfLTXiOSUtilsComponent
134 | case .halfChecked:
135 | return "TreeTableView_checkbox_halfchecked".imageOfLTXiOSUtilsComponent
136 | }
137 | }
138 |
139 | private func getCheckButton(isSingleCheck: Bool) -> UIButton {
140 | let button = UIButton(type: .custom)
141 | button.addTouchUpInsideAction { _ in
142 | self.checkClick?(self.treeNode)
143 | }
144 | button.adjustsImageWhenHighlighted = false
145 | if isSingleCheck {
146 | let buttonWidth: CGFloat = 50
147 | let buttonHeight: CGFloat = 25
148 | button.frame = CGRect(x: 0, y: (contentView.bounds.size.height - buttonHeight) / 2, width: buttonWidth, height: buttonHeight)
149 | button.setTitle("选择", for: .normal)
150 | button.backgroundColor = .white
151 | button.setTitleColor(.blue, for: .normal)
152 | button.layer.borderColor = UIColor.gray.cgColor
153 | button.layer.cornerRadius = 5
154 | button.titleLabel?.font = UIFont.systemFont(ofSize: fontSize)
155 | button.layer.borderWidth = 0.5
156 | } else {
157 | button.frame = CGRect(x: 0, y: 0, width: contentView.bounds.size.height, height: contentView.bounds.size.height)
158 | let checkImage = getCheckImage()
159 | button.setImage(checkImage, for: .normal)
160 | let margin = (button.frame.height - button.imageView!.frame.height) / 2
161 | button.contentEdgeInsets = UIEdgeInsets(top: margin, left: margin, bottom: margin, right: margin)
162 | }
163 | return button
164 | }
165 |
166 | /// 更新cell前面箭头
167 | private func updateArrow() {
168 | if treeNode.isExpand {
169 | imageView?.transform = CGAffineTransform(rotationAngle: CGFloat(Double.pi / 2))
170 | } else {
171 | imageView?.transform = CGAffineTransform(rotationAngle: 0)
172 | }
173 | }
174 | }
175 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Component/Class/TreeTableView/TreeTableViewSearchBar.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TreeTableViewSearchBar.swift
3 | // LTXiOSUtils
4 | //
5 | // Created by CoderStar on 2020/2/1.
6 | //
7 |
8 | import Foundation
9 |
10 | /// 协议
11 | public protocol TreeTableViewSearchBarDelegate: AnyObject {
12 | /// 输入框开始进行编辑
13 | func treeTableViewSearchBarDidBeginEditing(searchBar: TreeTableViewSearchBar)
14 | /// 点击键盘搜索键
15 | func treeTableViewSearchBarShouldReturn(searchBar: TreeTableViewSearchBar)
16 | /// 实时搜索
17 | func treeTableViewSearchBarSearhing(searchBar: TreeTableViewSearchBar)
18 | }
19 |
20 | public class TreeTableViewSearchBar: UIView {
21 | public lazy var searchTextField: UITextField = {
22 | let searchTextField = UITextField()
23 | searchTextField.layer.cornerRadius = 5
24 | searchTextField.placeholder = "请输入关键字进行搜索"
25 | searchTextField.clearButtonMode = .whileEditing
26 | searchTextField.adjustsFontSizeToFitWidth = true
27 | searchTextField.returnKeyType = .search
28 | searchTextField.leftViewMode = .always
29 | searchTextField.backgroundColor = .white
30 | searchTextField.delegate = self
31 | searchTextField.addTarget(self, action: #selector(search), for: .editingChanged)
32 | return searchTextField
33 | }()
34 |
35 | public weak var delegate: TreeTableViewSearchBarDelegate?
36 |
37 | public var text: String? {
38 | return searchTextField.text
39 | }
40 |
41 | public override init(frame: CGRect) {
42 | super.init(frame: frame)
43 | setupView()
44 | }
45 |
46 | required init?(coder: NSCoder) {
47 | super.init(coder: coder)
48 | setupView()
49 | }
50 |
51 | private func setupView() {
52 | backgroundColor = UIColor(hexString: "#eeeeee")
53 | addSubview(searchTextField)
54 |
55 | searchTextField.frame = CGRect(x: 5, y: 5, width: frame.size.width - 10, height: frame.size.height - 10)
56 | searchTextField.leftView = getLeftView(height: frame.size.height - 10)
57 | }
58 |
59 | private func getLeftView(height: CGFloat) -> UIView {
60 | let button = UIButton(type: .custom)
61 | button.isUserInteractionEnabled = false
62 | button.frame = CGRect(x: 0, y: 0, width: height, height: height)
63 | button.contentEdgeInsets = UIEdgeInsets(top: height / 4, left: height / 4, bottom: height / 4, right: height / 4)
64 | button.setImage("TreeTableView_search".imageOfLTXiOSUtilsComponent, for: .normal)
65 | return button
66 | }
67 | }
68 |
69 | extension TreeTableViewSearchBar: UITextFieldDelegate {
70 | public func textFieldDidBeginEditing(_ textField: UITextField) {
71 | delegate?.treeTableViewSearchBarDidBeginEditing(searchBar: self)
72 | }
73 |
74 | public func textFieldShouldReturn(_ textField: UITextField) -> Bool {
75 | delegate?.treeTableViewSearchBarShouldReturn(searchBar: self)
76 | textField.resignFirstResponder()
77 | return true
78 | }
79 |
80 | @objc
81 | private func search() {
82 | delegate?.treeTableViewSearchBarSearhing(searchBar: self)
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Component/Resources/LTXiOSUtilsResource.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LTXiOSUtilsResource.swift
3 | // LTXiOSUtils
4 | //
5 | // Created by CoderStar on 2019/12/20.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - 字符串扩展,获取资源
11 |
12 | extension String {
13 | /// 获取LTXiOSUtilsComponent Bundle中的国际化
14 | /// - Parameter comment: 注释参数
15 | public var localizedOfLTXiOSUtilsComponent: String {
16 | return Bundle.tx.getBundle(bundleName: "LTXiOSUtilsComponent")?.localizedString(forKey: self, value: nil, table: nil) ?? ""
17 | }
18 |
19 | /// 获取LTXiOSUtilsComponent Bundle库中的图片
20 | public var imageOfLTXiOSUtilsComponent: UIImage? {
21 | return UIImage(named: self, in: Bundle.tx.getBundle(bundleName: "LTXiOSUtilsComponent"), compatibleWith: nil)
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Component/Resources/Resource/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Component/Resources/Resource/Assets.xcassets/ImagePickGridView/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Component/Resources/Resource/Assets.xcassets/ImagePickGridView/ImagePickGridView_addImage.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "addImage.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "addImage@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "addImage@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Component/Resources/Resource/Assets.xcassets/ImagePickGridView/ImagePickGridView_addImage.imageset/addImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Coder-Star/LTXiOSUtils/8ede2e39e3d20ae396f3cadf8799665e085329e9/Sources/LTXiOSUtils/Classes/Component/Resources/Resource/Assets.xcassets/ImagePickGridView/ImagePickGridView_addImage.imageset/addImage.png
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Component/Resources/Resource/Assets.xcassets/ImagePickGridView/ImagePickGridView_addImage.imageset/addImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Coder-Star/LTXiOSUtils/8ede2e39e3d20ae396f3cadf8799665e085329e9/Sources/LTXiOSUtils/Classes/Component/Resources/Resource/Assets.xcassets/ImagePickGridView/ImagePickGridView_addImage.imageset/addImage@2x.png
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Component/Resources/Resource/Assets.xcassets/ImagePickGridView/ImagePickGridView_addImage.imageset/addImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Coder-Star/LTXiOSUtils/8ede2e39e3d20ae396f3cadf8799665e085329e9/Sources/LTXiOSUtils/Classes/Component/Resources/Resource/Assets.xcassets/ImagePickGridView/ImagePickGridView_addImage.imageset/addImage@3x.png
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Component/Resources/Resource/Assets.xcassets/ImagePickGridView/ImagePickGridView_deleteImage.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "删除.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Component/Resources/Resource/Assets.xcassets/ImagePickGridView/ImagePickGridView_deleteImage.imageset/删除.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Coder-Star/LTXiOSUtils/8ede2e39e3d20ae396f3cadf8799665e085329e9/Sources/LTXiOSUtils/Classes/Component/Resources/Resource/Assets.xcassets/ImagePickGridView/ImagePickGridView_deleteImage.imageset/删除.png
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Component/Resources/Resource/Assets.xcassets/TreeTableView/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Component/Resources/Resource/Assets.xcassets/TreeTableView/TreeTableView_arrow.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "arrow.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "arrow@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "arrow@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Component/Resources/Resource/Assets.xcassets/TreeTableView/TreeTableView_arrow.imageset/arrow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Coder-Star/LTXiOSUtils/8ede2e39e3d20ae396f3cadf8799665e085329e9/Sources/LTXiOSUtils/Classes/Component/Resources/Resource/Assets.xcassets/TreeTableView/TreeTableView_arrow.imageset/arrow.png
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Component/Resources/Resource/Assets.xcassets/TreeTableView/TreeTableView_arrow.imageset/arrow@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Coder-Star/LTXiOSUtils/8ede2e39e3d20ae396f3cadf8799665e085329e9/Sources/LTXiOSUtils/Classes/Component/Resources/Resource/Assets.xcassets/TreeTableView/TreeTableView_arrow.imageset/arrow@2x.png
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Component/Resources/Resource/Assets.xcassets/TreeTableView/TreeTableView_arrow.imageset/arrow@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Coder-Star/LTXiOSUtils/8ede2e39e3d20ae396f3cadf8799665e085329e9/Sources/LTXiOSUtils/Classes/Component/Resources/Resource/Assets.xcassets/TreeTableView/TreeTableView_arrow.imageset/arrow@3x.png
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Component/Resources/Resource/Assets.xcassets/TreeTableView/TreeTableView_checkbox_checked.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "checkbox-checked.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "checkbox-checked@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "checkbox-checked@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Component/Resources/Resource/Assets.xcassets/TreeTableView/TreeTableView_checkbox_checked.imageset/checkbox-checked.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Coder-Star/LTXiOSUtils/8ede2e39e3d20ae396f3cadf8799665e085329e9/Sources/LTXiOSUtils/Classes/Component/Resources/Resource/Assets.xcassets/TreeTableView/TreeTableView_checkbox_checked.imageset/checkbox-checked.png
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Component/Resources/Resource/Assets.xcassets/TreeTableView/TreeTableView_checkbox_checked.imageset/checkbox-checked@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Coder-Star/LTXiOSUtils/8ede2e39e3d20ae396f3cadf8799665e085329e9/Sources/LTXiOSUtils/Classes/Component/Resources/Resource/Assets.xcassets/TreeTableView/TreeTableView_checkbox_checked.imageset/checkbox-checked@2x.png
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Component/Resources/Resource/Assets.xcassets/TreeTableView/TreeTableView_checkbox_checked.imageset/checkbox-checked@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Coder-Star/LTXiOSUtils/8ede2e39e3d20ae396f3cadf8799665e085329e9/Sources/LTXiOSUtils/Classes/Component/Resources/Resource/Assets.xcassets/TreeTableView/TreeTableView_checkbox_checked.imageset/checkbox-checked@3x.png
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Component/Resources/Resource/Assets.xcassets/TreeTableView/TreeTableView_checkbox_halfchecked.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "checkbox-partial.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "checkbox-partial@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "checkbox-partial@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Component/Resources/Resource/Assets.xcassets/TreeTableView/TreeTableView_checkbox_halfchecked.imageset/checkbox-partial.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Coder-Star/LTXiOSUtils/8ede2e39e3d20ae396f3cadf8799665e085329e9/Sources/LTXiOSUtils/Classes/Component/Resources/Resource/Assets.xcassets/TreeTableView/TreeTableView_checkbox_halfchecked.imageset/checkbox-partial.png
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Component/Resources/Resource/Assets.xcassets/TreeTableView/TreeTableView_checkbox_halfchecked.imageset/checkbox-partial@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Coder-Star/LTXiOSUtils/8ede2e39e3d20ae396f3cadf8799665e085329e9/Sources/LTXiOSUtils/Classes/Component/Resources/Resource/Assets.xcassets/TreeTableView/TreeTableView_checkbox_halfchecked.imageset/checkbox-partial@2x.png
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Component/Resources/Resource/Assets.xcassets/TreeTableView/TreeTableView_checkbox_halfchecked.imageset/checkbox-partial@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Coder-Star/LTXiOSUtils/8ede2e39e3d20ae396f3cadf8799665e085329e9/Sources/LTXiOSUtils/Classes/Component/Resources/Resource/Assets.xcassets/TreeTableView/TreeTableView_checkbox_halfchecked.imageset/checkbox-partial@3x.png
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Component/Resources/Resource/Assets.xcassets/TreeTableView/TreeTableView_checkbox_uncheck.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "checkbox-uncheck.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "checkbox-uncheck@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "checkbox-uncheck@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Component/Resources/Resource/Assets.xcassets/TreeTableView/TreeTableView_checkbox_uncheck.imageset/checkbox-uncheck.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Coder-Star/LTXiOSUtils/8ede2e39e3d20ae396f3cadf8799665e085329e9/Sources/LTXiOSUtils/Classes/Component/Resources/Resource/Assets.xcassets/TreeTableView/TreeTableView_checkbox_uncheck.imageset/checkbox-uncheck.png
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Component/Resources/Resource/Assets.xcassets/TreeTableView/TreeTableView_checkbox_uncheck.imageset/checkbox-uncheck@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Coder-Star/LTXiOSUtils/8ede2e39e3d20ae396f3cadf8799665e085329e9/Sources/LTXiOSUtils/Classes/Component/Resources/Resource/Assets.xcassets/TreeTableView/TreeTableView_checkbox_uncheck.imageset/checkbox-uncheck@2x.png
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Component/Resources/Resource/Assets.xcassets/TreeTableView/TreeTableView_checkbox_uncheck.imageset/checkbox-uncheck@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Coder-Star/LTXiOSUtils/8ede2e39e3d20ae396f3cadf8799665e085329e9/Sources/LTXiOSUtils/Classes/Component/Resources/Resource/Assets.xcassets/TreeTableView/TreeTableView_checkbox_uncheck.imageset/checkbox-uncheck@3x.png
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Component/Resources/Resource/Assets.xcassets/TreeTableView/TreeTableView_search.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "search.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "search@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "search@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Component/Resources/Resource/Assets.xcassets/TreeTableView/TreeTableView_search.imageset/search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Coder-Star/LTXiOSUtils/8ede2e39e3d20ae396f3cadf8799665e085329e9/Sources/LTXiOSUtils/Classes/Component/Resources/Resource/Assets.xcassets/TreeTableView/TreeTableView_search.imageset/search.png
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Component/Resources/Resource/Assets.xcassets/TreeTableView/TreeTableView_search.imageset/search@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Coder-Star/LTXiOSUtils/8ede2e39e3d20ae396f3cadf8799665e085329e9/Sources/LTXiOSUtils/Classes/Component/Resources/Resource/Assets.xcassets/TreeTableView/TreeTableView_search.imageset/search@2x.png
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Component/Resources/Resource/Assets.xcassets/TreeTableView/TreeTableView_search.imageset/search@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Coder-Star/LTXiOSUtils/8ede2e39e3d20ae396f3cadf8799665e085329e9/Sources/LTXiOSUtils/Classes/Component/Resources/Resource/Assets.xcassets/TreeTableView/TreeTableView_search.imageset/search@3x.png
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Component/Resources/Resource/en.lproj/Localizable.strings:
--------------------------------------------------------------------------------
1 | /*
2 | Localizable.strings
3 | Pods
4 | 英文语言资源
5 | Created by CoderStar on 2019/12/20.
6 | */
7 |
8 | // 通用
9 | sure = "Sure";
10 | confirm = "Confirm";
11 | cancel = "Cancel";
12 | complete = "Complete";
13 | clear = "Clear";
14 |
15 | // 起止时间选择
16 | DurationDatePickView.topTitle = "Please Select Start And End Time";
17 | DurationDatePickView.to = "To";
18 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Component/Resources/Resource/zh-Hans.lproj/Localizable.strings:
--------------------------------------------------------------------------------
1 | /*
2 | Localizable.strings
3 | Pods
4 | 中文语言资源
5 | Created by CoderStar on 2019/12/20.
6 | */
7 |
8 | // 通用
9 | sure = "确定";
10 | cancel = "取消";
11 | confirm = "确认";
12 | complete = "完成";
13 | clear = "清空";
14 |
15 | // 起止时间选择
16 | DurationDatePickView.topTitle = "请选择起止时间";
17 | DurationDatePickView.to = "至";
18 |
19 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Extension/Core/TxExtensionWrapper.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TxExtensionWrapper.swift
3 | // LTXiOSUtils
4 | // 系统扩展命名空间,对系统类进行扩展,用来替换OC中前缀的做法,更Swift的做法
5 | // Created by CoderStar on 2020/1/12.
6 | //
7 |
8 | import Foundation
9 |
10 | /// 扩展命名空间
11 | public struct TxExtensionWrapper {
12 | /// 泛型
13 | public let base: Base
14 |
15 | /// 构造函数
16 | /// - Parameter base: 泛型
17 | public init(_ base: Base) {
18 | self.base = base
19 | }
20 | }
21 |
22 | /// 引用类型协议
23 | public protocol TxExtensionWrapperCompatible: AnyObject {}
24 |
25 | /// 值类型协议
26 | public protocol TxExtensionWrapperCompatibleValue {}
27 |
28 | /// TxExtensionWrapperCompatible协议默认实现
29 | extension TxExtensionWrapperCompatible {
30 | /// 实例变量
31 | public var tx: TxExtensionWrapper {
32 | get {
33 | return TxExtensionWrapper(self)
34 | }
35 | // swiftlint:disable:next unused_setter_value
36 | set {
37 | // 提供set方法使关联的实例可以进行修改
38 | }
39 | }
40 |
41 | /// 类变量
42 | public static var tx: TxExtensionWrapper.Type {
43 | get {
44 | return TxExtensionWrapper.self
45 | }
46 | // swiftlint:disable:next unused_setter_value
47 | set {
48 | // 提供set方法使关联的类可以进行修改
49 | }
50 | }
51 | }
52 |
53 | /// TxExtensionWrapperCompatibleValue协议默认实现
54 | extension TxExtensionWrapperCompatibleValue {
55 | /// 实例变量
56 | public var tx: TxExtensionWrapper {
57 | get {
58 | return TxExtensionWrapper(self)
59 | }
60 | // swiftlint:disable:next unused_setter_value
61 | set {
62 | // 提供set方法使关联的实例可以进行修改
63 | }
64 | }
65 |
66 | /// 类变量
67 | public static var tx: TxExtensionWrapper.Type {
68 | get {
69 | return TxExtensionWrapper.self
70 | }
71 | // swiftlint:disable:next unused_setter_value
72 | set {
73 | // 提供set方法使关联的类可以进行修改
74 | }
75 | }
76 | }
77 |
78 | extension NSObject: TxExtensionWrapperCompatible {}
79 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Extension/Dispatch/DispatchExtensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DispatchExtensions.swift
3 | // LTXiOSUtils
4 | //
5 | // Created by CoderStar on 2019/12/16.
6 | //
7 |
8 | import Foundation
9 |
10 | extension TxExtensionWrapper where Base: DispatchQueue {
11 | /// 延时扩展
12 | ///
13 | /// - Parameters:
14 | /// - delay: 延时时间
15 | /// - execute: 闭包执行
16 | public func delay(_ delay: Double, execute: @escaping () -> Void) {
17 | base.asyncAfter(deadline: DispatchTime(floatLiteral: delay), execute: execute)
18 | }
19 |
20 | /// 切换队列
21 | ///
22 | /// 当当前线程为主线程并且是主队列切换时,不再进行切换,而是直接执行代码块
23 | /// - Note: 主线程可以执行非主队列的任务(如主线程中调用全局队列的同步函数)
24 | /// 避免当主线程执行非主队列任务时,主线程切换队列的开销
25 | /// 同时避免切换队列造成的执行时序问题
26 | /// - Parameter block: 代码块
27 | public func safeAsync(_ block: @escaping () -> Void) {
28 | if base === DispatchQueue.main, Thread.isMainThread {
29 | block()
30 | } else {
31 | base.async { block() }
32 | }
33 | }
34 | }
35 |
36 | extension TxExtensionWrapper where Base == DispatchQueue {
37 | private static var onceTracker = Set()
38 |
39 | /// 保证代码块只执行一次
40 | ///
41 | /// - Parameters:
42 | /// - token: 唯一标识符
43 | /// - block: 闭包
44 | public static func once(token: String, block: () -> Void) {
45 | objc_sync_enter(self)
46 | defer {
47 | objc_sync_exit(self)
48 | }
49 | if onceTracker.contains(token) {
50 | return
51 | }
52 | onceTracker.insert(token)
53 | block()
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Extension/Dispatch/DispatchTimeExtensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DispatchTimeExtensions.swift
3 | // LTXiOSUtils
4 | //
5 | // Created by CoderStar on 2021/9/12.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - DispatchTime扩展,遍历构造函数
11 |
12 | // MARK: - 未使用命名空间
13 |
14 | extension DispatchTime: ExpressibleByIntegerLiteral, ExpressibleByFloatLiteral {
15 | public init(integerLiteral value: Int) {
16 | self = DispatchTime.now() + .seconds(value)
17 | }
18 |
19 | public init(floatLiteral value: Double) {
20 | self = DispatchTime.now() + .milliseconds(Int(value * 1_000))
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Extension/Foundation/BundleExtensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BundleExtensions.swift
3 | // LTXiOSUtils
4 | //
5 | // Created by CoderStar on 2020/11/9.
6 | //
7 |
8 | import Foundation
9 |
10 | extension TxExtensionWrapper where Base: Bundle {
11 | /// 获取指定pod的指定Bundle,前提是pod以resource_bundle方式引入资源
12 | /// - Parameters:
13 | /// - bundleName: bundleName
14 | /// - podName: podName,当使用framwork时使用,如果为nil,赋值为bundleName
15 | /// - Note: 当使用framework方式引用三方库时,每一个三方库都会在Frameworks下面以一个单独的framework存在
16 | public static func getBundle(bundleName: String, podName: String? = nil) -> Bundle? {
17 | var podNameStr = podName
18 | var bundleNameStr = bundleName
19 |
20 | if bundleNameStr.contains(".bundle") {
21 | bundleNameStr = bundleNameStr.components(separatedBy: ".bundle").first!
22 | }
23 |
24 | // 不使用framwork
25 | var url = Bundle.main.url(forResource: bundleNameStr, withExtension: "bundle")
26 |
27 | // 使用framwork,拼接路径
28 | if url == nil {
29 | if podNameStr == nil {
30 | podNameStr = bundleNameStr
31 | }
32 | url = Bundle.main.url(forResource: "Frameworks", withExtension: nil)
33 | url = url?.appendingPathComponent(podNameStr!).appendingPathExtension("framework")
34 | if url == nil {
35 | return nil
36 | }
37 | url = Bundle(url: url!)?.url(forResource: bundleNameStr, withExtension: "bundle")
38 | }
39 |
40 | guard let bundleUrl = url else {
41 | return nil
42 | }
43 | return Bundle(url: bundleUrl)
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Extension/Foundation/DataExtensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DataExtensions.swift
3 | // LTXiOSUtils
4 | //
5 | // Created by CoderStar on 2020/9/30.
6 | //
7 |
8 | import Foundation
9 |
10 | extension Data: TxExtensionWrapperCompatibleValue {}
11 |
12 | extension TxExtensionWrapper where Base == Data {
13 | /// 16进制Data转字符串
14 | ///
15 | /// - Note: 获取的推送token可以用这个进行转换
16 | public var hexString: String {
17 | return base.map { String(format: "%02x", $0) }.joined()
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Extension/Foundation/DateExtensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DateExtensions.swift
3 | // LTXiOSUtils
4 | // 时间扩展
5 | // Created by CoderStar on 2019/8/2.
6 | // Copyright © 2019年 CoderStar. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension Date: TxExtensionWrapperCompatibleValue {}
12 |
13 | /// 日期格式化类型
14 | public enum DateFormateType: String {
15 | /// - YMDHMS: 年月日时分秒/2019-01-01 12:00:00
16 | case YMDHMS = "yyyy-MM-dd HH:mm:ss"
17 | /// - YMDHM: 年月日时分/2019-01-01 12:00
18 | case YMDHM = "yyyy-MM-dd HH:mm"
19 | /// - MDHM: 月日时分/01-01 12:00
20 | case MDHM = "MM-dd HH:mm"
21 | /// - YMDE: 日期星期/2019-01-01 星期一
22 | case YMDE = "yyyy-MM-dd EEEE"
23 | /// - YMD: 年月日/2019-01-01
24 | case YMD = "yyyy-MM-dd"
25 | /// - HMS: 时分秒/12:00:00
26 | case HMS = "HH:mm:ss"
27 | /// - YM: 年月日/2019-01
28 | case YM = "yyyy-MM"
29 | /// - MD: 月日/2019-01
30 | case MD = "MM-dd"
31 | /// - HM: 时分/12:00
32 | case HM = "HH:mm"
33 | }
34 |
35 | // MARK: - 日期扩展
36 |
37 | extension TxExtensionWrapper where Base == Date {
38 | /// Date格式化
39 | ///
40 | /// - Parameter format: 格式化类型
41 | /// - Returns: 格式化后的字符串
42 | public func formatDate(format: DateFormateType) -> String {
43 | return formatDate(formatStr: format.rawValue)
44 | }
45 |
46 | /// Date格式化
47 | ///
48 | /// - Parameter format: 日期格式
49 | /// - Returns: 格式化后的字符串
50 | public func formatDate(formatStr: String) -> String {
51 | let dateFormatter = DateFormatter()
52 | dateFormatter.dateFormat = formatStr
53 | dateFormatter.calendar = Calendar(identifier: Calendar.Identifier.gregorian)
54 | dateFormatter.timeZone = TimeZone.current
55 | dateFormatter.locale = Locale(identifier: "en_US_POSIX")
56 | let dateString = dateFormatter.string(from: base)
57 | return dateString
58 | }
59 |
60 | /// 获取星期
61 | public var weekDay: String {
62 | let weekDays = [NSNull(), "日", "一", "二", "三", "四", "五", "六"] as [Any]
63 | let calendar = NSCalendar(calendarIdentifier: .gregorian)
64 | let timeZone = TimeZone.current
65 | calendar?.timeZone = timeZone
66 | let calendarUnit = NSCalendar.Unit.weekday
67 | let theComponents = calendar?.components(calendarUnit, from: base)
68 | if let index = theComponents?.weekday, weekDays.count > index, let weekday = weekDays[index] as? String {
69 | return weekday
70 | }
71 | return ""
72 | }
73 |
74 | /// 获取相对指定时间之前几天或者之后几天的日期,之前的填入负数
75 | /// - Parameter days: 日期,单位为天
76 | public func getDateByDays(days: Int) -> Date {
77 | let date = Date(timeInterval: TimeInterval(days * 24 * 60 * 60), since: base)
78 | return date
79 | }
80 | }
81 |
82 | // MARK: - 当前时间等相关
83 |
84 | extension TxExtensionWrapper where Base == Date {
85 | /// 秒级时间戳 - 10位
86 | public var timeStamp: Int {
87 | let timeInterval = base.timeIntervalSince1970
88 | let timeStamp = Int(timeInterval)
89 | return timeStamp
90 | }
91 |
92 | /// 获取秒级时间戳 - 10位
93 | public var timeStampStr: String {
94 | return "\(timeStamp)"
95 | }
96 |
97 | /// 获取毫秒级时间戳 - 13位
98 | public var milliTimeStamp: Int {
99 | let timeInterval = base.timeIntervalSince1970
100 | let millisecond = Int(round(timeInterval * 1_000))
101 | return millisecond
102 | }
103 |
104 | /// 获取毫秒级时间戳 - 13位
105 | public var milliTimeStampStr: String {
106 | return "\(milliTimeStamp)"
107 | }
108 |
109 | /// 获取当前时间
110 | public static func getCurrentTime() -> String {
111 | return Date().tx.formatDate(format: .YMDHMS)
112 | }
113 |
114 | /// 获取当前日期
115 | public static func getCurrentDate() -> String {
116 | return Date().tx.formatDate(format: .YMD)
117 | }
118 | }
119 |
120 | // MARK: - 时间戳转时间
121 |
122 | extension TxExtensionWrapper where Base == TimeInterval {
123 | /// 时间戳(毫秒)转时间
124 | public var dateAsMilliStamp: Date {
125 | let timeInterval = base / 1_000
126 | return Date(timeIntervalSince1970: timeInterval)
127 | }
128 |
129 | /// 时间戳转时间字符串
130 | /// - Parameters:
131 | /// - format: 时间格式化格式
132 | public func toDateStrAsMilliStamp(format: DateFormateType) -> String {
133 | return dateAsMilliStamp.tx.formatDate(format: format)
134 | }
135 |
136 | /// 时间戳(秒)转时间
137 | public var dateAsTimeStamp: Date {
138 | return Date(timeIntervalSince1970: base)
139 | }
140 |
141 | /// 时间戳(秒)转时间字符串
142 | /// - Parameters:
143 | /// - format: 时间格式化格式
144 | public func toDateStrAsTimeStamp(format: DateFormateType) -> String {
145 | return dateAsTimeStamp.tx.formatDate(format: format)
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Extension/Foundation/URLExtensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // URLExtensions.swift
3 | // LTXiOSUtils
4 | //
5 | // Created by CoderStar on 2020/9/9.
6 | //
7 |
8 | import Foundation
9 |
10 | extension TxExtensionWrapper where Base == URL {
11 | /// 获取URL的参数
12 | public var parametersFromQueryString: [String: String]? {
13 | guard let components = URLComponents(url: base, resolvingAgainstBaseURL: true),
14 | let queryItems = components.queryItems else { return nil }
15 | return queryItems.reduce(into: [String: String]()) { result, item in
16 | result[item.name] = item.value
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Extension/SwiftStdlib/CollectionExtensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CollectionExtensions.swift
3 | // LTXiOSUtils
4 | //
5 | // Created by CoderStar on 2021/9/14.
6 | //
7 |
8 | extension Collection {
9 | /// 安全取指定索引数据
10 | ///
11 | /// - Parameters:
12 | /// - safeIndex: 指定索引
13 | public subscript(safeIndex index: Index) -> Element? {
14 | return indices.contains(index) ? self[index] : nil
15 | }
16 |
17 | /// 判断集合非空
18 | public var isNotEmpty: Bool {
19 | return !isEmpty
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Extension/SwiftStdlib/DoubleExtensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DoubleExtensions.swift
3 | // LTXiOSUtils
4 | // Double扩展
5 | // Created by CoderStar on 2020/3/20.
6 | //
7 |
8 | extension Double: TxExtensionWrapperCompatibleValue {}
9 |
10 | extension TxExtensionWrapper where Base == Double {
11 | /// 去除浮点数后面多余的0
12 | public var removeSuffixZero: String {
13 | return base.truncatingRemainder(dividingBy: 1) == 0 ? String(format: "%.0f", base) : String(base)
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Extension/SwiftStdlib/IntExtensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // IntExtensions.swift
3 | // LTXiOSUtils
4 | //
5 | // Created by CoderStar on 2019/12/30.
6 | //
7 |
8 | extension Int: TxExtensionWrapperCompatibleValue {}
9 |
10 | extension TxExtensionWrapper where Base == Int {}
11 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Extension/SwiftStdlib/OptionalExtensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // OptionalExtensions.swift
3 | // LTXiOSUtils
4 | //
5 | // Created by CoderStar on 2019/11/21.
6 | //
7 |
8 | extension Optional {
9 | /// 判断是否为空
10 | public var isNil: Bool {
11 | switch self {
12 | case .none:
13 | return true
14 | case .some:
15 | return false
16 | }
17 | }
18 |
19 | /// 判断是否有值
20 | public var isNotNil: Bool {
21 | return !isNil
22 | }
23 |
24 | /// 返回解包后的值或者默认值
25 | public func or(_ default: Wrapped) -> Wrapped {
26 | return self ?? `default`
27 | }
28 |
29 | /// 返回解包后的值或`else`表达式的值
30 | public func or(else: @autoclosure () -> Wrapped) -> Wrapped {
31 | return self ?? `else`()
32 | }
33 |
34 | /// 返回解包后的值或执行闭包返回值
35 | public func or(else: () -> Wrapped) -> Wrapped {
36 | return self ?? `else`()
37 | }
38 |
39 | /// 当可选值不为空时,执行 `some` 闭包
40 | public func on(some: () throws -> Void) rethrows {
41 | if self != nil { try some() }
42 | }
43 |
44 | /// 当可选值为空时,执行 `none` 闭包
45 | public func on(none: () throws -> Void) rethrows {
46 | if self == nil { try none() }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Extension/SwiftStdlib/SequenceExtensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ArrayExtensions.swift
3 | // LTXiOSUtils
4 | // Array扩展
5 | // Created by CoderStar on 2020/1/8.
6 | //
7 |
8 | extension Array: TxExtensionWrapperCompatibleValue {}
9 |
10 | extension TxExtensionWrapper where Base: Sequence {
11 | /// 数组去重,排序后数据保持顺序
12 | /// - Parameter repeated: 去重标准
13 | public func removeDuplicate(_ repeated: (Base.Iterator.Element) -> E) -> [Base.Iterator.Element] {
14 | var result = [Base.Iterator.Element]()
15 | base.forEach {
16 | let key = repeated($0)
17 | let keys = result.compactMap { repeated($0) }
18 | guard !keys.contains(key) else {
19 | return
20 | }
21 | result.append($0)
22 | }
23 | return result
24 | }
25 |
26 | /// keyPath的形式对数组排序
27 | /// - Parameter keyPath: keyPath
28 | /// - Returns: 排序后的数组
29 | public func sorted(by keyPath: KeyPath) -> [Base.Iterator.Element] {
30 | return base.sorted {
31 | return $0[keyPath: keyPath] < $1[keyPath: keyPath]
32 | }
33 | }
34 |
35 | /// 将集合分组
36 | ///
37 | /// - Parameter key: 分组依据
38 | /// - Returns: 分组后结果
39 | public func group(by key: (Base.Iterator.Element) -> GroupingType) -> [[Base.Iterator.Element]] {
40 | var groups: [GroupingType: [Base.Iterator.Element]] = [:]
41 | var groupsOrder: [GroupingType] = []
42 | base.forEach { element in
43 | let key = key(element)
44 | if case nil = groups[key]?.append(element) {
45 | groups[key] = [element]
46 | groupsOrder.append(key)
47 | }
48 | }
49 | return groupsOrder.map { groups[$0]! }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Extension/UIKit/NSMutableAttributedStringExtensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSMutableAttributedStringExtensions.swift
3 | // LTXiOSUtils
4 | // NSMutableAttributedString扩展
5 | // Created by CoderStar on 2020/3/17.
6 | //
7 |
8 | import Foundation
9 | import UIKit
10 |
11 | // MARK: - 富文本文字样式相关
12 |
13 | /// 字体样式
14 | public enum FontStyle {
15 | /// 默认
16 | case `default`
17 | /// 粗体
18 | case bold
19 | /// 斜体
20 | case italic
21 | /// 粗斜体
22 | case boldItalic
23 | }
24 |
25 | extension TxExtensionWrapper where Base: NSMutableAttributedString {
26 | private var allRange: NSRange {
27 | let str = base.string
28 | let theRange = NSString(string: str).range(of: str)
29 | return theRange
30 | }
31 |
32 | /// 添加字体
33 | /// - Parameters:
34 | /// - fontName: 字体名称
35 | /// - fontStyle: 字体样式
36 | /// - fontSize: 字体尺寸
37 | public func addFontStyle(fontName: String = "", fontStyle: FontStyle, fontSize: CGFloat) -> NSMutableAttributedString {
38 | var font = UIFont.systemFont(ofSize: fontSize)
39 | switch fontStyle {
40 | case .default:
41 | break
42 | case .bold:
43 | font = UIFont.tx.boldFont(name: fontName, size: fontSize)
44 | case .italic:
45 | font = UIFont.tx.italicFont(name: fontName, size: fontSize)
46 | case .boldItalic:
47 | font = UIFont.tx.boldItalicFont(name: fontName, size: fontSize)
48 | }
49 | base.addAttribute(NSMutableAttributedString.Key.font, value: font, range: allRange)
50 | return base
51 | }
52 |
53 | /// 添加下划线
54 | /// - Parameters:
55 | /// - underlineStyle: 下划线样式,默认为single
56 | /// - underlineColor: 下划线颜色,默认为nil,nil时跟字体颜色保持相同
57 | public func addUnderline(style underlineStyle: NSUnderlineStyle = .single, color underlineColor: UIColor? = nil) -> NSMutableAttributedString {
58 | base.addAttribute(NSMutableAttributedString.Key.underlineStyle, value: underlineStyle, range: allRange)
59 | if let tempColor = underlineColor {
60 | base.addAttribute(NSMutableAttributedString.Key.underlineColor, value: tempColor, range: allRange)
61 | }
62 | return base
63 | }
64 |
65 | /// 添加删除线
66 | /// - Parameters:
67 | /// - strikethroughStyle: 删除线样式,默认为single
68 | /// - strikethroughColor: 删除线样式,默认为nil,nil时跟字体颜色保持相同
69 | public func addStrikethrough(style strikethroughStyle: NSUnderlineStyle = .single, color strikethroughColor: UIColor? = nil) -> NSMutableAttributedString {
70 | base.addAttribute(NSMutableAttributedString.Key.strikethroughStyle, value: strikethroughStyle, range: allRange)
71 | if let tempColor = strikethroughColor {
72 | base.addAttribute(NSMutableAttributedString.Key.strikethroughColor, value: tempColor, range: allRange)
73 | }
74 | return base
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Extension/UIKit/UICollectionViewCellExtensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UICollectionViewCellExtensions.swift
3 | // LTXiOSUtils
4 | //
5 | // Created by CoderStar on 2021/9/12.
6 | //
7 |
8 | import Foundation
9 | import UIKit
10 |
11 | extension TxExtensionWrapper where Base: UICollectionViewCell {
12 | /// 标识符
13 | ///
14 | /// - Note: 重用时可使用
15 | public var identifier: String {
16 | return String(describing: base)
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Extension/UIKit/UIColorExtensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIColorExtensions.swift
3 | // LTXiOSUtils
4 | // 颜色工具类以及扩展
5 | // Created by CoderStar on 2019/8/2.
6 | // Copyright © 2019年 CoderStar. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 |
12 | // MARK: - 颜色扩展
13 |
14 | extension UIColor {
15 | /// 颜色hex值转颜色,如果hex值去除头部符号后不满6位,返回默认色-白色
16 | ///
17 | /// - Parameters:
18 | /// - hexString: hex值
19 | /// - alpha: 透明度
20 | public convenience init(hexString: String, alpha: CGFloat = 1.0) {
21 | var hexString = hexString.trimmingCharacters(in: .whitespacesAndNewlines)
22 | hexString = hexString.lowercased()
23 |
24 | if hexString.hasPrefix("#") {
25 | hexString = String(hexString.dropFirst())
26 | }
27 | if hexString.hasPrefix("0x") {
28 | hexString = String(hexString.dropFirst(2))
29 | }
30 | // hex值少于6位,返回白色
31 | if hexString.count < 6 {
32 | self.init(red: 255, green: 255, blue: 255, alpha: alpha)
33 | } else {
34 | let scanner = Scanner(string: hexString)
35 | var color: UInt32 = 0
36 | scanner.scanHexInt32(&color)
37 |
38 | let mask = 0x000000FF
39 | let r = Int(color >> 16) & mask
40 | let g = Int(color >> 8) & mask
41 | let b = Int(color) & mask
42 |
43 | let red = CGFloat(r) / 255.0
44 | let green = CGFloat(g) / 255.0
45 | let blue = CGFloat(b) / 255.0
46 |
47 | self.init(red: red, green: green, blue: blue, alpha: alpha)
48 | }
49 | }
50 | }
51 |
52 | extension TxExtensionWrapper where Base: UIColor {
53 | /// 颜色转hex值
54 | public var hexString: String? {
55 | var red: CGFloat = 0
56 | var green: CGFloat = 0
57 | var blue: CGFloat = 0
58 | var alpha: CGFloat = 0
59 |
60 | let multiplier = CGFloat(255.999_999)
61 |
62 | guard base.getRed(&red, green: &green, blue: &blue, alpha: &alpha) else {
63 | return nil
64 | }
65 |
66 | if alpha == 1.0 {
67 | return String(
68 | format: "#%02lX%02lX%02lX",
69 | Int(red * multiplier),
70 | Int(green * multiplier),
71 | Int(blue * multiplier)
72 | )
73 | } else {
74 | return String(
75 | format: "#%02lX%02lX%02lX%02lX",
76 | Int(red * multiplier),
77 | Int(green * multiplier),
78 | Int(blue * multiplier),
79 | Int(alpha * multiplier)
80 | )
81 | }
82 | }
83 |
84 | /// 颜色的反色
85 | public var invertColor: UIColor {
86 | var r: CGFloat = 0, g: CGFloat = 0, b: CGFloat = 0
87 | base.getRed(&r, green: &g, blue: &b, alpha: nil)
88 | return UIColor(red: 1.0 - r, green: 1.0 - g, blue: 1.0 - b, alpha: 1)
89 | }
90 |
91 | /// 红色值
92 | public var redColor: Int {
93 | var red: CGFloat = 0
94 | base.getRed(&red, green: nil, blue: nil, alpha: nil)
95 | return Int(red * 255)
96 | }
97 |
98 | /// 绿色值
99 | public var greenColor: Int {
100 | var green: CGFloat = 0
101 | base.getRed(nil, green: &green, blue: nil, alpha: nil)
102 | return Int(green * 255)
103 | }
104 |
105 | /// 蓝色值
106 | public var blueColor: Int {
107 | var blue: CGFloat = 0
108 | base.getRed(nil, green: nil, blue: &blue, alpha: nil)
109 | return Int(blue * 255)
110 | }
111 | }
112 |
113 | // MARK: - 颜色、图片
114 |
115 | extension TxExtensionWrapper where Base: UIColor {
116 | /// 颜色生成指定大小的UIImage
117 | ///
118 | /// - Parameter size: 图片尺寸
119 | /// - Returns: 图片
120 | public func toImage(size: CGSize) -> UIImage? {
121 | let createImage = { (_: UIColor) -> UIImage? in
122 | let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
123 | UIGraphicsBeginImageContext(rect.size)
124 | guard let context = UIGraphicsGetCurrentContext() else {
125 | return nil
126 | }
127 | context.setFillColor(base.cgColor)
128 | context.fill(rect)
129 | let image = UIGraphicsGetImageFromCurrentImageContext()
130 | UIGraphicsGetCurrentContext()
131 | return image
132 | }
133 |
134 | // 兼容暗黑模式
135 | if #available(iOS 13.0, *) {
136 | let image = UIImage()
137 | let appearances: [UIUserInterfaceStyle] = [.light, .dark]
138 | for item in appearances {
139 | let traitCollection = UITraitCollection(userInterfaceStyle: item)
140 | guard let eachImage = createImage(base.resolvedColor(with: traitCollection)) else {
141 | return nil
142 | }
143 | image.imageAsset?.register(eachImage, with: traitCollection)
144 | }
145 | return image
146 | } else {
147 | return createImage(base)
148 | }
149 | }
150 | }
151 |
152 | extension TxExtensionWrapper where Base: UIColor {
153 | /// 适配暗黑模式
154 | /// - Parameter colorWithDark: 暗黑模式下的颜色,默认为nil,取颜色反色
155 | public func adaptDark(_ colorWithDark: UIColor? = nil) -> UIColor {
156 | if #available(iOS 13.0, *) {
157 | if UITraitCollection.current.userInterfaceStyle == .dark {
158 | guard let darkColor = colorWithDark else {
159 | return self.invertColor
160 | }
161 | return darkColor
162 | }
163 | } else {
164 | return base
165 | }
166 | return base
167 | }
168 |
169 | /// 适配各种模式
170 | /// - Parameter colorWithDark: 暗黑模式颜色
171 | public func adapt(colorWithDark: UIColor? = nil) -> UIColor {
172 | return adaptDark(colorWithDark)
173 | }
174 | }
175 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Extension/UIKit/UIFontExtensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIFontExtensions.swift
3 | // LTXiOSUtils
4 | // UIFont扩展
5 | // Created by CoderStar on 2020/3/17.
6 | //
7 |
8 | import Foundation
9 | import UIKit
10 |
11 | extension TxExtensionWrapper where Base: UIFont {
12 | /// 粗体
13 | /// - Parameter fontName: 字体名称,为空时使用系统字体,默认为空
14 | /// - Parameter fontSize: 字体尺寸
15 | public static func boldFont(name fontName: String = "", size fontSize: CGFloat) -> UIFont {
16 | var tempFontName = fontName
17 | if tempFontName.isEmpty {
18 | tempFontName = UIFont.systemFont(ofSize: fontSize).fontName
19 | }
20 | var descriptor = UIFontDescriptor(name: tempFontName, size: fontSize)
21 | if let tempDescriptor = descriptor.withSymbolicTraits([.traitBold]) {
22 | descriptor = tempDescriptor
23 | }
24 | let font = UIFont(descriptor: descriptor, size: fontSize)
25 | return font
26 | }
27 |
28 | /// 斜体
29 | /// - Parameters:
30 | /// - fontName: 字体名称,为空时使用系统字体,默认为空
31 | /// - fontSize: 字体尺寸
32 | public static func italicFont(name fontName: String = "", size fontSize: CGFloat) -> UIFont {
33 | let matrix = CGAffineTransform(a: 1, b: 0, c: CGFloat(tanf(15 * Float.pi / 180)), d: 1, tx: 0, ty: 0)
34 | var tempFontName = fontName
35 | if tempFontName.isEmpty {
36 | tempFontName = UIFont.systemFont(ofSize: fontSize).fontName
37 | }
38 | let descriptor = UIFontDescriptor(name: tempFontName, matrix: matrix)
39 | let font = UIFont(descriptor: descriptor, size: fontSize)
40 | return font
41 | }
42 |
43 | /// 粗体、斜体
44 | /// 如果粗体生成失败,则会返回斜体
45 | /// - Parameter fontName: 字体名称,为空时使用系统字体,默认为空
46 | /// - Parameter fontSize: 字体尺寸
47 | public static func boldItalicFont(name fontName: String = "", size fontSize: CGFloat) -> UIFont {
48 | let matrix = CGAffineTransform(a: 1, b: 0, c: CGFloat(tanf(15 * Float.pi / 180)), d: 1, tx: 0, ty: 0)
49 | var tempFontName = fontName
50 | if tempFontName.isEmpty {
51 | tempFontName = UIFont.systemFont(ofSize: fontSize).fontName
52 | }
53 | var descriptor = UIFontDescriptor(name: tempFontName, matrix: matrix)
54 | if let tempDescriptor = descriptor.withSymbolicTraits([.traitBold]) {
55 | descriptor = tempDescriptor
56 | }
57 | let font = UIFont(descriptor: descriptor, size: fontSize)
58 | return font
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Extension/UIKit/UIImageViewExtensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIImageViewExtensions.swift
3 | // LTXiOSUtils
4 | //
5 | // Created by CoderStar on 2019/11/21.
6 | //
7 |
8 | import Foundation
9 | import UIKit
10 |
11 | extension TxExtensionWrapper where Base: UIImageView {}
12 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Extension/UIKit/UILabelExtensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UILabelExtensions.swift
3 | // LTXiOSUtils
4 | // UILabel扩展
5 | // Created by CoderStar on 2020/1/2.
6 | //
7 |
8 | import Foundation
9 | import UIKit
10 |
11 | extension TxExtensionWrapper where Base: UILabel {}
12 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Extension/UIKit/UINavigationControllerExtensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UINavigationControllerExtensions.swift
3 | // LTXiOSUtils
4 | // UINavigationController扩展
5 | // Created by CoderStar on 2020/9/17.
6 | //
7 |
8 | import Foundation
9 | import UIKit
10 |
11 | extension TxExtensionWrapper where Base: UINavigationController {
12 | /// 关闭当前页面并跳转到新页面
13 | /// - Parameters:
14 | /// - viewController: 页面
15 | /// - animated: 是否显示动画
16 | public func toViewControllerAndCloseSelf(_ viewController: UIViewController, animated: Bool = true) {
17 | var newViewControllers = [UIViewController]()
18 | if base.viewControllers.count > 0 {
19 | for index in 0 ..< base.viewControllers.count - 1 {
20 | newViewControllers.append(base.viewControllers[index])
21 | }
22 | }
23 | newViewControllers.append(viewController)
24 | base.setViewControllers(newViewControllers, animated: animated)
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Extension/UIKit/UITableViewCellExtensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UITableViewCellExtensions.swift
3 | // LTXiOSUtils
4 | //
5 | // Created by CoderStar on 2021/9/12.
6 | //
7 |
8 | import Foundation
9 | import UIKit
10 |
11 | extension TxExtensionWrapper where Base: UITableViewCell {
12 | /// 标识符
13 | ///
14 | /// - Note: 重用时可使用
15 | public var identifier: String {
16 | return String(describing: base)
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Extension/UIKit/UITapGestureRecognizerExtensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UITapGestureRecognizerExtensions.swift
3 | // LTXiOSUtils
4 | //
5 | // Created by CoderStar on 2021/9/12.
6 | //
7 |
8 | import Foundation
9 | import UIKit
10 |
11 | // MARK: - 未使用命名空间
12 |
13 | // MARK: - UITapGestureRecognizer添加不可用时间间隔
14 |
15 | extension UITapGestureRecognizer: UIGestureRecognizerDelegate {
16 | private struct UITapGestureDictKey {
17 | static var key: Void?
18 | }
19 |
20 | /// 不可用时间间隔
21 | private var disEnabledtimeInterval: CGFloat? {
22 | set {
23 | objc_setAssociatedObject(self, &UITapGestureDictKey.key, newValue, .OBJC_ASSOCIATION_COPY_NONATOMIC)
24 | }
25 | get {
26 | return objc_getAssociatedObject(self, &UITapGestureDictKey.key) as? CGFloat
27 | }
28 | }
29 |
30 | /// 防重复点击手势便利构造函数
31 | /// - Parameters:
32 | /// - target: target
33 | /// - action: action
34 | /// - disEnabledtimeInterval: 不可点击时间
35 | public convenience init(target: Any?, action: Selector?, disEnabledtimeInterval: CGFloat) {
36 | self.init(target: target, action: action)
37 | self.disEnabledtimeInterval = disEnabledtimeInterval
38 | delegate = self
39 | }
40 |
41 | public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
42 | // 延时禁用,防止认为手势未识别成功导致调用touch相关函数不正常
43 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
44 | self.isEnabled = false
45 | }
46 | let time = TimeInterval(disEnabledtimeInterval ?? 0.0)
47 | DispatchQueue.main.asyncAfter(deadline: .now() + time) {
48 | self.isEnabled = true
49 | }
50 | return true
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Extension/UIKit/UITextFieldExtensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UITextFieldExtensions.swift
3 | // LTXiOSUtils
4 | //
5 | // Created by CoderStar on 2020/9/28.
6 | //
7 |
8 | import Foundation
9 | import UIKit
10 |
11 | extension TxExtensionWrapper where Base: UITextField {
12 | /// 取值时去除了空格符以及换行符
13 | /// 可用于提交表单前用来判断值是否不为空
14 | public var contentText: String? {
15 | set {
16 | base.text = newValue
17 | }
18 | get {
19 | return base.text?.trimmingCharacters(in: .whitespacesAndNewlines)
20 | }
21 | }
22 | }
23 |
24 | @IBDesignable
25 | extension UITextField {
26 | @IBInspectable
27 | public var csLeftPaddingWidth: CGFloat {
28 | get {
29 | return leftView!.frame.size.width
30 | }
31 | set {
32 | let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: newValue, height: frame.size.height))
33 | leftView = paddingView
34 | leftViewMode = .always
35 | }
36 | }
37 |
38 | @IBInspectable
39 | public var csRigthPaddingWidth: CGFloat {
40 | get {
41 | return rightView!.frame.size.width
42 | }
43 | set {
44 | let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: newValue, height: frame.size.height))
45 | rightView = paddingView
46 | rightViewMode = .always
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Extension/WebKit/WKWebViewExtensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WKWebViewExtensions.swift
3 | // LTXiOSUtils
4 | //
5 | // Created by CoderStar on 2021/4/16.
6 | //
7 |
8 | import Foundation
9 | import UIKit
10 | import WebKit
11 |
12 | extension TxExtensionWrapper where Base: WKWebView {
13 | private class WKWebViewInputAccessoryView {
14 | @objc
15 | var inputAccessoryView: AnyObject? {
16 | return nil
17 | }
18 | }
19 |
20 | /// 移除键盘inputAccessoryView
21 | public func removeInputAccessoryView() {
22 | guard let target = base.scrollView.subviews.first(where: {
23 | String(describing: type(of: $0)).hasPrefix("WKContent")
24 | }), let superclass = target.superclass else {
25 | return
26 | }
27 |
28 | let noInputAccessoryViewClassName = "\(superclass)_NoInputAccessoryView"
29 | var newClass: AnyClass? = NSClassFromString(noInputAccessoryViewClassName)
30 |
31 | if newClass == nil, let targetClass = object_getClass(target), let classNameCString = noInputAccessoryViewClassName.cString(using: .ascii) {
32 | newClass = objc_allocateClassPair(targetClass, classNameCString, 0)
33 |
34 | if let newClass = newClass {
35 | objc_registerClassPair(newClass)
36 | }
37 | }
38 |
39 | guard let noInputAccessoryClass = newClass, let originalMethod = class_getInstanceMethod(WKWebViewInputAccessoryView.self, #selector(getter: WKWebViewInputAccessoryView.inputAccessoryView)) else {
40 | return
41 | }
42 | class_addMethod(noInputAccessoryClass.self, #selector(getter: WKWebViewInputAccessoryView.inputAccessoryView), method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod))
43 | object_setClass(target, noInputAccessoryClass)
44 | }
45 | }
46 |
47 | extension TxExtensionWrapper where Base: WKWebView {
48 | private typealias OlderClosureType = @convention(c) (Any, Selector, UnsafeRawPointer, Bool, Bool, Any?) -> Void
49 | private typealias NewerClosureType = @convention(c) (Any, Selector, UnsafeRawPointer, Bool, Bool, Bool, Any?) -> Void
50 |
51 | /// 使html autofocus 属性生效,而不需要用户手动点击
52 | /// Stack Overflow上可以设置开关,经过设置,如果value设置为true,input输入框都无法点击弹出键盘了
53 | /// 注意检查有没有副作用
54 | public func setKeyboardDisplayDoesNotRequireUserAction() {
55 | guard let WKContentViewClass = NSClassFromString("WKContentView") else {
56 | return
57 | }
58 |
59 | let olderSelector: Selector = sel_getUid("_startAssistingNode:userIsInteracting:blurPreviousNode:userObject:")
60 | let newSelector: Selector = sel_getUid("_startAssistingNode:userIsInteracting:blurPreviousNode:changingActivityState:userObject:")
61 | let newerSelector: Selector = sel_getUid("_elementDidFocus:userIsInteracting:blurPreviousNode:changingActivityState:userObject:")
62 | let ios13Selector: Selector = sel_getUid("_elementDidFocus:userIsInteracting:blurPreviousNode:activityStateChanges:userObject:")
63 |
64 | if let method = class_getInstanceMethod(WKContentViewClass, olderSelector) {
65 | let originalImp: IMP = method_getImplementation(method)
66 | let original: OlderClosureType = unsafeBitCast(originalImp, to: OlderClosureType.self)
67 | let block: @convention(block) (Any, UnsafeRawPointer, Bool, Bool, Any?) -> Void = { me, arg0, _, arg2, arg3 in
68 | original(me, olderSelector, arg0, true, arg2, arg3)
69 | }
70 | let imp: IMP = imp_implementationWithBlock(block)
71 | method_setImplementation(method, imp)
72 | }
73 |
74 | if let method = class_getInstanceMethod(WKContentViewClass, newSelector) {
75 | swizzleAutofocusMethod(method, newSelector)
76 | }
77 |
78 | if let method = class_getInstanceMethod(WKContentViewClass, newerSelector) {
79 | swizzleAutofocusMethod(method, newerSelector)
80 | }
81 |
82 | if let method = class_getInstanceMethod(WKContentViewClass, ios13Selector) {
83 | swizzleAutofocusMethod(method, ios13Selector)
84 | }
85 | }
86 |
87 | private func swizzleAutofocusMethod(_ method: Method, _ selector: Selector) {
88 | let originalImp: IMP = method_getImplementation(method)
89 | let original: NewerClosureType = unsafeBitCast(originalImp, to: NewerClosureType.self)
90 | let block: @convention(block) (Any, UnsafeRawPointer, Bool, Bool, Bool, Any?) -> Void = { me, arg0, _, arg2, arg3, arg4 in
91 | original(me, selector, arg0, true, arg2, arg3, arg4)
92 | }
93 | let imp: IMP = imp_implementationWithBlock(block)
94 | method_setImplementation(method, imp)
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/PropertyWrapper/Atomic.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Atomic.swift
3 | // LTXiOSUtils
4 | //
5 | // Created by CoderStar on 2022/3/24.
6 | //
7 |
8 | import Foundation
9 |
10 | /* Example
11 |
12 | struct MyStruct {
13 | @Atomic var x = 0
14 | }
15 |
16 | var value = MyStruct()
17 |
18 | /// 写
19 | value.x = 1
20 |
21 | /// 边读边写
22 | value.$x.mutate { $0 += 1 }
23 |
24 | **/
25 |
26 | /// 因为要实现 `mutate` 的操作,需要使用class结构,struct结构不ok
27 |
28 | @propertyWrapper
29 | final public class Atomic {
30 | private let queue = DispatchQueue(label: "com.coderstar.atomic")
31 | private var value: Value
32 |
33 | public init(wrappedValue: Value) {
34 | value = wrappedValue
35 | }
36 |
37 | /// 保证分别读写安全
38 | public var wrappedValue: Value {
39 | get {
40 | return queue.sync { value }
41 | }
42 | set {
43 | queue.sync { value = newValue }
44 | }
45 | }
46 |
47 | public var projectedValue: Atomic {
48 | return self
49 | }
50 |
51 | /// 保证边读边写安全
52 | public func mutate(_ mutation: (inout Value) -> Void) {
53 | return queue.sync {
54 | mutation(&value)
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/PropertyWrapper/UserDefaultWrapper.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UserDefaultWrapper.swift
3 | // LTXiOSUtils
4 | //
5 | // Created by CoderStar on 2021/10/31.
6 | //
7 |
8 | import Foundation
9 |
10 | /* Example
11 |
12 | enum UserDefaultsConfig {
13 | @UserDefaultsWrapper("hadShownGuideView", defaultValue: false)
14 | static var hadShownGuideView: Bool
15 | }
16 |
17 | var observation: UserDefaultsObservation?
18 |
19 | observation = UserDefaultsConfig.$hadShownGuideView.observe { _, _ in }
20 |
21 | UserDefaultsConfig.hadShownGuideView = true
22 | let hadShownGuideView = UserDefaultsConfig.hadShownGuideView
23 |
24 | **/
25 |
26 | // MARK: - 定义可以被UserDefaults存储的类型
27 |
28 | public protocol UserDefaultsStoreValue {}
29 |
30 | extension Data: UserDefaultsStoreValue {}
31 | extension String: UserDefaultsStoreValue {}
32 | extension Date: UserDefaultsStoreValue {}
33 | extension Bool: UserDefaultsStoreValue {}
34 | extension Int: UserDefaultsStoreValue {}
35 | extension Double: UserDefaultsStoreValue {}
36 | extension Float: UserDefaultsStoreValue {}
37 | extension Array: UserDefaultsStoreValue where Element: UserDefaultsStoreValue {}
38 | extension Dictionary: UserDefaultsStoreValue where Key == String, Value: UserDefaultsStoreValue {}
39 | extension Optional: UserDefaultsStoreValue {}
40 |
41 | @propertyWrapper
42 | public struct UserDefaultsWrapper {
43 | let key: String
44 | let defaultValue: T
45 | let userDefaults: UserDefaults
46 |
47 | /// 构造函数
48 | /// - Parameters:
49 | /// - key: 存储key值
50 | /// - defaultValue: 当存储值不存在时返回的默认值
51 | public init(_ key: String, defaultValue: T, userDefaults: UserDefaults = UserDefaults.standard) {
52 | self.key = key
53 | self.defaultValue = defaultValue
54 | self.userDefaults = userDefaults
55 | }
56 |
57 | /// wrappedValue是@propertyWrapper必须需要实现的属性
58 | /// 当操作我们要包裹的属性时,其具体的set、get方法实际上走的都是wrappedValue的get、set方法
59 | public var wrappedValue: T {
60 | get {
61 | return userDefaults.object(forKey: key) as? T ?? defaultValue
62 | }
63 | set {
64 | userDefaults.setValue(newValue, forKey: key)
65 | }
66 | }
67 |
68 | /// 使可以通过 $ 的形式访问到 UserDefaultsWrapper,继而可以访问observe方法
69 | public var projectedValue: UserDefaultsWrapper { return self }
70 |
71 | public func observe(change: @escaping (T?, T?) -> Void) -> UserDefaultsObservation {
72 | return UserDefaultsObservation(key: key, userDefaults: userDefaults) { old, new in
73 | change(old as? T, new as? T)
74 | }
75 | }
76 | }
77 |
78 | /// 观察UserDefaults变化
79 | public class UserDefaultsObservation: NSObject {
80 | let key: String
81 | let userDefaults: UserDefaults
82 |
83 | private var onChange: (Any, Any) -> Void
84 |
85 | init(key: String, userDefaults: UserDefaults, onChange: @escaping (Any, Any) -> Void) {
86 | self.onChange = onChange
87 | self.key = key
88 | self.userDefaults = userDefaults
89 | super.init()
90 | self.userDefaults.addObserver(self, forKeyPath: key, options: [.old, .new], context: nil)
91 | }
92 |
93 | public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) {
94 | guard let change = change, object != nil, keyPath == key else { return }
95 | onChange(change[.oldKey] as Any, change[.newKey] as Any)
96 | }
97 |
98 | deinit {
99 | userDefaults.removeObserver(self, forKeyPath: key, context: nil)
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Util/AsyncOperation.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AsyncOperation.swift
3 | // LTXiOSUtils
4 | // 异步处理Operation
5 | // Created by CoderStar on 2021/8/30.
6 | //
7 |
8 | import Foundation
9 |
10 | final public class AsyncOperation: Operation {
11 | private var block: ((_ operation: AsyncOperation) -> Void)?
12 |
13 | private let queue = DispatchQueue(label: "async.operation.queue")
14 | private let lock = NSLock()
15 |
16 | private var _executing = false
17 | private var _finished = false
18 |
19 | /// 数据
20 | ///
21 | /// 为Operation绑定一下数据,方便被依赖的Operation获取该Operation处理后的一些数据
22 | public var data: Any?
23 |
24 | /// 是否执行
25 | ///
26 | /// 内部加锁保证线程安全
27 | public override var isExecuting: Bool {
28 | get {
29 | lock.lock()
30 | let wasExecuting = _executing
31 | lock.unlock()
32 | return wasExecuting
33 | }
34 | set {
35 | if isExecuting != newValue {
36 | willChangeValue(forKey: "isExecuting")
37 | lock.lock()
38 | _executing = newValue
39 | lock.unlock()
40 | didChangeValue(forKey: "isExecuting")
41 | }
42 | }
43 | }
44 |
45 | /// 是否结束
46 | ///
47 | /// 内部加锁保证线程安全
48 | /// 需要手动进行KVO,否则completionBlock不会被触发,被依赖的Operation也不会开始
49 | public override var isFinished: Bool {
50 | get {
51 | lock.lock()
52 | let wasFinished = _finished
53 | lock.unlock()
54 | return wasFinished
55 | }
56 | set {
57 | if isFinished != newValue {
58 | willChangeValue(forKey: "isFinished")
59 | lock.lock()
60 | _finished = newValue
61 | lock.unlock()
62 | didChangeValue(forKey: "isFinished")
63 | }
64 | }
65 | }
66 |
67 | /// 标识该Operation是否以异步形式运行
68 | public override var isAsynchronous: Bool {
69 | return true
70 | }
71 |
72 | public override func start() {
73 | if isCancelled {
74 | isFinished = true
75 | return
76 | }
77 |
78 | isExecuting = true
79 |
80 | queue.async { [weak self] in
81 | self?.main()
82 | }
83 | }
84 |
85 | public override func main() {
86 | if let block = block {
87 | block(self)
88 | } else {
89 | finish()
90 | }
91 | }
92 | }
93 |
94 | // MARK: - 公开方法
95 |
96 | extension AsyncOperation {
97 | /// 创建AsyncOperation
98 | /// - Parameter block: 执行闭包,在main方法内部执行,如果传入为nil,则自动结束,如果不为nil,则由自己调用`finish()`手动结束
99 | public convenience init(block: ((_ operation: AsyncOperation) -> Void)?) {
100 | self.init()
101 | self.block = block
102 | }
103 |
104 | /// 手动
105 | public func finish() {
106 | isExecuting = false
107 | isFinished = true
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Util/BackupUtils.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BackUpUtils.swift
3 | // LTXiOSUtils
4 | // 备份相关工具类
5 | // Created by CoderStar on 2021/1/18.
6 | //
7 |
8 | import Foundation
9 |
10 | /// 备份工具类
11 | public struct BackupUtils {
12 | /// 设置跳过备份的文件路径
13 | /// - Parameter filePath: 文件路径
14 | /// - Throws: 设置过程中出现的Error
15 | public func addSkipBackupFilePath(filePath: String) throws {
16 | var excludeUrl = URL(fileURLWithPath: filePath)
17 | var urlResourceValues = URLResourceValues()
18 | urlResourceValues.isExcludedFromBackup = true
19 | try excludeUrl.setResourceValues(urlResourceValues)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Util/ChainGrammar.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ChainGrammar.swift
3 | // LTXiOSUtils
4 | // 链式语法
5 | // Created by CoderStar on 2020/2/20.
6 | //
7 |
8 | import Foundation
9 | import UIKit
10 |
11 | // MARK: - Then
12 |
13 | /// 接口
14 | public protocol Then {}
15 |
16 | extension Then where Self: Any {
17 | /// 值类型数据初始化属性
18 | ///
19 | /// let frame = CGRect().with {
20 | /// $0.origin.x = 10
21 | /// $0.size.width = 100
22 | /// }
23 | public func with(_ block: (inout Self) throws -> Void) rethrows -> Self {
24 | var copy = self
25 | try block(©)
26 | return copy
27 | }
28 |
29 | /// 初始化动作,与初始化属性不同在于此没有返回值
30 | ///
31 | /// UserDefaults.standard.do {
32 | /// $0.set("devxoul", forKey: "username")
33 | /// $0.set("devxoul@gmail.com", forKey: "email")
34 | /// $0.synchronize()
35 | /// }
36 | public func `do`(_ block: (Self) throws -> Void) rethrows {
37 | try block(self)
38 | }
39 | }
40 |
41 | extension Then where Self: AnyObject {
42 | /// 引用类型数据初始化属性
43 | ///
44 | /// let label = UILabel().then {
45 | /// $0.textAlignment = .center
46 | /// $0.textColor = UIColor.black
47 | /// $0.text = "Hello, World!"
48 | /// }
49 | public func then(_ block: (Self) throws -> Void) rethrows -> Self {
50 | try block(self)
51 | return self
52 | }
53 | }
54 |
55 | extension NSObject: Then {}
56 | extension CGPoint: Then {}
57 | extension CGRect: Then {}
58 | extension CGSize: Then {}
59 | extension CGVector: Then {}
60 | extension Array: Then {}
61 | extension Dictionary: Then {}
62 | extension Set: Then {}
63 | extension UIEdgeInsets: Then {}
64 | extension UIOffset: Then {}
65 | extension UIRectEdge: Then {}
66 |
67 | /// dynamicMemberLookup实现链式语法
68 | ///
69 | /// - Note: 使用方式
70 | /// ```
71 | /// Setter(subject: UIView())
72 | /// .frame(CGRect(x: 0, y: 0, width: 100, height: 100))
73 | /// .backgroundColor(.white)
74 | /// .alpha(0.5)
75 | /// .subject
76 | /// ```
77 | @dynamicMemberLookup
78 | public struct Setter {
79 | let subject: Subject
80 |
81 | subscript(dynamicMember keyPath: WritableKeyPath) -> ((Value) -> Setter) {
82 | // 获取到真正的对象
83 | var subject = self.subject
84 | return { value in
85 | // 把 value 指派给 subject
86 | subject[keyPath: keyPath] = value
87 | // 回传的类型是 Setter 而不是 Subject
88 | // 因为使用Setter来链式,而不是 Subject 本身
89 | return Setter(subject: subject)
90 | }
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Util/Debouncer+Throttler.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Debouncer.swift
3 | // LTXiOSUtils
4 | //
5 | // Created by CoderStar on 2022/8/3.
6 | //
7 |
8 | import Foundation
9 |
10 | /// 防抖
11 | /// n 秒后在执行该事件,若在 n 秒内被重复触发,则重新计时
12 | /// 应用场景1、处理搜索框过于频繁发起网络请求的问题,每当用户输入一个字符的时候,都发起网络请求,会浪费一部分网络资源,通过debounce,可以实现,当用户停止输入0.5秒再发送请求。
13 | /// 应用场景2、处理按钮的连续点击问题,debounce只接收0.5秒后的最后一次点击事件,因此自动忽略了中间的多次连续点击事件
14 | public class Debouncer {
15 | private let queue: DispatchQueue
16 | private let interval: TimeInterval
17 |
18 | private let semaphore: DebouncerSemaphore
19 | private var workItem: DispatchWorkItem?
20 |
21 | public init(seconds: TimeInterval = 0.5, queue: DispatchQueue = .main) {
22 | interval = seconds
23 | semaphore = DebouncerSemaphore(value: 1)
24 | self.queue = queue
25 | }
26 |
27 | public func invoke(_ action: @escaping (() -> Void)) {
28 | semaphore.sync {
29 | workItem?.cancel()
30 | workItem = DispatchWorkItem(block: {
31 | action()
32 | })
33 | if let item = workItem {
34 | queue.asyncAfter(deadline: .now() + self.interval, execute: item)
35 | }
36 | }
37 | }
38 | }
39 |
40 | /// 节流
41 | /// n 秒内只运行一次,若在 n 秒内重复触发,只有一次生效
42 | /// 应用场景1:监听页面滚动进行相关操作
43 | public class Throttler {
44 | private let queue: DispatchQueue
45 | private let interval: TimeInterval
46 |
47 | private let semaphore: DebouncerSemaphore
48 | private var workItem: DispatchWorkItem?
49 | private var lastExecuteTime = Date()
50 |
51 | public init(seconds: TimeInterval = 0.5, queue: DispatchQueue = .main) {
52 | interval = seconds
53 | semaphore = DebouncerSemaphore(value: 1)
54 | self.queue = queue
55 | }
56 |
57 | public func invoke(_ action: @escaping (() -> Void)) {
58 | semaphore.sync {
59 | workItem?.cancel()
60 | workItem = DispatchWorkItem(block: { [weak self] in
61 | self?.lastExecuteTime = Date()
62 | action()
63 | })
64 | let deadline = Date().timeIntervalSince(lastExecuteTime) > interval ? 0 : interval
65 | if let item = workItem {
66 | queue.asyncAfter(deadline: .now() + deadline, execute: item)
67 | }
68 | }
69 | }
70 | }
71 |
72 | private struct DebouncerSemaphore {
73 | private let semaphore: DispatchSemaphore
74 |
75 | fileprivate init(value: Int) {
76 | semaphore = DispatchSemaphore(value: value)
77 | }
78 |
79 | fileprivate func sync(execute: () -> Void) {
80 | defer { semaphore.signal() }
81 | semaphore.wait()
82 | execute()
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Util/DebugUtils.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DebugUtils.swift
3 | // LTXiOSUtils
4 | //
5 | // Created by CoderStar on 2021/11/13.
6 | //
7 |
8 | import Foundation
9 | import UIKit
10 |
11 | /// 调试工具类
12 | public struct DebugUtils {}
13 |
14 | extension DebugUtils {
15 | /// 获取内存地址
16 | /// - Parameter values:
17 | /// - Parameter o: 地址,使用`&`符号
18 | /// - Returns: 地址
19 | public static func address(_ o: UnsafeRawPointer) -> String {
20 | let addr = Int(bitPattern: o)
21 | return NSString(format: "%p", addr) as String
22 | }
23 |
24 | /// 获取class的地址
25 | /// - Parameter o: 类实例
26 | /// - Returns: 地址
27 | public static func address(_ o: T) -> String {
28 | let addr = unsafeBitCast(o, to: Int.self)
29 | return NSString(format: "%p", addr) as String
30 | }
31 |
32 | /// 计算执行时长
33 | /// - Parameter f: 执行体
34 | /// - Returns: 时长
35 | public static func measureTime(f: () -> Void) -> CFTimeInterval {
36 | let start = CACurrentMediaTime()
37 | f()
38 | let end = CACurrentMediaTime()
39 | return end - start
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Util/DeviceInfo.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DeviceInfo.swift
3 | // LTXiOSUtils
4 | // 设备信息
5 | // Created by CoderStar on 2021/1/26.
6 | //
7 |
8 | import Foundation
9 | import UIKit
10 |
11 | /// 设置相关信息
12 | public struct DeviceInfo {
13 | /// statusBar高度
14 | ///
15 | /// 状态栏高度,非刘海屏20;其余型号不一致,其中iPhone11 Pro 为44,iPhone12 Pro Max 47;
16 | public static var statusBarHeight: CGFloat {
17 | var result: CGFloat = 20
18 | if UIApplication.shared.statusBarFrame.height > 0 {
19 | // 当状态栏隐藏时,UIApplication.shared.statusBarFrame.height取到的值为0
20 | result = UIApplication.shared.statusBarFrame.height
21 | } else if #available(iOS 11.0, *) {
22 | if let safeAreaInsets = UIApplication.shared.delegate?.window??.safeAreaInsets {
23 | // 使用max的原因是出现屏幕旋转情况
24 | result = max(result, safeAreaInsets.top)
25 | result = max(result, safeAreaInsets.left)
26 | result = max(result, safeAreaInsets.bottom)
27 | result = max(result, safeAreaInsets.right)
28 | }
29 | }
30 | return result
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Util/LaunchImageUtils.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LaunchImageUtils.swift
3 | // LTXiOSUtilsDemo
4 | //
5 | // Created by CoderStar on 2022/9/27.
6 | //
7 |
8 | import Foundation
9 |
10 | public struct LaunchImageUtils {
11 | public static func clearLaunchScreenCache() {
12 | let version = 1
13 | let key = "UpdateLaunchScreen_\(version)"
14 | let isUpdated = UserDefaults.standard.bool(forKey: key)
15 | if isUpdated { return }
16 | if let path = launchImageCacheDirectory() {
17 | do {
18 | try FileManager.default.removeItem(atPath: path)
19 | UserDefaults.standard.set(true, forKey: key)
20 | } catch {
21 | print("Failed to delete launch screen cache: \(error)")
22 | }
23 | }
24 | }
25 |
26 | public static func launchImageCacheDirectory() -> String? {
27 | guard let bundleId = Bundle.main.object(forInfoDictionaryKey: "CFBundleIdentifier") else { return nil }
28 | let fileManager = FileManager.default
29 | // iOS 13
30 | if #available(iOS 13.0, *) {
31 | let libraryDirectory = NSSearchPathForDirectoriesInDomains(.libraryDirectory, .userDomainMask, true).first
32 | let libraryPath = libraryDirectory! as NSString
33 | let snaPath = libraryPath.appending("/SplashBoard/Snapshots/\(bundleId) - {DEFAULT GROUP}")
34 | if fileManager.fileExists(atPath: snaPath) {
35 | return snaPath
36 | }
37 |
38 | } else {
39 | let cacheDirectory = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true).first
40 | let cachePath = cacheDirectory! as NSString
41 | let snap = cachePath.appendingPathComponent("Snapshots") as NSString
42 | let snapPath = snap.appendingPathComponent(bundleId as! String)
43 | if fileManager.fileExists(atPath: snapPath) {
44 | return snapPath
45 | }
46 | }
47 | return nil
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Util/LaunchMonitor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LaunchMonitor.swift
3 | // LTXiOSUtils
4 | //
5 | // Created by CoderStar on 2021/12/27.
6 | //
7 |
8 | import Foundation
9 |
10 | extension ProcessInfo {
11 | /// 调用时间距进程启动时刻间隔时间
12 | public var uptime: TimeInterval {
13 | return Date().timeIntervalSince(startTime)
14 | }
15 |
16 | /// 当前进程启动时间
17 | public var startTime: Date {
18 | return processStartTime(for: processIdentifier)
19 | }
20 |
21 | /// 根据进程id获取进程启动时间
22 | /// - Parameter pid: 进程id
23 | /// - Returns: 进程启动时间
24 | public func processStartTime(for pid: Int32) -> Date {
25 | var mib = [CTL_KERN, KERN_PROC, KERN_PROC_PID, pid]
26 | var proc = kinfo_proc()
27 | var size = MemoryLayout.size(ofValue: proc)
28 | mib.withUnsafeMutableBufferPointer { p in
29 | _ = sysctl(p.baseAddress, 4, &proc, &size, nil, 0)
30 | }
31 |
32 | let time = proc.kp_proc.p_starttime
33 | let seconds = Double(time.tv_sec) + Double(time.tv_usec) / Double(NSEC_PER_SEC)
34 |
35 | return Date(timeIntervalSince1970: seconds)
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Util/LockUtils.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LockUtils.swift
3 | // LTXiOSUtilsDemo
4 | //
5 | // Created by CoderStar on 2022/9/25.
6 | //
7 |
8 | import Foundation
9 |
10 | /// 自旋锁
11 | public class SpinLock {
12 | private var locked = 0
13 |
14 | public func lock() {
15 | while !OSAtomicCompareAndSwapLongBarrier(0, 1, &locked) {}
16 | }
17 |
18 | public func unlock() {
19 | OSAtomicCompareAndSwapLongBarrier(1, 0, &locked)
20 | }
21 | }
22 |
23 | /// 递归自旋锁
24 | public class RecursiveSpinLock {
25 | private var thread: UnsafeMutableRawPointer?
26 | private var count = 0
27 |
28 | public func lock() {
29 | if OSAtomicCompareAndSwapPtrBarrier(pthread_self(), pthread_self(), &thread) {
30 | count += 1
31 | return
32 | }
33 | while !OSAtomicCompareAndSwapPtrBarrier(nil, pthread_self(), &thread) {
34 | /// usleep 10提高性能
35 | usleep(10)
36 | }
37 | }
38 |
39 | public func unlock() {
40 | if count > 0 {
41 | count -= 1
42 | } else {
43 | OSAtomicCompareAndSwapPtrBarrier(pthread_self(), nil, &thread)
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Util/NCRuntimeUtils.h:
--------------------------------------------------------------------------------
1 | //
2 | // NCRuntimeUtils.h
3 | // NCModuleManager
4 | //
5 | // Created by CoderStar on 2022/11/25.
6 | //
7 |
8 | #import
9 |
10 | NS_ASSUME_NONNULL_BEGIN
11 |
12 | FOUNDATION_EXTERN BOOL canEnumerateClassesInImage(void);
13 |
14 | FOUNDATION_EXTERN void enumerateClassesInMainBundle(void(^handler)(__unsafe_unretained Class aClass));
15 |
16 | NS_ASSUME_NONNULL_END
17 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Util/NCRuntimeUtils.m:
--------------------------------------------------------------------------------
1 | //
2 | // NCRuntimeUtils.m
3 | // NCModuleManager
4 | //
5 | // Created by CoderStar on 2022/11/25.
6 | //
7 |
8 | #import "NCRuntimeUtils.h"
9 | #include
10 | #import
11 | #import
12 | #import
13 | #import
14 |
15 | #ifndef __LP64__
16 | typedef struct mach_header mach_header_xx;
17 | #else
18 | typedef struct mach_header_64 mach_header_xx;
19 | #endif
20 |
21 | typedef struct objc_cache *Cache;
22 |
23 | typedef struct class_t {
24 | const struct class_t *isa;
25 | const struct class_t *superclass;
26 | const Cache cache;
27 | const IMP *vtable;
28 | const uintptr_t data_NEVER_USE;
29 | } class_t;
30 |
31 | #pragma mark - 私有方法
32 |
33 | static void enumerateImages(void(^handler)(const mach_header_xx *mh, const char *path)) {
34 | if (handler == nil) {
35 | return;
36 | }
37 | for (uint32_t i = 0, count = _dyld_image_count(); i < count; i++) {
38 | handler((const mach_header_xx *)_dyld_get_image_header(i), _dyld_get_image_name(i));
39 | }
40 | }
41 |
42 | /// Check that objc class layout is not changed
43 | ///
44 | /// @param aClass class
45 | static BOOL canReadSuperclassOfClass(Class aClass) {
46 | class_t *cls = (__bridge class_t *)aClass;
47 | uintptr_t superClassAddr = (uintptr_t)cls + sizeof(class_t *);
48 | uintptr_t superClass;
49 | vm_size_t size;
50 | kern_return_t result = vm_read_overwrite(mach_task_self(), superClassAddr, sizeof(void*), (vm_address_t)&superClass, &size);
51 | if (result != KERN_SUCCESS) {
52 | return NO;
53 | }
54 | return YES;
55 | }
56 |
57 | static void enumerateClassesInImage(const mach_header_xx *mh, void(^handler)(Class __unsafe_unretained aClass)) {
58 | if (handler == nil) {
59 | return;
60 | }
61 | #ifndef __LP64__
62 | const struct section *section = getsectbynamefromheader(mh, "__DATA", "__objc_classlist");
63 | if (section == NULL) {
64 | return;
65 | }
66 | uint32_t size = section->size;
67 | #else
68 | const struct section_64 *section = getsectbynamefromheader_64(mh, "__DATA", "__objc_classlist");
69 | if (section == NULL) {
70 | return;
71 | }
72 | uint64_t size = section->size;
73 | #endif
74 | char *imageBaseAddress = (char *)mh;
75 | Class *classReferences = (Class *)(void *)(imageBaseAddress + ((uintptr_t)section->offset&0xffffffff));
76 | for (unsigned long i = 0; i < size/sizeof(void *); i++) {
77 | Class aClass = classReferences[i];
78 | if (aClass) {
79 | handler(aClass);
80 | }
81 | }
82 | }
83 |
84 |
85 | #pragma mark - 公开方法
86 |
87 | BOOL canEnumerateClassesInImage() {
88 | if (canReadSuperclassOfClass([NSObject class]) == NO) {
89 | return NO;
90 | }
91 | NSString *mainBundlePath = [NSBundle mainBundle].executablePath;
92 | for (uint32_t i = 0, count = _dyld_image_count(); i < count; i++) {
93 | const char *path = _dyld_get_image_name(i);
94 | if (strcmp(path, mainBundlePath.UTF8String) == 0) {
95 | const mach_header_xx *mh = (const mach_header_xx *)_dyld_get_image_header(i);
96 | #ifndef __LP64__
97 | const struct section *section = getsectbynamefromheader(mh, "__DATA", "__objc_classlist");
98 | if (section == NULL) {
99 | return NO;
100 | }
101 | uint32_t size = section->size;
102 | #else
103 | const struct section_64 *section = getsectbynamefromheader_64(mh, "__DATA", "__objc_classlist");
104 | if (section == NULL) {
105 | return NO;
106 | }
107 | uint64_t size = section->size;
108 | #endif
109 | if (size > 0) {
110 | char *imageBaseAddress = (char *)mh;
111 | Class *classReferences = (Class *)(void *)(imageBaseAddress + ((uintptr_t)section->offset&0xffffffff));
112 | Class firstClass = classReferences[0];
113 | if (canReadSuperclassOfClass(firstClass) == NO) {
114 | return NO;
115 | }
116 | }
117 | break;
118 | }
119 | }
120 | return YES;
121 | }
122 |
123 | void enumerateClassesInMainBundle(void(^handler)(__unsafe_unretained Class aClass)) {
124 | if (handler == nil) {
125 | return;
126 | }
127 |
128 | enumerateImages(^(const mach_header_xx *mh, const char *path) {
129 | if (strstr(path, "/System/Library/") != NULL ||
130 | strstr(path, "/usr/") != NULL ||
131 | strstr(path, ".dylib") != NULL) {
132 | return;
133 | }
134 | enumerateClassesInImage(mh, ^(__unsafe_unretained Class aClass) {
135 | handler(aClass);
136 | });
137 | });
138 | }
139 |
140 |
141 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Util/NotificationToken.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NotificationToken.swift
3 | // LTXiOSUtils
4 | //
5 | // Created by CoderStar on 2021/7/27.
6 | //
7 |
8 | import Foundation
9 |
10 | final public class NotificationToken: NSObject {
11 | let notificationCenter: NotificationCenter
12 | let token: Any
13 |
14 | init(notificationCenter: NotificationCenter = .default, token: Any) {
15 | self.notificationCenter = notificationCenter
16 | self.token = token
17 | }
18 |
19 | deinit {
20 | notificationCenter.removeObserver(token)
21 | }
22 | }
23 |
24 | extension NotificationCenter {
25 | /// 添加观察
26 | /// 使用该方式无需手动removeObserver, 但需要将返回的实例保存到属性上去
27 | /// - Parameters:
28 | /// - name: name
29 | /// - obj: obj
30 | /// - queue: queue
31 | /// - block: block
32 | /// - Returns: NotificationToken
33 | public func observe(name: NSNotification.Name?, object obj: Any?, queue: OperationQueue?, using block: @escaping (Notification) -> Void) -> NotificationToken {
34 | let token = addObserver(forName: name, object: obj, queue: queue, using: block)
35 | return NotificationToken(notificationCenter: self, token: token)
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Util/ResourceUtils.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ResourceUtils.swift
3 | // LTXiOSUtils
4 | // 本地资源操作工具类
5 | // Created by CoderStar on 2020/2/3.
6 | //
7 |
8 | import Foundation
9 |
10 | /// 文本资源plist类型
11 | public enum ResourcePlistType {
12 | /// 列表
13 | case arr
14 | /// 字典
15 | case dic
16 | }
17 |
18 | /// 文本资源类型
19 | public enum ResourceType {
20 | /// plist
21 | case plist(type: ResourcePlistType)
22 | /// json
23 | case json
24 | /// 字符串
25 | case str
26 | }
27 |
28 | /// 本地资源
29 | public struct ResourceUtils {
30 | /// 读取本地文本资源文件
31 | /// - Parameters:
32 | /// - path: 文件路径
33 | /// - type: 文件类型
34 | public static func getContentInfo(path: String?, type: ResourceType) -> Any? {
35 | var resultInfo: Any?
36 | guard let filePath = path else {
37 | return nil
38 | }
39 | switch type {
40 | case let .plist(type):
41 | switch type {
42 | case .arr:
43 | resultInfo = NSArray(contentsOfFile: filePath)
44 | case .dic:
45 | resultInfo = NSDictionary(contentsOfFile: filePath)
46 | }
47 | case .json:
48 | do {
49 | resultInfo = try Data(contentsOf: URL(fileURLWithPath: filePath))
50 | } catch {
51 | assert(false, error.localizedDescription)
52 | }
53 | case .str:
54 | do {
55 | resultInfo = try String(contentsOfFile: filePath)
56 | } catch {
57 | assert(false, error.localizedDescription)
58 | }
59 | }
60 | return resultInfo
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Util/RuntimeUtils.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RuntimeUtils.swift
3 | // LTXiOSUtils
4 | // 运行时工具类
5 | // Created by CoderStar on 2021/8/20.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - Class相关
11 |
12 | public struct RuntimeUtils {
13 | /// 获取所有的class
14 | /// 只能找到继承于 NSObject 的类
15 | public static var allClasses: [AnyClass] {
16 | let numberOfClasses = Int(objc_getClassList(nil, 0))
17 | if numberOfClasses > 0 {
18 | let classesPtr = UnsafeMutablePointer.allocate(capacity: numberOfClasses)
19 | let autoreleasingClasses = AutoreleasingUnsafeMutablePointer(classesPtr)
20 | let count = objc_getClassList(autoreleasingClasses, Int32(numberOfClasses))
21 | assert(numberOfClasses == count, "")
22 | defer { classesPtr.deallocate() }
23 | return (0 ..< numberOfClasses).map { classesPtr[$0] }
24 | }
25 | return []
26 | }
27 |
28 | /// 获取指定类所有子类,注意会返回指定类自身
29 | ///
30 | /// - Parameter class: 指定类
31 | /// - Returns: 所有子类
32 | public static func subclasses(of class: AnyClass) -> [AnyClass] {
33 | return allClasses.filter {
34 | var ancestor: AnyClass? = $0
35 | while let type = ancestor {
36 | if ObjectIdentifier(type) == ObjectIdentifier(`class`) {
37 | return true
38 | }
39 | ancestor = class_getSuperclass(type)
40 | }
41 | return false
42 | }
43 | }
44 |
45 | /// 判断一个类是不是另一个类的子类
46 | ///
47 | /// - Parameters:
48 | /// - subclass: 子类
49 | /// - superclass: 父类
50 | /// - Returns: 结果
51 | public static func isSubclass(_ subclass: AnyClass, superclass: AnyClass) -> Bool {
52 | var eachSubclass: AnyClass = subclass
53 | while let eachSuperclass: AnyClass = class_getSuperclass(eachSubclass) {
54 | if ObjectIdentifier(eachSuperclass) == ObjectIdentifier(superclass) {
55 | return true
56 | }
57 | eachSubclass = eachSuperclass
58 | }
59 | return false
60 | }
61 |
62 | /// 获取指定类所遵循的协议
63 | public static func getProtocolArray(_ clazz: AnyClass) -> [Protocol] {
64 | var protocolCount: UInt32 = 0
65 | let protocolList = class_copyProtocolList(clazz, &protocolCount)
66 | return (0 ..< protocolCount).compactMap { protocolList?[Int($0)] }
67 | }
68 |
69 | /// 获取指定类所遵循的协议名称
70 | public static func getProtocolStringArray(_ clazz: AnyClass) -> [String] {
71 | var protocolCount: UInt32 = 0
72 | let protocolList = class_copyProtocolList(clazz, &protocolCount)
73 | return (0 ..< protocolCount).compactMap {
74 | guard let protocolInfo = protocolList?[Int($0)] else {
75 | return nil
76 | }
77 | return String(cString: protocol_getName(protocolInfo))
78 | }
79 | }
80 | }
81 |
82 | // MARK: - 协议相关
83 |
84 | extension RuntimeUtils {
85 | /// 判断一个类是否符合一个协议
86 | ///
87 | /// - Parameters:
88 | /// - baseclass: 类
89 | /// - baseProtocol: 协议
90 | /// - Returns: 是否符合
91 | public static func confirm(_ baseclass: AnyClass, confirm baseProtocol: Protocol) -> Bool {
92 | var eachSubclass: AnyClass = baseclass
93 | if class_conformsToProtocol(baseclass, baseProtocol) {
94 | return true
95 | }
96 | while let eachSuperclass: AnyClass = class_getSuperclass(eachSubclass) {
97 | if class_conformsToProtocol(eachSuperclass, baseProtocol) {
98 | return true
99 | }
100 | eachSubclass = eachSuperclass
101 | }
102 | return false
103 | }
104 |
105 | /// 获取实现某协议的所有类
106 | ///
107 | /// - Parameter baseProtocol: 协议
108 | /// - Returns: 类数组
109 | public static func getAllClasses(confirm baseProtocol: Protocol) -> [AnyClass] {
110 | var proResult = [AnyClass]()
111 | for item in allClasses {
112 | // 这个判断是防止 oc调用 该方式时出现`methodSignatureForSelector`以及`doesNotRecognizeSelector` does not implement问题
113 | // if class_getInstanceMethod(item, NSSelectorFromString("methodSignatureForSelector:")) != nil,
114 | // class_getInstanceMethod(item, NSSelectorFromString("doesNotRecognizeSelector:")) != nil {
115 | // }
116 |
117 | if confirm(item, confirm: baseProtocol) {
118 | proResult.append(item)
119 | }
120 | }
121 | return proResult
122 | }
123 | }
124 |
125 | // MARK: - 方法相关
126 |
127 | extension RuntimeUtils {}
128 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Util/SwiftDemangle.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftDemangle.swift
3 | // LTXiOSUtilsDemo
4 | //
5 | // Created by CoderStar on 2022/2/3.
6 | //
7 | import Foundation
8 |
9 | @_silgen_name("swift_demangle")
10 | private func stdlibDemangleImpl(
11 | mangledName: UnsafePointer?,
12 | mangledNameLength: UInt,
13 | outputBuffer: UnsafeMutablePointer?,
14 | outputBufferSize: UnsafeMutablePointer?,
15 | flags: UInt32
16 | ) -> UnsafeMutablePointer?
17 |
18 | public struct SwiftDemangle {
19 | /// 对Swift函数名进行demangle
20 | /// - Parameter mangledName: 函数名
21 | /// - Returns: demangle后的函数名
22 | public static func getDemangleName(_ mangledName: String) -> String {
23 | return mangledName.utf8CString.withUnsafeBufferPointer { mangledNameUTF8CStr in
24 | let demangledNamePtr = stdlibDemangleImpl(
25 | mangledName: mangledNameUTF8CStr.baseAddress,
26 | mangledNameLength: UInt(mangledNameUTF8CStr.count - 1),
27 | outputBuffer: nil,
28 | outputBufferSize: nil,
29 | flags: 0
30 | )
31 |
32 | if let demangledNamePtr = demangledNamePtr {
33 | let demangledName = String(cString: demangledNamePtr)
34 | free(demangledNamePtr)
35 | return demangledName
36 | }
37 | return mangledName
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Util/UIFeedbackGeneratorUtils.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIFeedbackGeneratorUtils.swift
3 | // LTXiOSUtils
4 | // 触感反馈工具类
5 | // Created by CoderStar on 2020/3/17.
6 | // Copyright © 2020 CoderStar. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 |
12 | /// 触感反馈类型
13 | public enum FeedbackType: Int {
14 | /// 轻
15 | case light
16 | /// 中
17 | case medium
18 | /// 重
19 | case heavy
20 |
21 | /// 成功
22 | case success
23 | /// 警告
24 | case warning
25 | /// 失败
26 | case error
27 |
28 | /// 变化,选择时使用
29 | /// 如时间选择
30 | case change
31 | }
32 |
33 | /// 触感反馈工具类
34 | public struct UIFeedbackGeneratorUtils {
35 | /// 触感反馈
36 | /// - Parameter style: 触感反馈类型
37 | public static func impactFeedback(style: FeedbackType) {
38 | if #available(iOS 10.0, *) {
39 | switch style {
40 | case .light:
41 | let generator = UIImpactFeedbackGenerator(style: .light)
42 | generator.impactOccurred()
43 | case .medium:
44 | let generator = UIImpactFeedbackGenerator(style: .medium)
45 | generator.impactOccurred()
46 | case .heavy:
47 | let generator = UIImpactFeedbackGenerator(style: .heavy)
48 | generator.impactOccurred()
49 | case .success:
50 | let generator = UINotificationFeedbackGenerator()
51 | generator.notificationOccurred(.success)
52 | case .warning:
53 | let generator = UINotificationFeedbackGenerator()
54 | generator.notificationOccurred(.warning)
55 | case .error:
56 | let generator = UINotificationFeedbackGenerator()
57 | generator.notificationOccurred(.error)
58 | case .change:
59 | let generator = UISelectionFeedbackGenerator()
60 | generator.selectionChanged()
61 | }
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Util/WeakProxy.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WeakProxy.swift
3 | // LTXiOSUtils
4 | // 用在Timer等场景,防止循环引用
5 | // Created by CoderStar on 2021/7/27.
6 | //
7 |
8 | import Foundation
9 |
10 | final public class WeakProxy: NSObject {
11 | private weak var target: NSObjectProtocol?
12 |
13 | public init(_ target: NSObjectProtocol?) {
14 | self.target = target
15 | super.init()
16 | }
17 |
18 | public class func proxy(with target: NSObjectProtocol?) -> WeakProxy {
19 | return WeakProxy(target)
20 | }
21 | }
22 |
23 | extension WeakProxy {
24 | // 消息转发
25 | public override func forwardingTarget(for aSelector: Selector!) -> Any? {
26 | // 判断是否实现了Selector,如果实现了,就将消息转发给它
27 | if target?.responds(to: aSelector) == true {
28 | return target
29 | } else {
30 | return super.forwardingTarget(for: aSelector)
31 | }
32 | }
33 |
34 | public override func responds(to aSelector: Selector!) -> Bool {
35 | return target?.responds(to: aSelector) == true
36 | }
37 |
38 | public override func conforms(to aProtocol: Protocol) -> Bool {
39 | return target?.conforms(to: aProtocol) == true
40 | }
41 |
42 | public override func isEqual(_ object: Any?) -> Bool {
43 | return target?.isEqual(object) == true
44 | }
45 |
46 | public override var superclass: AnyClass? {
47 | return target?.superclass
48 | }
49 |
50 | public override func isKind(of aClass: AnyClass) -> Bool {
51 | return target?.isKind(of: aClass) == true
52 | }
53 |
54 | public override func isMember(of aClass: AnyClass) -> Bool {
55 | return target?.isMember(of: aClass) == true
56 | }
57 |
58 | public override var description: String {
59 | return target?.description ?? ""
60 | }
61 |
62 | public override var debugDescription: String {
63 | return target?.debugDescription ?? ""
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/Sources/LTXiOSUtils/Classes/Util/WeakWKScriptMessageHandler.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WeakWKScriptMessageHandler.swift
3 | // LTXiOSUtils
4 | // 解决WKUserContentController add方法 循环引用问题
5 | // Created by CoderStar on 2021/11/23.
6 | //
7 |
8 | import WebKit
9 |
10 | /**
11 | 使用例子(两者皆可):
12 | 1、WKUserContentController().weakAdd(self, name: "")
13 | 2、WKUserContentController().add(WeakWKScriptMessageHandler(self), name: "")
14 | */
15 |
16 | final public class WeakWKScriptMessageHandler: NSObject {
17 | private weak var weakScriptMessageHandler: WKScriptMessageHandler?
18 |
19 | public init(_ weakScriptMessageHandler: WKScriptMessageHandler) {
20 | super.init()
21 | self.weakScriptMessageHandler = weakScriptMessageHandler
22 | }
23 | }
24 |
25 | extension WeakWKScriptMessageHandler: WKScriptMessageHandler {
26 | public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
27 | weakScriptMessageHandler?.userContentController(userContentController, didReceive: message)
28 | }
29 | }
30 |
31 | extension WKUserContentController {
32 | open func weakAdd(_ scriptMessageHandler: WKScriptMessageHandler, name: String) {
33 | add(WeakWKScriptMessageHandler(scriptMessageHandler), name: name)
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/docs/badge.svg:
--------------------------------------------------------------------------------
1 |
29 |
--------------------------------------------------------------------------------
/docs/css/highlight.css:
--------------------------------------------------------------------------------
1 | /* Credit to https://gist.github.com/wataru420/2048287 */
2 | .highlight {
3 | /* Comment */
4 | /* Error */
5 | /* Keyword */
6 | /* Operator */
7 | /* Comment.Multiline */
8 | /* Comment.Preproc */
9 | /* Comment.Single */
10 | /* Comment.Special */
11 | /* Generic.Deleted */
12 | /* Generic.Deleted.Specific */
13 | /* Generic.Emph */
14 | /* Generic.Error */
15 | /* Generic.Heading */
16 | /* Generic.Inserted */
17 | /* Generic.Inserted.Specific */
18 | /* Generic.Output */
19 | /* Generic.Prompt */
20 | /* Generic.Strong */
21 | /* Generic.Subheading */
22 | /* Generic.Traceback */
23 | /* Keyword.Constant */
24 | /* Keyword.Declaration */
25 | /* Keyword.Pseudo */
26 | /* Keyword.Reserved */
27 | /* Keyword.Type */
28 | /* Literal.Number */
29 | /* Literal.String */
30 | /* Name.Attribute */
31 | /* Name.Builtin */
32 | /* Name.Class */
33 | /* Name.Constant */
34 | /* Name.Entity */
35 | /* Name.Exception */
36 | /* Name.Function */
37 | /* Name.Namespace */
38 | /* Name.Tag */
39 | /* Name.Variable */
40 | /* Operator.Word */
41 | /* Text.Whitespace */
42 | /* Literal.Number.Float */
43 | /* Literal.Number.Hex */
44 | /* Literal.Number.Integer */
45 | /* Literal.Number.Oct */
46 | /* Literal.String.Backtick */
47 | /* Literal.String.Char */
48 | /* Literal.String.Doc */
49 | /* Literal.String.Double */
50 | /* Literal.String.Escape */
51 | /* Literal.String.Heredoc */
52 | /* Literal.String.Interpol */
53 | /* Literal.String.Other */
54 | /* Literal.String.Regex */
55 | /* Literal.String.Single */
56 | /* Literal.String.Symbol */
57 | /* Name.Builtin.Pseudo */
58 | /* Name.Variable.Class */
59 | /* Name.Variable.Global */
60 | /* Name.Variable.Instance */
61 | /* Literal.Number.Integer.Long */ }
62 | .highlight .c {
63 | color: #999988;
64 | font-style: italic; }
65 | .highlight .err {
66 | color: #a61717;
67 | background-color: #e3d2d2; }
68 | .highlight .k {
69 | color: #000000;
70 | font-weight: bold; }
71 | .highlight .o {
72 | color: #000000;
73 | font-weight: bold; }
74 | .highlight .cm {
75 | color: #999988;
76 | font-style: italic; }
77 | .highlight .cp {
78 | color: #999999;
79 | font-weight: bold; }
80 | .highlight .c1 {
81 | color: #999988;
82 | font-style: italic; }
83 | .highlight .cs {
84 | color: #999999;
85 | font-weight: bold;
86 | font-style: italic; }
87 | .highlight .gd {
88 | color: #000000;
89 | background-color: #ffdddd; }
90 | .highlight .gd .x {
91 | color: #000000;
92 | background-color: #ffaaaa; }
93 | .highlight .ge {
94 | color: #000000;
95 | font-style: italic; }
96 | .highlight .gr {
97 | color: #aa0000; }
98 | .highlight .gh {
99 | color: #999999; }
100 | .highlight .gi {
101 | color: #000000;
102 | background-color: #ddffdd; }
103 | .highlight .gi .x {
104 | color: #000000;
105 | background-color: #aaffaa; }
106 | .highlight .go {
107 | color: #888888; }
108 | .highlight .gp {
109 | color: #555555; }
110 | .highlight .gs {
111 | font-weight: bold; }
112 | .highlight .gu {
113 | color: #aaaaaa; }
114 | .highlight .gt {
115 | color: #aa0000; }
116 | .highlight .kc {
117 | color: #000000;
118 | font-weight: bold; }
119 | .highlight .kd {
120 | color: #000000;
121 | font-weight: bold; }
122 | .highlight .kp {
123 | color: #000000;
124 | font-weight: bold; }
125 | .highlight .kr {
126 | color: #000000;
127 | font-weight: bold; }
128 | .highlight .kt {
129 | color: #445588; }
130 | .highlight .m {
131 | color: #009999; }
132 | .highlight .s {
133 | color: #d14; }
134 | .highlight .na {
135 | color: #008080; }
136 | .highlight .nb {
137 | color: #0086B3; }
138 | .highlight .nc {
139 | color: #445588;
140 | font-weight: bold; }
141 | .highlight .no {
142 | color: #008080; }
143 | .highlight .ni {
144 | color: #800080; }
145 | .highlight .ne {
146 | color: #990000;
147 | font-weight: bold; }
148 | .highlight .nf {
149 | color: #990000; }
150 | .highlight .nn {
151 | color: #555555; }
152 | .highlight .nt {
153 | color: #000080; }
154 | .highlight .nv {
155 | color: #008080; }
156 | .highlight .ow {
157 | color: #000000;
158 | font-weight: bold; }
159 | .highlight .w {
160 | color: #bbbbbb; }
161 | .highlight .mf {
162 | color: #009999; }
163 | .highlight .mh {
164 | color: #009999; }
165 | .highlight .mi {
166 | color: #009999; }
167 | .highlight .mo {
168 | color: #009999; }
169 | .highlight .sb {
170 | color: #d14; }
171 | .highlight .sc {
172 | color: #d14; }
173 | .highlight .sd {
174 | color: #d14; }
175 | .highlight .s2 {
176 | color: #d14; }
177 | .highlight .se {
178 | color: #d14; }
179 | .highlight .sh {
180 | color: #d14; }
181 | .highlight .si {
182 | color: #d14; }
183 | .highlight .sx {
184 | color: #d14; }
185 | .highlight .sr {
186 | color: #009926; }
187 | .highlight .s1 {
188 | color: #d14; }
189 | .highlight .ss {
190 | color: #990073; }
191 | .highlight .bp {
192 | color: #999999; }
193 | .highlight .vc {
194 | color: #008080; }
195 | .highlight .vg {
196 | color: #008080; }
197 | .highlight .vi {
198 | color: #008080; }
199 | .highlight .il {
200 | color: #009999; }
201 |
--------------------------------------------------------------------------------
/docs/docsets/LTXiOSUtils.docset/Contents/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleIdentifier
6 | com.jazzy.ltxiosutils
7 | CFBundleName
8 | LTXiOSUtils
9 | DocSetPlatformFamily
10 | ltxiosutils
11 | isDashDocset
12 |
13 | dashIndexFilePath
14 | index.html
15 | isJavaScriptEnabled
16 |
17 | DashDocSetFamily
18 | dashtoc
19 |
20 |
21 |
--------------------------------------------------------------------------------
/docs/docsets/LTXiOSUtils.docset/Contents/Resources/Documents/css/highlight.css:
--------------------------------------------------------------------------------
1 | /* Credit to https://gist.github.com/wataru420/2048287 */
2 | .highlight {
3 | /* Comment */
4 | /* Error */
5 | /* Keyword */
6 | /* Operator */
7 | /* Comment.Multiline */
8 | /* Comment.Preproc */
9 | /* Comment.Single */
10 | /* Comment.Special */
11 | /* Generic.Deleted */
12 | /* Generic.Deleted.Specific */
13 | /* Generic.Emph */
14 | /* Generic.Error */
15 | /* Generic.Heading */
16 | /* Generic.Inserted */
17 | /* Generic.Inserted.Specific */
18 | /* Generic.Output */
19 | /* Generic.Prompt */
20 | /* Generic.Strong */
21 | /* Generic.Subheading */
22 | /* Generic.Traceback */
23 | /* Keyword.Constant */
24 | /* Keyword.Declaration */
25 | /* Keyword.Pseudo */
26 | /* Keyword.Reserved */
27 | /* Keyword.Type */
28 | /* Literal.Number */
29 | /* Literal.String */
30 | /* Name.Attribute */
31 | /* Name.Builtin */
32 | /* Name.Class */
33 | /* Name.Constant */
34 | /* Name.Entity */
35 | /* Name.Exception */
36 | /* Name.Function */
37 | /* Name.Namespace */
38 | /* Name.Tag */
39 | /* Name.Variable */
40 | /* Operator.Word */
41 | /* Text.Whitespace */
42 | /* Literal.Number.Float */
43 | /* Literal.Number.Hex */
44 | /* Literal.Number.Integer */
45 | /* Literal.Number.Oct */
46 | /* Literal.String.Backtick */
47 | /* Literal.String.Char */
48 | /* Literal.String.Doc */
49 | /* Literal.String.Double */
50 | /* Literal.String.Escape */
51 | /* Literal.String.Heredoc */
52 | /* Literal.String.Interpol */
53 | /* Literal.String.Other */
54 | /* Literal.String.Regex */
55 | /* Literal.String.Single */
56 | /* Literal.String.Symbol */
57 | /* Name.Builtin.Pseudo */
58 | /* Name.Variable.Class */
59 | /* Name.Variable.Global */
60 | /* Name.Variable.Instance */
61 | /* Literal.Number.Integer.Long */ }
62 | .highlight .c {
63 | color: #999988;
64 | font-style: italic; }
65 | .highlight .err {
66 | color: #a61717;
67 | background-color: #e3d2d2; }
68 | .highlight .k {
69 | color: #000000;
70 | font-weight: bold; }
71 | .highlight .o {
72 | color: #000000;
73 | font-weight: bold; }
74 | .highlight .cm {
75 | color: #999988;
76 | font-style: italic; }
77 | .highlight .cp {
78 | color: #999999;
79 | font-weight: bold; }
80 | .highlight .c1 {
81 | color: #999988;
82 | font-style: italic; }
83 | .highlight .cs {
84 | color: #999999;
85 | font-weight: bold;
86 | font-style: italic; }
87 | .highlight .gd {
88 | color: #000000;
89 | background-color: #ffdddd; }
90 | .highlight .gd .x {
91 | color: #000000;
92 | background-color: #ffaaaa; }
93 | .highlight .ge {
94 | color: #000000;
95 | font-style: italic; }
96 | .highlight .gr {
97 | color: #aa0000; }
98 | .highlight .gh {
99 | color: #999999; }
100 | .highlight .gi {
101 | color: #000000;
102 | background-color: #ddffdd; }
103 | .highlight .gi .x {
104 | color: #000000;
105 | background-color: #aaffaa; }
106 | .highlight .go {
107 | color: #888888; }
108 | .highlight .gp {
109 | color: #555555; }
110 | .highlight .gs {
111 | font-weight: bold; }
112 | .highlight .gu {
113 | color: #aaaaaa; }
114 | .highlight .gt {
115 | color: #aa0000; }
116 | .highlight .kc {
117 | color: #000000;
118 | font-weight: bold; }
119 | .highlight .kd {
120 | color: #000000;
121 | font-weight: bold; }
122 | .highlight .kp {
123 | color: #000000;
124 | font-weight: bold; }
125 | .highlight .kr {
126 | color: #000000;
127 | font-weight: bold; }
128 | .highlight .kt {
129 | color: #445588; }
130 | .highlight .m {
131 | color: #009999; }
132 | .highlight .s {
133 | color: #d14; }
134 | .highlight .na {
135 | color: #008080; }
136 | .highlight .nb {
137 | color: #0086B3; }
138 | .highlight .nc {
139 | color: #445588;
140 | font-weight: bold; }
141 | .highlight .no {
142 | color: #008080; }
143 | .highlight .ni {
144 | color: #800080; }
145 | .highlight .ne {
146 | color: #990000;
147 | font-weight: bold; }
148 | .highlight .nf {
149 | color: #990000; }
150 | .highlight .nn {
151 | color: #555555; }
152 | .highlight .nt {
153 | color: #000080; }
154 | .highlight .nv {
155 | color: #008080; }
156 | .highlight .ow {
157 | color: #000000;
158 | font-weight: bold; }
159 | .highlight .w {
160 | color: #bbbbbb; }
161 | .highlight .mf {
162 | color: #009999; }
163 | .highlight .mh {
164 | color: #009999; }
165 | .highlight .mi {
166 | color: #009999; }
167 | .highlight .mo {
168 | color: #009999; }
169 | .highlight .sb {
170 | color: #d14; }
171 | .highlight .sc {
172 | color: #d14; }
173 | .highlight .sd {
174 | color: #d14; }
175 | .highlight .s2 {
176 | color: #d14; }
177 | .highlight .se {
178 | color: #d14; }
179 | .highlight .sh {
180 | color: #d14; }
181 | .highlight .si {
182 | color: #d14; }
183 | .highlight .sx {
184 | color: #d14; }
185 | .highlight .sr {
186 | color: #009926; }
187 | .highlight .s1 {
188 | color: #d14; }
189 | .highlight .ss {
190 | color: #990073; }
191 | .highlight .bp {
192 | color: #999999; }
193 | .highlight .vc {
194 | color: #008080; }
195 | .highlight .vg {
196 | color: #008080; }
197 | .highlight .vi {
198 | color: #008080; }
199 | .highlight .il {
200 | color: #009999; }
201 |
--------------------------------------------------------------------------------
/docs/docsets/LTXiOSUtils.docset/Contents/Resources/Documents/img/carat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Coder-Star/LTXiOSUtils/8ede2e39e3d20ae396f3cadf8799665e085329e9/docs/docsets/LTXiOSUtils.docset/Contents/Resources/Documents/img/carat.png
--------------------------------------------------------------------------------
/docs/docsets/LTXiOSUtils.docset/Contents/Resources/Documents/img/dash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Coder-Star/LTXiOSUtils/8ede2e39e3d20ae396f3cadf8799665e085329e9/docs/docsets/LTXiOSUtils.docset/Contents/Resources/Documents/img/dash.png
--------------------------------------------------------------------------------
/docs/docsets/LTXiOSUtils.docset/Contents/Resources/Documents/img/gh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Coder-Star/LTXiOSUtils/8ede2e39e3d20ae396f3cadf8799665e085329e9/docs/docsets/LTXiOSUtils.docset/Contents/Resources/Documents/img/gh.png
--------------------------------------------------------------------------------
/docs/docsets/LTXiOSUtils.docset/Contents/Resources/Documents/js/jazzy.js:
--------------------------------------------------------------------------------
1 | window.jazzy = {'docset': false};
2 |
3 | if (typeof window.dash != 'undefined') {
4 | document.documentElement.className += ' dash';
5 | window.jazzy.docset = true;
6 | }
7 |
8 | if (navigator.userAgent.match(/xcode/i)) {
9 | document.documentElement.className += ' xcode';
10 | window.jazzy.docset = true;
11 | }
12 |
13 | if (!window.jazzy.docset) {
14 | $(function() {
15 | var filename = window.location.pathname.split('/').pop();
16 | $('a[href="Documentation.html"]').attr('href', 'index.html');
17 | $('.navigation a[href$="/' + filename + '"]').addClass('current-page');
18 | $('.navigation a[href="' + filename + '"]').addClass('current-page');
19 | });
20 | }
21 |
--------------------------------------------------------------------------------
/docs/docsets/LTXiOSUtils.docset/Contents/Resources/docSet.dsidx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Coder-Star/LTXiOSUtils/8ede2e39e3d20ae396f3cadf8799665e085329e9/docs/docsets/LTXiOSUtils.docset/Contents/Resources/docSet.dsidx
--------------------------------------------------------------------------------
/docs/docsets/LTXiOSUtils.tgz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Coder-Star/LTXiOSUtils/8ede2e39e3d20ae396f3cadf8799665e085329e9/docs/docsets/LTXiOSUtils.tgz
--------------------------------------------------------------------------------
/docs/img/carat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Coder-Star/LTXiOSUtils/8ede2e39e3d20ae396f3cadf8799665e085329e9/docs/img/carat.png
--------------------------------------------------------------------------------
/docs/img/dash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Coder-Star/LTXiOSUtils/8ede2e39e3d20ae396f3cadf8799665e085329e9/docs/img/dash.png
--------------------------------------------------------------------------------
/docs/img/gh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Coder-Star/LTXiOSUtils/8ede2e39e3d20ae396f3cadf8799665e085329e9/docs/img/gh.png
--------------------------------------------------------------------------------
/docs/js/jazzy.js:
--------------------------------------------------------------------------------
1 | window.jazzy = {'docset': false};
2 |
3 | if (typeof window.dash != 'undefined') {
4 | document.documentElement.className += ' dash';
5 | window.jazzy.docset = true;
6 | }
7 |
8 | if (navigator.userAgent.match(/xcode/i)) {
9 | document.documentElement.className += ' xcode';
10 | window.jazzy.docset = true;
11 | }
12 |
13 | if (!window.jazzy.docset) {
14 | $(function() {
15 | var filename = window.location.pathname.split('/').pop();
16 | $('a[href="Documentation.html"]').attr('href', 'index.html');
17 | $('.navigation a[href$="/' + filename + '"]').addClass('current-page');
18 | $('.navigation a[href="' + filename + '"]').addClass('current-page');
19 | });
20 | }
21 |
--------------------------------------------------------------------------------