Merge branch 'master' into feature/deeplink_api
# Conflicts: # LeadKit.podspec # Package.swift # TIAppleMapUtils/TIAppleMapUtils.podspec # TIAuth/TIAuth.podspec # TIDeveloperUtils/TIDeveloperUtils.podspec # TIEcommerce/TIEcommerce.podspec # TIFoundationUtils/TIFoundationUtils.podspec # TIGoogleMapUtils/TIGoogleMapUtils.podspec # TIKeychainUtils/TIKeychainUtils.podspec # TIMapUtils/TIMapUtils.podspec # TIMoyaNetworking/TIMoyaNetworking.podspec # TINetworking/TINetworking.podspec # TINetworkingCache/TINetworkingCache.podspec # TIPagination/TIPagination.podspec # TISwiftUICore/TISwiftUICore.podspec # TISwiftUtils/TISwiftUtils.podspec # TITableKitUtils/TITableKitUtils.podspec # TITransitions/TITransitions.podspec # TIUIElements/TIUIElements.podspec # TIUIKitCore/TIUIKitCore.podspec # TIYandexMapUtils/TIYandexMapUtils.podspec # project-scripts/push_to_podspecs.sh
This commit is contained in:
commit
c8985cde1e
|
|
@ -1,3 +1,3 @@
|
|||
[submodule "build-scripts"]
|
||||
path = build-scripts
|
||||
url = https://github.com/TouchInstinct/BuildScripts.git
|
||||
url = https://gitlab.ti/touchinstinct/BuildScripts.git
|
||||
|
|
|
|||
54
CHANGELOG.md
54
CHANGELOG.md
|
|
@ -1,5 +1,59 @@
|
|||
# Changelog
|
||||
|
||||
### 1.39.0
|
||||
|
||||
- **Added**: UIButton Appearance model
|
||||
- **Added**: `SpacedWrappedViewLayout` for spacing configurations
|
||||
- **Update**: UIView appearance model with border configurations
|
||||
|
||||
### 1.38.0
|
||||
|
||||
- **Added**: Placemarks states for icon updating
|
||||
- **Added**: Selecting / deselecting markers through cluster manager
|
||||
|
||||
### 1.37.0
|
||||
|
||||
- **Added**: API for converting view hierarchy to skeletons
|
||||
|
||||
### 1.36.1
|
||||
|
||||
- **Update**: `YandexMapsMobile` version updated
|
||||
- **Fix**: Map manager memory leak removed
|
||||
|
||||
### 1.36.0
|
||||
|
||||
- **Removed**: `TILogger`module
|
||||
- **Updated**: moved `LoggingPresenter` to `TIDeveloperUtils` module.
|
||||
|
||||
### 1.35.1
|
||||
|
||||
- **Added**: Auto documentation generation for `TIFoundationUtils` playground and compile checks for playground before release
|
||||
- **Updated**: `AsyncOperation` fixed ordering of chain operations execution
|
||||
|
||||
### 1.35.0
|
||||
|
||||
- **Added**: `TIDeveloperUtils` framework, that contains different utils for development
|
||||
- **Added**: `UIView` and `UIViewController` extensions for showing SwiftUI previews
|
||||
- **Added**: `DashedBoundsLayer` for debugging views' frames visually
|
||||
|
||||
### 1.34.0
|
||||
|
||||
- **Added**: `BaseListItemView` for displaying three views horizontally
|
||||
- **Added**: `DefaultTitleSubtitleView` for displaying one or two labels vertically
|
||||
- **Update**: `StatefulButton` now can be configured with `ViewAppearance` model for each state
|
||||
|
||||
### 1.33.0
|
||||
|
||||
- **Added**: `ViewAppearance` and `ViewLayout` models for setting up Views' appearance and layout
|
||||
- **Added**: `TableKit.Row` extension for configuration inner View's appearance and layout
|
||||
- **Added**: `WrappableView` with typealiases for creating wrapped in the container views
|
||||
- **Added**: `CollectionTableViewCell` and `ContainerView`
|
||||
- **Update**: Separator appearance configureation for table views
|
||||
|
||||
### 1.32.0
|
||||
|
||||
- **Added**: `BaseInitializableWebView` with navigation and error handling api.
|
||||
|
||||
### 1.31.0
|
||||
|
||||
- **Added**: `URLInteractiveTextView` for terms and conditions hints in login flow
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = "LeadKit"
|
||||
s.version = "1.33.0"
|
||||
s.version = "1.35.0"
|
||||
s.summary = "iOS framework with a bunch of tools for rapid development"
|
||||
s.homepage = "https://github.com/TouchInstinct/LeadKit"
|
||||
s.homepage = "https://gitlab.ti/touchinstinct/LeadKit"
|
||||
s.license = "Apache License, Version 2.0"
|
||||
s.author = "Touch Instinct"
|
||||
s.source = { :git => "https://github.com/TouchInstinct/LeadKit.git", :tag => s.version }
|
||||
s.source = { :git => "https://gitlab.ti/touchinstinct/LeadKit.git", :tag => s.version }
|
||||
s.platform = :ios, '10.0'
|
||||
s.swift_versions = ['5.1']
|
||||
|
||||
|
|
|
|||
150
Package.resolved
150
Package.resolved
|
|
@ -1,79 +1,77 @@
|
|||
{
|
||||
"object": {
|
||||
"pins": [
|
||||
{
|
||||
"package": "Alamofire",
|
||||
"repositoryURL": "https://github.com/Alamofire/Alamofire.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "f96b619bcb2383b43d898402283924b80e2c4bae",
|
||||
"version": "5.4.3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "Cache",
|
||||
"repositoryURL": "https://github.com/hyperoslo/Cache.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "c7f4d633049c3bd649a353bad36f6c17e9df085f",
|
||||
"version": "6.0.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "Cursors",
|
||||
"repositoryURL": "https://github.com/petropavel13/Cursors",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "a1561869135e72832eff3b1e729075c56c2eebf6",
|
||||
"version": "0.5.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "KeychainAccess",
|
||||
"repositoryURL": "https://github.com/kishikawakatsumi/KeychainAccess.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "84e546727d66f1adc5439debad16270d0fdd04e7",
|
||||
"version": "4.2.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "Moya",
|
||||
"repositoryURL": "https://github.com/Moya/Moya.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "9b906860e3c3c09032879465c471e6375829593f",
|
||||
"version": "15.0.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "ReactiveSwift",
|
||||
"repositoryURL": "https://github.com/ReactiveCocoa/ReactiveSwift.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "c43bae3dac73fdd3cb906bd5a1914686ca71ed3c",
|
||||
"version": "6.7.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "RxSwift",
|
||||
"repositoryURL": "https://github.com/ReactiveX/RxSwift.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "b4307ba0b6425c0ba4178e138799946c3da594f8",
|
||||
"version": "6.5.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "TableKit",
|
||||
"repositoryURL": "https://github.com/maxsokolov/TableKit.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "8bf4840d9d0475a92352f02f368f88b74eced447",
|
||||
"version": "2.11.0"
|
||||
}
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "alamofire",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/Alamofire/Alamofire.git",
|
||||
"state" : {
|
||||
"revision" : "f96b619bcb2383b43d898402283924b80e2c4bae",
|
||||
"version" : "5.4.3"
|
||||
}
|
||||
]
|
||||
},
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"identity" : "cache",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/hyperoslo/Cache.git",
|
||||
"state" : {
|
||||
"revision" : "c7f4d633049c3bd649a353bad36f6c17e9df085f",
|
||||
"version" : "6.0.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "cursors",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/petropavel13/Cursors",
|
||||
"state" : {
|
||||
"revision" : "a1561869135e72832eff3b1e729075c56c2eebf6",
|
||||
"version" : "0.5.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "keychainaccess",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/kishikawakatsumi/KeychainAccess.git",
|
||||
"state" : {
|
||||
"revision" : "84e546727d66f1adc5439debad16270d0fdd04e7",
|
||||
"version" : "4.2.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "moya",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/Moya/Moya.git",
|
||||
"state" : {
|
||||
"revision" : "9b906860e3c3c09032879465c471e6375829593f",
|
||||
"version" : "15.0.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "reactiveswift",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/ReactiveCocoa/ReactiveSwift.git",
|
||||
"state" : {
|
||||
"revision" : "c43bae3dac73fdd3cb906bd5a1914686ca71ed3c",
|
||||
"version" : "6.7.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "rxswift",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/ReactiveX/RxSwift.git",
|
||||
"state" : {
|
||||
"revision" : "b4307ba0b6425c0ba4178e138799946c3da594f8",
|
||||
"version" : "6.5.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "tablekit",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/maxsokolov/TableKit.git",
|
||||
"state" : {
|
||||
"revision" : "8bf4840d9d0475a92352f02f368f88b74eced447",
|
||||
"version" : "2.11.0"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version" : 2
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// swift-tools-version:5.1
|
||||
// swift-tools-version:5.7
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
|
|
@ -11,17 +11,18 @@ let package = Package(
|
|||
// MARK: - UIKit
|
||||
.library(name: "TIUIKitCore", targets: ["TIUIKitCore"]),
|
||||
.library(name: "TIUIElements", targets: ["TIUIElements"]),
|
||||
.library(name: "TIWebView", targets: ["TIWebView"]),
|
||||
|
||||
// MARK: - SwiftUI
|
||||
.library(name: "TISwiftUICore", targets: ["TISwiftUICore"]),
|
||||
|
||||
|
||||
// MARK: - Utils
|
||||
.library(name: "TISwiftUtils", targets: ["TISwiftUtils"]),
|
||||
.library(name: "TIFoundationUtils", targets: ["TIFoundationUtils"]),
|
||||
.library(name: "TIKeychainUtils", targets: ["TIKeychainUtils"]),
|
||||
.library(name: "TITableKitUtils", targets: ["TITableKitUtils"]),
|
||||
.library(name: "TILogging", targets: ["TILogging"]),
|
||||
.library(name: "TIDeeplink", targets: ["TIDeeplink"]),
|
||||
.library(name: "TIDeveloperUtils", targets: ["TIDeveloperUtils"]),
|
||||
|
||||
// MARK: - Networking
|
||||
|
||||
|
|
@ -39,7 +40,7 @@ let package = Package(
|
|||
.library(name: "TITransitions", targets: ["TITransitions"]),
|
||||
.library(name: "TIPagination", targets: ["TIPagination"]),
|
||||
.library(name: "TIAuth", targets: ["TIAuth"]),
|
||||
|
||||
|
||||
//MARK: - Skolkovo
|
||||
.library(name: "TIEcommerce", targets: ["TIEcommerce"])
|
||||
],
|
||||
|
|
@ -52,21 +53,22 @@ let package = Package(
|
|||
.package(url: "https://github.com/hyperoslo/Cache.git", .upToNextMajor(from: "6.0.0"))
|
||||
],
|
||||
targets: [
|
||||
|
||||
|
||||
// MARK: - UIKit
|
||||
.target(name: "TIUIKitCore", dependencies: ["TISwiftUtils"], path: "TIUIKitCore/Sources"),
|
||||
.target(name: "TIUIElements", dependencies: ["TIUIKitCore", "TISwiftUtils"], path: "TIUIElements/Sources"),
|
||||
.target(name: "TIWebView", dependencies: ["TIUIKitCore", "TISwiftUtils"], path: "TIWebView/Sources"),
|
||||
|
||||
// MARK: - SwiftUI
|
||||
.target(name: "TISwiftUICore", dependencies: ["TIUIKitCore", "TISwiftUtils"], path: "TISwiftUICore/Sources"),
|
||||
|
||||
|
||||
// MARK: - Utils
|
||||
.target(name: "TISwiftUtils", path: "TISwiftUtils/Sources"),
|
||||
.target(name: "TIFoundationUtils", dependencies: ["TISwiftUtils"], path: "TIFoundationUtils"),
|
||||
.target(name: "TIFoundationUtils", dependencies: ["TISwiftUtils"], path: "TIFoundationUtils", exclude: ["TIFoundationUtils.app"]),
|
||||
.target(name: "TIKeychainUtils", dependencies: ["TIFoundationUtils", "KeychainAccess"], path: "TIKeychainUtils/Sources"),
|
||||
.target(name: "TITableKitUtils", dependencies: ["TIUIElements", "TableKit"], path: "TITableKitUtils/Sources"),
|
||||
.target(name: "TILogging", dependencies: ["TIUIElements", "TISwiftUtils", "TIUIKitCore"], path: "TILogging/Sources"),
|
||||
.target(name: "TIDeeplink", dependencies: ["TIFoundationUtils"], path: "TIDeeplink/Sources"),
|
||||
.target(name: "TIDeveloperUtils", dependencies: ["TISwiftUtils", "TIUIKitCore", "TIUIElements"], path: "TIDeveloperUtils/Sources"),
|
||||
|
||||
// MARK: - Networking
|
||||
.target(name: "TINetworking", dependencies: ["TIFoundationUtils", "Alamofire"], path: "TINetworking/Sources"),
|
||||
|
|
@ -83,9 +85,9 @@ let package = Package(
|
|||
.target(name: "TIPagination", dependencies: ["Cursors", "TISwiftUtils"], path: "TIPagination/Sources"),
|
||||
.target(name: "TIAuth", dependencies: ["TIFoundationUtils", "TIUIKitCore", "KeychainAccess"], path: "TIAuth/Sources"),
|
||||
.target(name: "TIEcommerce", dependencies: ["TIFoundationUtils", "TISwiftUtils", "TINetworking", "TIUIKitCore", "TIUIElements"], path: "TIEcommerce/Sources"),
|
||||
|
||||
|
||||
// MARK: - Tests
|
||||
|
||||
|
||||
.testTarget(
|
||||
name: "TITimerTests",
|
||||
dependencies: ["TIFoundationUtils"],
|
||||
|
|
|
|||
73
README.md
73
README.md
|
|
@ -20,8 +20,71 @@ This repository contains the following frameworks:
|
|||
- [TIYandexMapUtils](TIYandexMapUtils) - set of helpers for map objects clustering and interacting using Yandex Maps SDK.
|
||||
- [TIAuth](TIAuth) - login, registration, confirmation and other related actions
|
||||
|
||||
Useful docs:
|
||||
## Playgrounds
|
||||
|
||||
### Create new Playground
|
||||
|
||||
```sh
|
||||
cd TIModuleName
|
||||
nef playground --name TIModuleName --cocoapods --custom-podfile PlaygroundPodfile
|
||||
```
|
||||
See example of `PlaygroundPodfile` in `TIFoundationUtils`
|
||||
|
||||
|
||||
### Rename/add pages to Playground
|
||||
|
||||
For every new feature in module create new Playground page with documentation in comments. See [nef markdown documentation](https://github.com/bow-swift/nef#-generating-a-markdown-project).
|
||||
|
||||
### Create symlink to nef playground
|
||||
|
||||
```sh
|
||||
cd TIModuleName
|
||||
ln -s TIModuleName.app/Contents/MacOS/TIModuleName.playground TIModuleName.playground
|
||||
```
|
||||
|
||||
### Add nef files to TIModuleName.app/.gitignore
|
||||
|
||||
```
|
||||
# gitignore nef files
|
||||
**/build/
|
||||
**/nef/
|
||||
LICENSE
|
||||
```
|
||||
|
||||
### Add new playground to pre release script
|
||||
|
||||
`project-scripts/gen_docs_from_playgrounds.sh`:
|
||||
|
||||
```sh
|
||||
PLAYGROUNDS="${SRCROOT}/TIFoundationUtils/TIFoundationUtils.app
|
||||
${SRCROOT}/TIModuleName/TIModuleName.app"
|
||||
```
|
||||
|
||||
### Exclude .app bundles from package sources
|
||||
|
||||
#### SPM
|
||||
|
||||
```swift
|
||||
.target(name: "TIModuleName", dependencies: ..., path: ..., exclude: ["TIModuleName.app"]),
|
||||
```
|
||||
|
||||
#### Podspec
|
||||
|
||||
```ruby
|
||||
sources = 'your_sources_expression'
|
||||
if ENV["DEVELOPMENT_INSTALL"] # installing using :path =>
|
||||
s.source_files = sources
|
||||
s.exclude_files = s.name + '.app'
|
||||
else
|
||||
s.source_files = s.name + '/' + sources
|
||||
s.exclude_files = s.name + '/*.app'
|
||||
end
|
||||
```
|
||||
|
||||
## Docs:
|
||||
|
||||
- [TIFoundationUtils](docs/tifoundationutils)
|
||||
* [AsyncOperation](docs/tifoundationutils/asyncoperation.md)
|
||||
- [Semantic Commit Messages](docs/semantic-commit-messages.md) - commit message codestyle.
|
||||
- [Snippets](docs/snippets.md) - useful commands and scripts for development.
|
||||
|
||||
|
|
@ -32,7 +95,7 @@ Useful docs:
|
|||
./setup
|
||||
```
|
||||
|
||||
- If legacy [Source](https://github.com/TouchInstinct/LeadKit/tree/master/Sources) folder needed, [build dependencies for LeadKit.xcodeproj](https://github.com/TouchInstinct/LeadKit/blob/master/docs/snippets.md#build-dependencies-for-LeadKit.xcodeproj).
|
||||
- If legacy [Source](https://gitlab.ti/touchinstinct/LeadKit/tree/master/Sources) folder needed, [build dependencies for LeadKit.xcodeproj](https://gitlab.ti/touchinstinct/LeadKit/blob/master/docs/snippets.md#build-dependencies-for-LeadKit.xcodeproj).
|
||||
|
||||
- Make sure the commit message codestyle is followed. More about [Semantic Commit Messages](docs/semantic-commit-messages.md).
|
||||
|
||||
|
|
@ -42,14 +105,14 @@ Useful docs:
|
|||
|
||||
```swift
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/TouchInstinct/LeadKit.git", from: "x.y.z"),
|
||||
.package(url: "https://gitlab.ti/touchinstinct/LeadKit.git", from: "x.y.z"),
|
||||
],
|
||||
```
|
||||
|
||||
### Cocoapods
|
||||
|
||||
```ruby
|
||||
source 'https://github.com/TouchInstinct/Podspecs.git'
|
||||
source 'https://gitlab.ti/touchinstinct/Podspecs.git'
|
||||
|
||||
pod 'TISwiftUtils', 'x.y.z'
|
||||
pod 'TIFoundationUtils', 'x.y.z'
|
||||
|
|
@ -58,4 +121,4 @@ pod 'TIFoundationUtils', 'x.y.z'
|
|||
|
||||
## Legacy
|
||||
|
||||
Code located in root `Sources` folder and `LeadKit.podspec` should be treated as legacy and shouldn't be used in newly created projects. Please use TI* modules via SPM or CocoaPods.
|
||||
Code located in root `Sources` folder and `LeadKit.podspec` should be treated as legacy and shouldn't be used in newly created projects. Please use TI* modules via SPM or CocoaPods.
|
||||
|
|
|
|||
|
|
@ -24,7 +24,11 @@ import TIMapUtils
|
|||
import MapKit
|
||||
import UIKit
|
||||
|
||||
open class AppleClusterPlacemarkManager<Model>: BasePlacemarkManager<MKAnnotationView, [ApplePlacemarkManager<Model>], MKMapRect>, MKMapViewDelegate {
|
||||
open class AppleClusterPlacemarkManager<Model>: BaseClusterPlacemarkManager<MKAnnotationView,
|
||||
ApplePlacemarkManager<Model>,
|
||||
MKMapRect>,
|
||||
MKMapViewDelegate {
|
||||
|
||||
public weak var mapViewDelegate: MKMapViewDelegate?
|
||||
|
||||
private let mapDelegateSelectors = NSObject.instanceMethodSelectors(of: MKMapViewDelegate.self)
|
||||
|
|
@ -36,7 +40,8 @@ open class AppleClusterPlacemarkManager<Model>: BasePlacemarkManager<MKAnnotatio
|
|||
|
||||
self.mapViewDelegate = mapViewDelegate
|
||||
|
||||
super.init(dataModel: placemarkManagers,
|
||||
super.init(placemarkPosition: .from(coordinates: placemarkManagers.map(\.placemarkPosition)),
|
||||
dataModel: placemarkManagers,
|
||||
iconFactory: iconFactory?.asAnyMarkerIconFactory { $0.map { $0.dataModel } },
|
||||
tapHandler: tapHandler)
|
||||
}
|
||||
|
|
@ -55,10 +60,10 @@ open class AppleClusterPlacemarkManager<Model>: BasePlacemarkManager<MKAnnotatio
|
|||
override open func configure(placemark: MKAnnotationView) {
|
||||
guard let clusterAnnotation = placemark.annotation as? MKClusterAnnotation,
|
||||
let placemarkManagers = clusterAnnotation.memberAnnotations as? [ApplePlacemarkManager<Model>] else {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
placemark.image = iconFactory?.markerIcon(for: placemarkManagers)
|
||||
placemark.image = iconFactory?.markerIcon(for: placemarkManagers, state: .default)
|
||||
}
|
||||
|
||||
// MARK: - MKMapViewDelegate
|
||||
|
|
@ -79,6 +84,7 @@ open class AppleClusterPlacemarkManager<Model>: BasePlacemarkManager<MKAnnotatio
|
|||
configure(placemark: defaultAnnotationView)
|
||||
|
||||
return defaultAnnotationView
|
||||
|
||||
case let placemarkManager as ApplePlacemarkManager<Model>:
|
||||
let defaultAnnotationView = placemarkManager.iconFactory != nil
|
||||
? MKAnnotationView(annotation: annotation,
|
||||
|
|
@ -89,6 +95,7 @@ open class AppleClusterPlacemarkManager<Model>: BasePlacemarkManager<MKAnnotatio
|
|||
placemarkManager.configure(placemark: defaultAnnotationView)
|
||||
|
||||
return defaultAnnotationView
|
||||
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
|
@ -107,8 +114,29 @@ open class AppleClusterPlacemarkManager<Model>: BasePlacemarkManager<MKAnnotatio
|
|||
}
|
||||
|
||||
_ = tapHandler?(placemarkManagers, .from(coordinates: placemarkManagers.map { $0.coordinate }))
|
||||
|
||||
case let placemarkManager as ApplePlacemarkManager<Model>:
|
||||
_ = placemarkManager.tapHandler?(placemarkManager.dataModel, placemarkManager.coordinate)
|
||||
let isTapHandled = placemarkManager.tapHandler?(placemarkManager.dataModel, placemarkManager.coordinate) ?? false
|
||||
|
||||
if isTapHandled {
|
||||
placemarkManager.state = .selected
|
||||
}
|
||||
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
open func mapView(_ mapView: MKMapView, didDeselect view: MKAnnotationView) {
|
||||
guard !(mapViewDelegate?.responds(to: #selector(mapView(_:didDeselect:))) ?? false) else {
|
||||
mapViewDelegate?.mapView?(mapView, didDeselect: view)
|
||||
return
|
||||
}
|
||||
|
||||
switch view.annotation {
|
||||
case let placemarkManager as ApplePlacemarkManager<Model>:
|
||||
placemarkManager.state = .default
|
||||
|
||||
default:
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,7 +44,8 @@ open class AppleMapManager<DataModel>: BaseMapManager<MKMapView,
|
|||
return nil
|
||||
}
|
||||
|
||||
return ApplePlacemarkManager(dataModel: $0,
|
||||
return ApplePlacemarkManager(map: map,
|
||||
dataModel: $0,
|
||||
position: position,
|
||||
clusteringIdentifier: clusteringIdentifierGetter($0),
|
||||
iconFactory: iconFactory?.asAnyMarkerIconFactory(),
|
||||
|
|
|
|||
|
|
@ -23,23 +23,53 @@
|
|||
import TIMapUtils
|
||||
import MapKit
|
||||
|
||||
open class ApplePlacemarkManager<Model>: BasePlacemarkManager<MKAnnotationView, Model, CLLocationCoordinate2D>, MKAnnotation {
|
||||
open class ApplePlacemarkManager<Model>: BaseItemPlacemarkManager<MKAnnotationView, Model, CLLocationCoordinate2D>,
|
||||
MKAnnotation {
|
||||
|
||||
// MARK: - MKAnnotation
|
||||
|
||||
/// A map where all placemarks are placed
|
||||
public let map: MKMapView
|
||||
|
||||
public let coordinate: CLLocationCoordinate2D
|
||||
|
||||
/// Identifier required for correct cluster placement
|
||||
public var clusteringIdentifier: String?
|
||||
|
||||
/// Point (coordinates) itself of the current placemark manager
|
||||
public var coordinate: CLLocationCoordinate2D {
|
||||
placemarkPosition
|
||||
}
|
||||
|
||||
/// The current state of a manager's placemark
|
||||
override public var state: MarkerState {
|
||||
didSet {
|
||||
guard let placemark = placemark else {
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Although the icon is being updated, it is necessary to manually deselect
|
||||
the annotation of the current placemark if it is currently selected.
|
||||
*/
|
||||
if state == .default, let annotation = placemark.annotation {
|
||||
map.deselectAnnotation(annotation, animated: true)
|
||||
}
|
||||
|
||||
placemark.image = iconFactory?.markerIcon(for: dataModel, state: state)
|
||||
}
|
||||
}
|
||||
|
||||
public init(dataModel: Model,
|
||||
public init(map: MKMapView,
|
||||
dataModel: Model,
|
||||
position: CLLocationCoordinate2D,
|
||||
clusteringIdentifier: String?,
|
||||
iconFactory: AnyMarkerIconFactory<DataModel>?,
|
||||
tapHandler: TapHandlerClosure?) {
|
||||
|
||||
self.coordinate = position
|
||||
self.map = map
|
||||
self.clusteringIdentifier = clusteringIdentifier
|
||||
|
||||
super.init(dataModel: dataModel,
|
||||
super.init(placemarkPosition: position,
|
||||
dataModel: dataModel,
|
||||
iconFactory: iconFactory,
|
||||
tapHandler: tapHandler)
|
||||
}
|
||||
|
|
@ -47,7 +77,10 @@ open class ApplePlacemarkManager<Model>: BasePlacemarkManager<MKAnnotationView,
|
|||
// MARK: - PlacemarkManager
|
||||
|
||||
override open func configure(placemark: MKAnnotationView) {
|
||||
placemark.image = iconFactory?.markerIcon(for: dataModel)
|
||||
placemark.clusteringIdentifier = clusteringIdentifier
|
||||
super.configure(placemark: placemark)
|
||||
|
||||
// Setting required values of the current manager and placemark respectively
|
||||
self.placemark?.clusteringIdentifier = clusteringIdentifier
|
||||
self.state = .default
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIAppleMapUtils'
|
||||
s.version = '1.33.0'
|
||||
s.version = '1.39.0'
|
||||
s.summary = 'Set of helpers for map objects clustering and interacting using Apple MapKit.'
|
||||
s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.homepage = 'https://gitlab.ti/touchinstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
s.author = { 'petropavel13' => 'ivan.smolin@touchin.ru' }
|
||||
s.source = { :git => 'https://github.com/TouchInstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
s.source = { :git => 'https://gitlab.ti/touchinstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
|
||||
s.ios.deployment_target = '11.0'
|
||||
s.swift_versions = ['5.3']
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIAuth'
|
||||
s.version = '1.33.0'
|
||||
s.version = '1.39.0'
|
||||
s.summary = 'Login, registration, confirmation and other related actions'
|
||||
s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.homepage = 'https://gitlab.ti/touchinstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
s.author = { 'petropavel13' => 'ivan.smolin@touchin.ru' }
|
||||
s.source = { :git => 'https://github.com/TouchInstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
s.source = { :git => 'https://gitlab.ti/touchinstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
|
||||
s.ios.deployment_target = '13.0'
|
||||
s.swift_versions = ['5.3']
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@ Pod::Spec.new do |s|
|
|||
s.name = 'TIDeeplink'
|
||||
s.version = '1.33.0'
|
||||
s.summary = 'Deeplink service API'
|
||||
s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.homepage = 'https://gitlab.ti/touchinstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
s.author = { 'petropavel13' => 'ivan.smolin@touchin.ru',
|
||||
'castlele' => 'nikita.semenov@touchin.ru' }
|
||||
s.source = { :git => 'https://github.com/TouchInstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
s.source = { :git => 'https://gitlab.ti/touchinstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
|
||||
s.ios.deployment_target = '11.0'
|
||||
s.swift_versions = ['5.3']
|
||||
|
|
@ -14,5 +14,5 @@ Pod::Spec.new do |s|
|
|||
s.source_files = s.name + '/Sources/**/*'
|
||||
|
||||
s.dependency 'TIFoundationUtils', s.version.to_s
|
||||
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,100 @@
|
|||
//
|
||||
// Copyright (c) 2023 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
open class DashedBoundsLayer: CAShapeLayer {
|
||||
|
||||
public static var predefinedColors: [UIColor] {
|
||||
[.red, .green, .blue, .brown, .gray, .yellow, .magenta, .black, .orange, .purple, .cyan]
|
||||
}
|
||||
|
||||
private var viewBoundsObservation: NSKeyValueObservation?
|
||||
|
||||
public var dashColor: UIColor = predefinedColors.randomElement() ?? .black
|
||||
|
||||
// MARK: - Open methods
|
||||
|
||||
open func configure(on view: UIView) {
|
||||
fillColor = UIColor.clear.cgColor
|
||||
strokeColor = dashColor.cgColor
|
||||
lineWidth = 1
|
||||
lineDashPattern = [4.0, 2.0]
|
||||
|
||||
updateGeometry(from: view)
|
||||
|
||||
view.layer.addSublayer(self)
|
||||
|
||||
viewBoundsObservation = view.observe(\.bounds, options: [.new]) { [weak self] view, _ in
|
||||
self?.updateGeometry(from: view)
|
||||
}
|
||||
}
|
||||
|
||||
open func updateGeometry(from view: UIView) {
|
||||
frame = view.bounds
|
||||
path = UIBezierPath(rect: view.bounds).cgPath
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UIView + DashedBoundsLayer
|
||||
|
||||
public extension UIView {
|
||||
|
||||
@discardableResult
|
||||
func debugBoundsVisually(debugSubviews: Bool = true) -> UIView {
|
||||
disableBoundsVisuallyDebug()
|
||||
|
||||
if debugSubviews {
|
||||
for subview in subviews {
|
||||
subview.debugBoundsVisually(debugSubviews: debugSubviews)
|
||||
}
|
||||
}
|
||||
|
||||
let dashedLayer = DashedBoundsLayer()
|
||||
dashedLayer.configure(on: self)
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
func disableBoundsVisuallyDebug() {
|
||||
for sublayer in layer.sublayers ?? [] {
|
||||
if sublayer is DashedBoundsLayer {
|
||||
sublayer.removeFromSuperlayer()
|
||||
}
|
||||
}
|
||||
|
||||
for subview in subviews {
|
||||
subview.disableBoundsVisuallyDebug()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UIViewController + DashedBoundsLayer
|
||||
|
||||
public extension UIViewController {
|
||||
|
||||
@discardableResult
|
||||
func debugBoundsVisually(debugSubviews: Bool = true) -> UIViewController {
|
||||
view.debugBoundsVisually(debugSubviews: debugSubviews)
|
||||
return self
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
//
|
||||
// Copyright (c) 2023 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
|
||||
@available(iOS 13, *)
|
||||
public extension UIView {
|
||||
private struct Preview: UIViewRepresentable {
|
||||
let view: UIView
|
||||
|
||||
func makeUIView(context: Context) -> UIView {
|
||||
view
|
||||
}
|
||||
|
||||
func updateUIView(_ uiView: UIView, context: Context) {
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
func showPreview() -> some View {
|
||||
Preview(view: self)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
//
|
||||
// Copyright (c) 2023 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
|
||||
@available(iOS 13, *)
|
||||
public extension UIViewController {
|
||||
|
||||
private struct Preview: UIViewControllerRepresentable {
|
||||
let viewController: UIViewController
|
||||
|
||||
func makeUIViewController(context: Context) -> UIViewController {
|
||||
viewController
|
||||
}
|
||||
|
||||
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
func showPreview() -> some View {
|
||||
Preview(viewController: self)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +1,12 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TILogging'
|
||||
s.version = '1.33.0'
|
||||
s.summary = 'Logging API'
|
||||
s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.name = 'TIDeveloperUtils'
|
||||
s.version = '1.39.0'
|
||||
s.summary = 'Universal web view API'
|
||||
s.homepage = 'https://gitlab.ti/touchinstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
s.author = { 'petropavel13' => 'ivan.smolin@touchin.ru',
|
||||
'castlele' => 'nikita.semenov@touchin.ru' }
|
||||
s.source = { :git => 'https://github.com/TouchInstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
s.source = { :git => 'https://gitlab.ti/touchinstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
|
||||
s.ios.deployment_target = '11.0'
|
||||
s.swift_versions = ['5.3']
|
||||
|
|
@ -14,7 +14,7 @@ Pod::Spec.new do |s|
|
|||
s.source_files = s.name + '/Sources/**/*'
|
||||
|
||||
s.dependency 'TIUIKitCore', s.version.to_s
|
||||
s.dependency 'TISwiftUtils', s.version.to_s
|
||||
s.dependency 'TIUIElements', s.version.to_s
|
||||
s.dependency 'TISwiftUtils', s.version.to_s
|
||||
|
||||
end
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIEcommerce'
|
||||
s.version = '1.33.0'
|
||||
s.version = '1.39.0'
|
||||
s.summary = 'Cart, products, promocodes, bonuses and other related actions'
|
||||
s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.homepage = 'https://gitlab.ti/touchinstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
s.author = { 'petropavel13' => 'ivan.smolin@touchin.ru' }
|
||||
s.source = { :git => 'https://github.com/TouchInstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
s.source = { :git => 'https://gitlab.ti/touchinstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
|
||||
s.ios.deployment_target = '11.0'
|
||||
s.swift_versions = ['5.3']
|
||||
|
|
|
|||
|
|
@ -1,45 +0,0 @@
|
|||
import Foundation
|
||||
import TIFoundationUtils
|
||||
|
||||
let operationQueue = OperationQueue()
|
||||
operationQueue.maxConcurrentOperationCount = 1
|
||||
|
||||
struct NonCancellableTask: CancellableTask {
|
||||
func cancel() {}
|
||||
}
|
||||
|
||||
ClosureAsyncOperation<Int, Error> { completion in
|
||||
DispatchQueue.global().asyncAfter(deadline: .now() + .seconds(1)) {
|
||||
completion(.success(1))
|
||||
}
|
||||
return NonCancellableTask()
|
||||
|
||||
}
|
||||
.map { $0 * 2 }
|
||||
.observe(onSuccess: { result in
|
||||
debugPrint("Async operation one has finished with \(result)")
|
||||
})
|
||||
.add(to: operationQueue)
|
||||
|
||||
ClosureAsyncOperation<Int, Error> { completion in
|
||||
DispatchQueue.global().asyncAfter(deadline: .now() + .seconds(1)) {
|
||||
completion(.success(2))
|
||||
}
|
||||
return NonCancellableTask()
|
||||
}
|
||||
.map { $0 * 2 }
|
||||
.observe(onSuccess: { result in
|
||||
debugPrint("Async operation two has finished with \(result)")
|
||||
})
|
||||
.add(to: operationQueue)
|
||||
|
||||
// "Async operation one has finished with 2"
|
||||
// "Async operation two has finished with 4"
|
||||
|
||||
struct Some {
|
||||
var arr: [Int] = []
|
||||
|
||||
mutating func add(_ i: Int) {
|
||||
arr.append(i)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<playground version='5.0' target-platform='ios' buildActiveScheme='true' importAppTypes='true'>
|
||||
<timeline fileName='timeline.xctimeline'/>
|
||||
</playground>
|
||||
|
|
@ -25,15 +25,25 @@ import Foundation
|
|||
private final class ClosureObserverOperation<Output, Failure: Error>: DependendAsyncOperation<Output, Failure> {
|
||||
public typealias OnResultClosure = (Result<Output, Failure>) -> Void
|
||||
|
||||
private let onResult: OnResultClosure?
|
||||
private let callbackQueue: DispatchQueue
|
||||
|
||||
public init(dependency: AsyncOperation<Output, Failure>,
|
||||
onResult: OnResultClosure? = nil,
|
||||
callbackQueue: DispatchQueue = .main) {
|
||||
|
||||
super.init(dependency: dependency) { result in
|
||||
callbackQueue.async {
|
||||
onResult?(result)
|
||||
}
|
||||
return result
|
||||
self.onResult = onResult
|
||||
self.callbackQueue = callbackQueue
|
||||
|
||||
super.init(dependency: dependency) { $0 }
|
||||
}
|
||||
|
||||
override func handle(result: Result<Output, Failure>) {
|
||||
self.result = result
|
||||
|
||||
callbackQueue.async {
|
||||
self.onResult?(result)
|
||||
self.state = .isFinished
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,22 +24,26 @@ import Foundation
|
|||
|
||||
open class DependendAsyncOperation<Output, Failure: Error>: AsyncOperation<Output, Failure> {
|
||||
public var dependencyObservation: NSKeyValueObservation?
|
||||
private let dependency: Operation
|
||||
|
||||
public init<DependencyOutput, DependencyFailure: Error>(dependency: AsyncOperation<DependencyOutput, DependencyFailure>,
|
||||
resultObservation: @escaping (Result<DependencyOutput, DependencyFailure>) -> Result<Output, Failure>) {
|
||||
self.dependency = dependency
|
||||
|
||||
super.init()
|
||||
|
||||
cancelOnCancellation(of: dependency)
|
||||
dependency.cancelOnCancellation(of: self)
|
||||
|
||||
dependencyObservation = dependency.subscribe { [weak self] in
|
||||
self?.result = resultObservation($0)
|
||||
self?.state = .isReady
|
||||
self?.handle(result: resultObservation($0))
|
||||
}
|
||||
|
||||
addDependency(dependency) // keeps strong reference to dependency as well
|
||||
|
||||
state = nil // prevent start of current operation if result is not yet provided by dependency
|
||||
state = .isReady
|
||||
}
|
||||
|
||||
open override func start() {
|
||||
state = .isExecuting
|
||||
|
||||
dependency.start()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
ENV["DEVELOPMENT_INSTALL"] = "true"
|
||||
|
||||
target 'TIFoundationUtils' do
|
||||
platform :ios, 10.0
|
||||
use_frameworks!
|
||||
|
||||
pod 'TISwiftUtils', :path => '../../../../TISwiftUtils/TISwiftUtils.podspec'
|
||||
pod 'TIFoundationUtils', :path => '../../../../TIFoundationUtils/TIFoundationUtils.podspec'
|
||||
end
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
# gitignore nef files
|
||||
**/build/
|
||||
**/nef/
|
||||
LICENSE
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>launcher</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>AppIcon</string>
|
||||
<key>CFBundleIconName</key>
|
||||
<string>AppIcon</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.fortysevendeg.nef</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleSupportedPlatforms</key>
|
||||
<array>
|
||||
<string>MacOSX</string>
|
||||
</array>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.developer-tools</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>10.14</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2019 The nef Authors. All rights reserved.</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
## gitignore nef files
|
||||
**/build/
|
||||
**/nef/
|
||||
LICENSE
|
||||
|
||||
## User data
|
||||
**/xcuserdata/
|
||||
podfile.lock
|
||||
**.DS_Store
|
||||
|
||||
## Obj-C/Swift specific
|
||||
*.hmap
|
||||
*.ipa
|
||||
*.dSYM.zip
|
||||
*.dSYM
|
||||
|
||||
## CocoaPods
|
||||
**Pods**
|
||||
|
||||
## Carthage
|
||||
**Carthage**
|
||||
|
||||
## SPM
|
||||
.build
|
||||
.swiftpm
|
||||
swiftpm
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
ENV["DEVELOPMENT_INSTALL"] = "true"
|
||||
|
||||
target 'TIFoundationUtils' do
|
||||
platform :ios, 10.0
|
||||
use_frameworks!
|
||||
|
||||
pod 'TISwiftUtils', :path => '../../../../TISwiftUtils/TISwiftUtils.podspec'
|
||||
pod 'TIFoundationUtils', :path => '../../../../TIFoundationUtils/TIFoundationUtils.podspec'
|
||||
end
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
/*:
|
||||
# `AsyncOperation<Result, Error>` - generic сабкласс Operation
|
||||
|
||||
Позволяет запускать:
|
||||
|
||||
- асинхронный код внутри операции
|
||||
- собирать цепочки из операций
|
||||
- подписываться на результат выполнения
|
||||
|
||||
## Базовые операции
|
||||
|
||||
"Из коробки", на данный момент, доступен всего один сабкласс асинхронной операции, потому что больше обычно и не нужно.
|
||||
Но можно наследоваться и создавать собственные сабклассы при необходимости.
|
||||
*/
|
||||
|
||||
/*:
|
||||
### `ClosureAsyncOperation<Result, Error>`
|
||||
|
||||
Операция принимающая некий closure, который по окончании своей работы вызовет completion, переданный ему параметром
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import TIFoundationUtils
|
||||
|
||||
let intResultOperation = ClosureAsyncOperation<Int, Never> { completion in
|
||||
DispatchQueue.global().asyncAfter(deadline: .now() + .seconds(3)) {
|
||||
completion(.success(1))
|
||||
}
|
||||
return Cancellables.nonCancellable()
|
||||
}
|
||||
|
||||
/*:
|
||||
## Базовые операторы
|
||||
|
||||
На данный момент реализовано всего два оператора:
|
||||
|
||||
- `map(mapOutput:mapFailure:)` - конвертирует ResultType в новый NewResultType и ErrorType в новый NewErrorType
|
||||
- `observe(onSuccess:onFailure)` - просто вызывает переданные callback'и при получении результата или ошибки
|
||||
|
||||
*/
|
||||
|
||||
//: ### Пример запуска асинхронных операци с применением операторов в последовательной очереди и вывод результатов
|
||||
|
||||
let operationQueue = OperationQueue()
|
||||
operationQueue.maxConcurrentOperationCount = 1
|
||||
|
||||
ClosureAsyncOperation<Int, Never> { completion in
|
||||
DispatchQueue.global().asyncAfter(deadline: .now() + .seconds(3)) {
|
||||
completion(.success(1))
|
||||
}
|
||||
return Cancellables.nonCancellable()
|
||||
}
|
||||
.map { $0 * 2 }
|
||||
.observe(onSuccess: { result in
|
||||
debugPrint("Async operation one has finished with \(result)")
|
||||
})
|
||||
.add(to: operationQueue)
|
||||
|
||||
ClosureAsyncOperation<String, Never> { completion in
|
||||
DispatchQueue.global().asyncAfter(deadline: .now() + .seconds(1)) {
|
||||
completion(.success("Success"))
|
||||
}
|
||||
return Cancellables.nonCancellable()
|
||||
}
|
||||
.observe(onSuccess: { result in
|
||||
debugPrint("Async operation two has finished with \(result)")
|
||||
})
|
||||
.add(to: operationQueue)
|
||||
|
||||
/*:
|
||||
В консоли будет выведено:
|
||||
|
||||
```
|
||||
"Async operation one has finished with 2"
|
||||
"Async operation two has finished with Success"
|
||||
```
|
||||
*/
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<playground version='6.0' target-platform='ios' display-mode='raw' buildActiveScheme='true'/>
|
||||
|
|
@ -0,0 +1,396 @@
|
|||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 50;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
A02229F337222E1B9E665195 /* Pods_TIFoundationUtils.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0CC8351C190B190691F8FC76 /* Pods_TIFoundationUtils.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
0CC8351C190B190691F8FC76 /* Pods_TIFoundationUtils.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_TIFoundationUtils.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
59A923BF05D59C9BEC19C6FD /* Pods-TIFoundationUtils.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TIFoundationUtils.release.xcconfig"; path = "Target Support Files/Pods-TIFoundationUtils/Pods-TIFoundationUtils.release.xcconfig"; sourceTree = "<group>"; };
|
||||
8BACBE8322576CAD00266845 /* TIFoundationUtils.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TIFoundationUtils.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
8BACBE8622576CAD00266845 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
EC0CA80D0000D9F6C5FACF34 /* Pods-TIFoundationUtils.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TIFoundationUtils.debug.xcconfig"; path = "Target Support Files/Pods-TIFoundationUtils/Pods-TIFoundationUtils.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
8BACBE8022576CAD00266845 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
A02229F337222E1B9E665195 /* Pods_TIFoundationUtils.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
37058452818BA6D3D6C19AA7 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0CC8351C190B190691F8FC76 /* Pods_TIFoundationUtils.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8B39A26221D40F8700DE2643 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8BACBE8422576CAD00266845 /* TIFoundationUtils */,
|
||||
8B39A26C21D40F8700DE2643 /* Products */,
|
||||
AAAF022BA9D15672298175E6 /* Pods */,
|
||||
37058452818BA6D3D6C19AA7 /* Frameworks */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8B39A26C21D40F8700DE2643 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8BACBE8322576CAD00266845 /* TIFoundationUtils.framework */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8BACBE8422576CAD00266845 /* TIFoundationUtils */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8BACBE8622576CAD00266845 /* Info.plist */,
|
||||
);
|
||||
path = TIFoundationUtils;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
AAAF022BA9D15672298175E6 /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
EC0CA80D0000D9F6C5FACF34 /* Pods-TIFoundationUtils.debug.xcconfig */,
|
||||
59A923BF05D59C9BEC19C6FD /* Pods-TIFoundationUtils.release.xcconfig */,
|
||||
);
|
||||
name = Pods;
|
||||
path = Pods;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
8BACBE7E22576CAD00266845 /* Headers */ = {
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXHeadersBuildPhase section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
8BACBE8222576CAD00266845 /* TIFoundationUtils */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 8BACBE8A22576CAD00266845 /* Build configuration list for PBXNativeTarget "TIFoundationUtils" */;
|
||||
buildPhases = (
|
||||
F4F8AAE8BFCE7066005B0EF5 /* [CP] Check Pods Manifest.lock */,
|
||||
8BACBE7E22576CAD00266845 /* Headers */,
|
||||
8BACBE7F22576CAD00266845 /* Sources */,
|
||||
8BACBE8022576CAD00266845 /* Frameworks */,
|
||||
8BACBE8122576CAD00266845 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = TIFoundationUtils;
|
||||
productName = TIFoundationUtils2;
|
||||
productReference = 8BACBE8322576CAD00266845 /* TIFoundationUtils.framework */;
|
||||
productType = "com.apple.product-type.framework";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
8B39A26321D40F8700DE2643 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 1010;
|
||||
LastUpgradeCheck = 1200;
|
||||
ORGANIZATIONNAME = "47 Degrees";
|
||||
TargetAttributes = {
|
||||
8BACBE8222576CAD00266845 = {
|
||||
CreatedOnToolsVersion = 10.1;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 8B39A26621D40F8700DE2643 /* Build configuration list for PBXProject "TIFoundationUtils" */;
|
||||
compatibilityVersion = "Xcode 9.3";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 8B39A26221D40F8700DE2643;
|
||||
productRefGroup = 8B39A26C21D40F8700DE2643 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
8BACBE8222576CAD00266845 /* TIFoundationUtils */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
8BACBE8122576CAD00266845 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
F4F8AAE8BFCE7066005B0EF5 /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||
"${PODS_ROOT}/Manifest.lock",
|
||||
);
|
||||
name = "[CP] Check Pods Manifest.lock";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
"$(DERIVED_FILE_DIR)/Pods-TIFoundationUtils-checkManifestLockResult.txt",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
8BACBE7F22576CAD00266845 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
8B39A27721D40F8800DE2643 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
ENABLE_TESTING_SEARCH_PATHS = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
8B39A27821D40F8800DE2643 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTING_SEARCH_PATHS = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
8BACBE8822576CAD00266845 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = EC0CA80D0000D9F6C5FACF34 /* Pods-TIFoundationUtils.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_TIFoundationUtils_VERSION = 1;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
INFOPLIST_FILE = "$(SRCROOT)/TIFoundationUtils/Info.plist";
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.47deg.ios.TIFoundationUtils;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_VERSION = 5;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
8BACBE8922576CAD00266845 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 59A923BF05D59C9BEC19C6FD /* Pods-TIFoundationUtils.release.xcconfig */;
|
||||
buildSettings = {
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_TIFoundationUtils_VERSION = 1;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
INFOPLIST_FILE = "$(SRCROOT)/TIFoundationUtils/Info.plist";
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.47deg.ios.TIFoundationUtils;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_VERSION = 5;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
8B39A26621D40F8700DE2643 /* Build configuration list for PBXProject "TIFoundationUtils" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
8B39A27721D40F8800DE2643 /* Debug */,
|
||||
8B39A27821D40F8800DE2643 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
8BACBE8A22576CAD00266845 /* Build configuration list for PBXNativeTarget "TIFoundationUtils" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
8BACBE8822576CAD00266845 /* Debug */,
|
||||
8BACBE8922576CAD00266845 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 8B39A26321D40F8700DE2643 /* Project object */;
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:TIFoundationUtils.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1200"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "8BACBE8222576CAD00266845"
|
||||
BuildableName = "TIFoundationUtils.framework"
|
||||
BlueprintName = "TIFoundationUtils"
|
||||
ReferencedContainer = "container:TIFoundationUtils.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "8BACBE8222576CAD00266845"
|
||||
BuildableName = "TIFoundationUtils.framework"
|
||||
BlueprintName = "TIFoundationUtils"
|
||||
ReferencedContainer = "container:TIFoundationUtils.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "8BACBE8222576CAD00266845"
|
||||
BuildableName = "TIFoundationUtils.framework"
|
||||
BlueprintName = "TIFoundationUtils"
|
||||
ReferencedContainer = "container:TIFoundationUtils.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef location = "group:TIFoundationUtils.playground"></FileRef>
|
||||
<FileRef
|
||||
location = "group:TIFoundationUtils.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:Pods/Pods.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2019. The nef authors.</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
#!/bin/bash
|
||||
|
||||
workspace="TIFoundationUtils.xcworkspace"
|
||||
workspacePath=$(echo "$0" | rev | cut -f2- -d '/' | rev)
|
||||
|
||||
open "`pwd`/$workspacePath/$workspace"
|
||||
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1 @@
|
|||
TIFoundationUtils.app/Contents/MacOS/TIFoundationUtils.playground
|
||||
|
|
@ -1,16 +1,23 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIFoundationUtils'
|
||||
s.version = '1.33.0'
|
||||
s.version = '1.39.0'
|
||||
s.summary = 'Set of helpers for Foundation framework classes.'
|
||||
s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.homepage = 'https://gitlab.ti/touchinstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
s.author = { 'petropavel13' => 'ivan.smolin@touchin.ru' }
|
||||
s.source = { :git => 'https://github.com/TouchInstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
s.source = { :git => 'https://gitlab.ti/touchinstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
|
||||
s.ios.deployment_target = '10.0'
|
||||
s.swift_versions = ['5.3']
|
||||
|
||||
s.source_files = s.name + '/**/Sources/**/*'
|
||||
sources = '**/Sources/**/*.swift'
|
||||
if ENV["DEVELOPMENT_INSTALL"] # installing using :path =>
|
||||
s.source_files = sources
|
||||
s.exclude_files = s.name + '.app'
|
||||
else
|
||||
s.source_files = s.name + '/' + sources
|
||||
s.exclude_files = s.name + '/*.app'
|
||||
end
|
||||
|
||||
s.dependency 'TISwiftUtils', s.version.to_s
|
||||
s.framework = 'Foundation'
|
||||
|
|
|
|||
|
|
@ -24,7 +24,9 @@ import TIMapUtils
|
|||
import GoogleMapsUtils
|
||||
import GoogleMaps
|
||||
|
||||
open class GoogleClusterPlacemarkManager<Model>: BasePlacemarkManager<GMSMarker, [GooglePlacemarkManager<Model>], GMSCoordinateBounds>,
|
||||
open class GoogleClusterPlacemarkManager<Model>: BaseClusterPlacemarkManager<GMSMarker,
|
||||
GooglePlacemarkManager<Model>,
|
||||
GMSCoordinateBounds>,
|
||||
GMUClusterRendererDelegate,
|
||||
GMUClusterManagerDelegate,
|
||||
GMUClusterIconGenerator {
|
||||
|
|
@ -44,7 +46,8 @@ open class GoogleClusterPlacemarkManager<Model>: BasePlacemarkManager<GMSMarker,
|
|||
mapDelegate: GMSMapViewDelegate? = nil,
|
||||
tapHandler: TapHandlerClosure?) where IF.Model == [Model] {
|
||||
|
||||
super.init(dataModel: placemarkManagers,
|
||||
super.init(placemarkPosition: .from(coordinates: placemarkManagers.map(\.placemarkPosition)) ?? .init(),
|
||||
dataModel: placemarkManagers,
|
||||
iconFactory: iconFactory?.asAnyMarkerIconFactory { $0.map { $0.dataModel } },
|
||||
tapHandler: tapHandler)
|
||||
|
||||
|
|
@ -91,7 +94,7 @@ open class GoogleClusterPlacemarkManager<Model>: BasePlacemarkManager<GMSMarker,
|
|||
return
|
||||
}
|
||||
|
||||
marker.icon = iconFactory?.markerIcon(for: placemarkManagers)
|
||||
marker.icon = iconFactory?.markerIcon(for: placemarkManagers, state: .default)
|
||||
?? defaultClusterIconGenerator.icon(forSize: UInt(placemarkManagers.count))
|
||||
case let clusterItem as GooglePlacemarkManager<Model>:
|
||||
clusterItem.configure(placemark: marker)
|
||||
|
|
@ -111,7 +114,7 @@ open class GoogleClusterPlacemarkManager<Model>: BasePlacemarkManager<GMSMarker,
|
|||
return false
|
||||
}
|
||||
|
||||
return tapHandler?(placemarkManagers, .from(coordinates: placemarkManagers.map { $0.position }) ?? .init()) ?? false
|
||||
return tapHandler?(placemarkManagers, .from(coordinates: placemarkManagers.map(\.placemarkPosition)) ?? .init()) ?? false
|
||||
}
|
||||
|
||||
open func clusterManager(_ clusterManager: GMUClusterManager, didTap clusterItem: GMUClusterItem) -> Bool {
|
||||
|
|
|
|||
|
|
@ -24,19 +24,34 @@ import TIMapUtils
|
|||
import GoogleMaps
|
||||
import GoogleMapsUtils
|
||||
|
||||
open class GooglePlacemarkManager<Model>: BasePlacemarkManager<GMSMarker, Model, CLLocationCoordinate2D>, GMUClusterItem {
|
||||
open class GooglePlacemarkManager<Model>: BaseItemPlacemarkManager<GMSMarker, Model, CLLocationCoordinate2D>,
|
||||
GMUClusterItem {
|
||||
|
||||
// MARK: - GMUClusterItem
|
||||
|
||||
public let position: CLLocationCoordinate2D
|
||||
/// Point (coordinates) itself of the current placemark manager
|
||||
public var position: CLLocationCoordinate2D {
|
||||
placemarkPosition
|
||||
}
|
||||
|
||||
/// The current state of a manager's placemark
|
||||
override public var state: MarkerState {
|
||||
didSet {
|
||||
guard let placemark = placemark else {
|
||||
return
|
||||
}
|
||||
|
||||
placemark.icon = iconFactory?.markerIcon(for: dataModel, state: state)
|
||||
}
|
||||
}
|
||||
|
||||
public init(dataModel: Model,
|
||||
position: CLLocationCoordinate2D,
|
||||
iconFactory: AnyMarkerIconFactory<DataModel>?,
|
||||
tapHandler: TapHandlerClosure?) {
|
||||
|
||||
self.position = position
|
||||
|
||||
super.init(dataModel: dataModel,
|
||||
super.init(placemarkPosition: position,
|
||||
dataModel: dataModel,
|
||||
iconFactory: iconFactory,
|
||||
tapHandler: tapHandler)
|
||||
}
|
||||
|
|
@ -44,6 +59,13 @@ open class GooglePlacemarkManager<Model>: BasePlacemarkManager<GMSMarker, Model,
|
|||
// MARK: - PlacemarkManager
|
||||
|
||||
override open func configure(placemark: GMSMarker) {
|
||||
placemark.icon = iconFactory?.markerIcon(for: dataModel)
|
||||
super.configure(placemark: placemark)
|
||||
|
||||
/*
|
||||
Note: it is required not to just resetting a state but setting a value depending
|
||||
on a previous state due to Google Maps' map re-rendering on zoom to save
|
||||
an appearance of the pin as it was previously
|
||||
*/
|
||||
self.state = state == .default ? .default : .selected
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIGoogleMapUtils'
|
||||
s.version = '1.33.0'
|
||||
s.version = '1.39.0'
|
||||
s.summary = 'Set of helpers for map objects clustering and interacting using Google Maps SDK.'
|
||||
s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.homepage = 'https://gitlab.ti/touchinstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
s.author = { 'petropavel13' => 'ivan.smolin@touchin.ru' }
|
||||
s.source = { :git => 'https://github.com/TouchInstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
s.source = { :git => 'https://gitlab.ti/touchinstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
|
||||
s.ios.deployment_target = '12.0'
|
||||
s.swift_versions = ['5.3']
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIKeychainUtils'
|
||||
s.version = '1.33.0'
|
||||
s.version = '1.39.0'
|
||||
s.summary = 'Set of helpers for Keychain classes.'
|
||||
s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.homepage = 'https://gitlab.ti/touchinstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
s.author = { 'petropavel13' => 'ivan.smolin@touchin.ru' }
|
||||
s.source = { :git => 'https://github.com/TouchInstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
s.source = { :git => 'https://gitlab.ti/touchinstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
|
||||
s.ios.deployment_target = '11.0'
|
||||
s.swift_versions = ['5.3']
|
||||
|
|
|
|||
|
|
@ -1,77 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2022 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import os
|
||||
|
||||
public struct TILogger: LoggerRepresentable {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
@available(iOS 12, *)
|
||||
public static let defaultLogger = TILogger(subsystem: .defaultSubsystem ?? "", category: .pointsOfInterest)
|
||||
|
||||
public let logInfo: OSLog
|
||||
|
||||
// MARK: - Init
|
||||
|
||||
public init(subsystem: String, category: String) {
|
||||
self.logInfo = .init(subsystem: subsystem, category: category)
|
||||
}
|
||||
|
||||
@available(iOS 12, *)
|
||||
public init(subsystem: String, category: OSLog.Category) {
|
||||
self.logInfo = .init(subsystem: subsystem, category: category)
|
||||
}
|
||||
|
||||
// MARK: - LoggerRepresentable
|
||||
|
||||
public func log(_ message: StaticString, log: OSLog?, type: OSLogType, _ arguments: CVarArg...) {
|
||||
os_log(message, log: log ?? logInfo, type: type, arguments)
|
||||
}
|
||||
|
||||
// MARK: - Public methods
|
||||
|
||||
public func verbose(_ message: StaticString, _ arguments: CVarArg...) {
|
||||
self.log(message, log: logInfo, type: .default, arguments)
|
||||
}
|
||||
|
||||
public func info(_ message: StaticString, _ arguments: CVarArg...) {
|
||||
self.log(message, log: logInfo, type: .info, arguments)
|
||||
}
|
||||
|
||||
public func debug(_ message: StaticString, _ arguments: CVarArg...) {
|
||||
self.log(message, log: logInfo, type: .debug, arguments)
|
||||
}
|
||||
|
||||
public func error(_ message: StaticString, _ arguments: CVarArg...) {
|
||||
self.log(message, log: logInfo, type: .error, arguments)
|
||||
}
|
||||
|
||||
public func fault(_ message: StaticString, _ arguments: CVarArg...) {
|
||||
self.log(message, log: logInfo, type: .fault, arguments)
|
||||
}
|
||||
}
|
||||
|
||||
private extension String {
|
||||
static let defaultSubsystem = Bundle.main.bundleIdentifier
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
//
|
||||
// Copyright (c) 2023 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
import CoreLocation
|
||||
|
||||
extension CLLocationCoordinate2D: Equatable {
|
||||
public static func == (lhs: CLLocationCoordinate2D, rhs: CLLocationCoordinate2D) -> Bool {
|
||||
lhs.latitude == rhs.latitude && lhs.longitude == rhs.longitude
|
||||
}
|
||||
}
|
||||
|
|
@ -23,20 +23,20 @@
|
|||
import UIKit.UIImage
|
||||
|
||||
public final class AnyMarkerIconFactory<Model>: MarkerIconFactory {
|
||||
public typealias IconProviderClosure = (Model) -> UIImage
|
||||
public typealias IconProviderClosure = (Model, MarkerState) -> UIImage
|
||||
|
||||
public var iconProviderClosure: IconProviderClosure
|
||||
|
||||
public init<IF: MarkerIconFactory>(iconFactory: IF) where IF.Model == Model {
|
||||
self.iconProviderClosure = { iconFactory.markerIcon(for: $0) }
|
||||
self.iconProviderClosure = { iconFactory.markerIcon(for: $0, state: $1) }
|
||||
}
|
||||
|
||||
public init<IF: MarkerIconFactory, T>(iconFactory: IF, transform: @escaping (Model) -> T) where IF.Model == T {
|
||||
self.iconProviderClosure = { iconFactory.markerIcon(for: transform($0)) }
|
||||
self.iconProviderClosure = { iconFactory.markerIcon(for: transform($0), state: $1) }
|
||||
}
|
||||
|
||||
public func markerIcon(for model: Model) -> UIImage {
|
||||
iconProviderClosure(model)
|
||||
public func markerIcon(for model: Model, state: MarkerState) -> UIImage {
|
||||
iconProviderClosure(model, state)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
import UIKit.UIImage
|
||||
|
||||
open class DefaultCachableMarkerIconFactory<M, K: AnyObject>: DefaultMarkerIconFactory<M> {
|
||||
public typealias CacheKeyProvider = (M) -> K
|
||||
public typealias CacheKeyProvider = (M, MarkerState) -> K
|
||||
|
||||
public let cache = NSCache<K, UIImage>()
|
||||
|
||||
|
|
@ -37,11 +37,11 @@ open class DefaultCachableMarkerIconFactory<M, K: AnyObject>: DefaultMarkerIconF
|
|||
super.init(createIconClosure: createIconClosure)
|
||||
}
|
||||
|
||||
open override func markerIcon(for model: M) -> UIImage {
|
||||
let cacheKey = cacheKeyProvider(model)
|
||||
open override func markerIcon(for model: M, state: MarkerState) -> UIImage {
|
||||
let cacheKey = cacheKeyProvider(model, state)
|
||||
|
||||
guard let cachedIcon = cache.object(forKey: cacheKey) else {
|
||||
let icon = super.markerIcon(for: model)
|
||||
let icon = super.markerIcon(for: model, state: state)
|
||||
cache.setObject(icon, forKey: cacheKey)
|
||||
|
||||
return icon
|
||||
|
|
|
|||
|
|
@ -32,12 +32,12 @@ public final class DefaultClusterMarkerIconFactory<Model>: DefaultCachableMarker
|
|||
self.beforeRenderCallback = beforeRenderCallback
|
||||
self.clusterIconRenderer = DefaultClusterIconRenderer()
|
||||
|
||||
super.init { [clusterIconRenderer] in
|
||||
beforeRenderCallback?($0, clusterIconRenderer)
|
||||
super.init { [clusterIconRenderer] models, _ in
|
||||
beforeRenderCallback?(models, clusterIconRenderer)
|
||||
|
||||
return clusterIconRenderer.renderCluster(of: $0.count)
|
||||
} cacheKeyProvider: {
|
||||
String($0.count) as NSString
|
||||
return clusterIconRenderer.renderCluster(of: models.count)
|
||||
} cacheKeyProvider: { models, _ in
|
||||
String(models.count) as NSString
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
import UIKit.UIImage
|
||||
|
||||
open class DefaultMarkerIconFactory<M>: MarkerIconFactory {
|
||||
public typealias CreateIconClosure = (M) -> UIImage
|
||||
public typealias CreateIconClosure = (M, MarkerState) -> UIImage
|
||||
|
||||
private let createIconClosure: CreateIconClosure
|
||||
|
||||
|
|
@ -31,8 +31,8 @@ open class DefaultMarkerIconFactory<M>: MarkerIconFactory {
|
|||
self.createIconClosure = createIconClosure
|
||||
}
|
||||
|
||||
open func markerIcon(for model: M) -> UIImage {
|
||||
postprocess(icon: createIconClosure(model))
|
||||
open func markerIcon(for model: M, state: MarkerState) -> UIImage {
|
||||
postprocess(icon: createIconClosure(model, state))
|
||||
}
|
||||
|
||||
open func postprocess(icon: UIImage) -> UIImage {
|
||||
|
|
|
|||
|
|
@ -25,5 +25,5 @@ import UIKit.UIImage
|
|||
public protocol MarkerIconFactory {
|
||||
associatedtype Model
|
||||
|
||||
func markerIcon(for model: Model) -> UIImage
|
||||
func markerIcon(for model: Model, state: MarkerState) -> UIImage
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
//
|
||||
// Copyright (c) 2023 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
/// Available marker states on any map
|
||||
public enum MarkerState: String {
|
||||
|
||||
/// A state where a map point is selected and a marker is highlighted
|
||||
case selected
|
||||
|
||||
/// A default state where a map point is shown on a map
|
||||
case `default`
|
||||
}
|
||||
|
|
@ -29,7 +29,7 @@ public final class StaticImageIconFactory<Model>: MarkerIconFactory {
|
|||
self.image = image
|
||||
}
|
||||
|
||||
public func markerIcon(for model: Model) -> UIImage {
|
||||
public func markerIcon(for model: Model, state: MarkerState) -> UIImage {
|
||||
image
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
//
|
||||
// Copyright (c) 2023 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
/**
|
||||
Base cluster placemark manager
|
||||
|
||||
- Parameters:
|
||||
- ClusterPlacemark: a generic parameter describing a cluster placemark itself
|
||||
- ItemPlacemarkManager: a single placemarks manager element of array for a cluster
|
||||
- ClusterBounds: a rectangle area of the current cluster or a collection of cluster placemark objects
|
||||
*/
|
||||
open class BaseClusterPlacemarkManager<ClusterPlacemark, ItemPlacemarkManager: PlacemarkManager, ClusterBounds>:
|
||||
BasePlacemarkManager<ClusterPlacemark, [ItemPlacemarkManager], ClusterBounds> where ItemPlacemarkManager.Position : Equatable {
|
||||
|
||||
/// Manual selecting of a placemark with an incoming point coordinates
|
||||
open func selectMarker(with point: ItemPlacemarkManager.Position) {
|
||||
dataModel.filter { $0.placemarkPosition == point }.forEach { $0.state = .selected }
|
||||
}
|
||||
|
||||
/// Manual state resetting of all placemarks with currently selected state
|
||||
open func resetMarkersState() {
|
||||
dataModel.filter { $0.state == .selected }.forEach { $0.state = .default }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
//
|
||||
// Copyright (c) 2023 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
/**
|
||||
Base single item placemark manager
|
||||
|
||||
Contains all properties of `BasePlacemarkManager` and adds a `placemark` property to use
|
||||
|
||||
- Parameters:
|
||||
- Placemark: a placemark itself managed by an item placemark manager
|
||||
- DataModel: a data model of a placemark which is used for configuration etc.
|
||||
- Location: latitude and longitude of a placemark
|
||||
*/
|
||||
open class BaseItemPlacemarkManager<Placemark, DataModel, Location>: BasePlacemarkManager<Placemark, DataModel, Location> {
|
||||
|
||||
/// Placemark itself of the current placemark manager
|
||||
public private(set) var placemark: Placemark?
|
||||
|
||||
override open func configure(placemark: Placemark) {
|
||||
super.configure(placemark: placemark)
|
||||
|
||||
self.placemark = placemark
|
||||
}
|
||||
}
|
||||
|
|
@ -23,7 +23,7 @@
|
|||
import Foundation
|
||||
import UIKit.UIGeometry
|
||||
|
||||
open class BaseMapManager<Map,
|
||||
open class BaseMapManager<Map: AnyObject,
|
||||
PM: PlacemarkManager,
|
||||
CPM: PlacemarkManager,
|
||||
CUF: CameraUpdateFactory> where PM.Position: LocationCoordinate,
|
||||
|
|
@ -79,16 +79,20 @@ open class BaseMapManager<Map,
|
|||
}
|
||||
|
||||
open func set(items: [PM.DataModel]) {
|
||||
let placemarkTapHandler: PM.TapHandlerClosure = { [map, selectPlacemarkHandler, animationDuration, cameraUpdateOnMarkerTap] model, location in
|
||||
cameraUpdateOnMarkerTap?(location).update(map: map, animationDuration: animationDuration)
|
||||
let placemarkTapHandler: PM.TapHandlerClosure = { [weak map, selectPlacemarkHandler, animationDuration, cameraUpdateOnMarkerTap] model, location in
|
||||
if let map = map {
|
||||
cameraUpdateOnMarkerTap?(location).update(map: map, animationDuration: animationDuration)
|
||||
}
|
||||
|
||||
return selectPlacemarkHandler(model)
|
||||
}
|
||||
|
||||
let placemarkManagers = items.compactMap { placemarkManagerCreator($0, placemarkTapHandler) }
|
||||
|
||||
let clusterTapHandler: CPM.TapHandlerClosure = { [map, animationDuration, cameraUpdateOnClusterTap] managers, boundingBox in
|
||||
cameraUpdateOnClusterTap?(boundingBox).update(map: map, animationDuration: animationDuration)
|
||||
let clusterTapHandler: CPM.TapHandlerClosure = { [weak map, animationDuration, cameraUpdateOnClusterTap] managers, boundingBox in
|
||||
if let map = map {
|
||||
cameraUpdateOnClusterTap?(boundingBox).update(map: map, animationDuration: animationDuration)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,17 +23,27 @@
|
|||
import ObjectiveC
|
||||
|
||||
open class BasePlacemarkManager<Placemark, DataModel, Location>: NSObject, PlacemarkManager, PlacemarkConfigurator {
|
||||
|
||||
public typealias TapHandlerClosure = (DataModel, Location) -> Bool
|
||||
|
||||
/// The current state of a manager's placemark
|
||||
open var state: MarkerState = .default
|
||||
|
||||
/// Point (coordinates) itself of the current placemark manager
|
||||
public let placemarkPosition: Location
|
||||
|
||||
/// Model for the current placemark manager
|
||||
public let dataModel: DataModel
|
||||
|
||||
public var tapHandler: TapHandlerClosure?
|
||||
public var iconFactory: AnyMarkerIconFactory<DataModel>?
|
||||
|
||||
public let dataModel: DataModel
|
||||
|
||||
public init(dataModel: DataModel,
|
||||
public init(placemarkPosition: Location,
|
||||
dataModel: DataModel,
|
||||
iconFactory: AnyMarkerIconFactory<DataModel>?,
|
||||
tapHandler: TapHandlerClosure?) {
|
||||
|
||||
self.placemarkPosition = placemarkPosition
|
||||
self.dataModel = dataModel
|
||||
self.iconFactory = iconFactory
|
||||
self.tapHandler = tapHandler
|
||||
|
|
|
|||
|
|
@ -20,12 +20,24 @@
|
|||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
public protocol PlacemarkManager {
|
||||
public protocol PlacemarkManager: AnyObject {
|
||||
associatedtype DataModel
|
||||
associatedtype Position
|
||||
|
||||
typealias TapHandlerClosure = (DataModel, Position) -> Bool
|
||||
|
||||
var placemarkPosition: Position { get }
|
||||
var dataModel: DataModel { get }
|
||||
var state: MarkerState { get set }
|
||||
|
||||
///
|
||||
/// Validates whether the current tap could be handled or not
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - DataModel: A data model of the current placemark manager
|
||||
/// - Position: A position of the current placemark
|
||||
///
|
||||
/// - Returns: A `Bool` value of the handling desicion
|
||||
///
|
||||
var tapHandler: TapHandlerClosure? { get set }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIMapUtils'
|
||||
s.version = '1.33.0'
|
||||
s.version = '1.39.0'
|
||||
s.summary = 'Set of helpers for map objects clustering and interacting.'
|
||||
s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.homepage = 'https://gitlab.ti/touchinstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
s.author = { 'petropavel13' => 'ivan.smolin@touchin.ru' }
|
||||
s.source = { :git => 'https://github.com/TouchInstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
s.source = { :git => 'https://gitlab.ti/touchinstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
|
||||
s.ios.deployment_target = '10.0'
|
||||
s.swift_versions = ['5.3']
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIMoyaNetworking'
|
||||
s.version = '1.33.0'
|
||||
s.version = '1.39.0'
|
||||
s.summary = 'Moya + Swagger network service.'
|
||||
s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.homepage = 'https://gitlab.ti/touchinstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
s.author = { 'petropavel13' => 'ivan.smolin@touchin.ru' }
|
||||
s.source = { :git => 'https://github.com/TouchInstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
s.source = { :git => 'https://gitlab.ti/touchinstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
|
||||
s.ios.deployment_target = '11.0'
|
||||
s.swift_versions = ['5.3']
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TINetworking'
|
||||
s.version = '1.33.0'
|
||||
s.version = '1.39.0'
|
||||
s.summary = 'Swagger-frendly networking layer helpers.'
|
||||
s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.homepage = 'https://gitlab.ti/touchinstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
s.author = { 'petropavel13' => 'ivan.smolin@touchin.ru' }
|
||||
s.source = { :git => 'https://github.com/TouchInstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
s.source = { :git => 'https://gitlab.ti/touchinstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
|
||||
s.ios.deployment_target = '10.0'
|
||||
s.swift_versions = ['5.3']
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TINetworkingCache'
|
||||
s.version = '1.33.0'
|
||||
s.version = '1.39.0'
|
||||
s.summary = 'Caching results of EndpointRequests.'
|
||||
s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.homepage = 'https://gitlab.ti/touchinstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
s.author = { 'petropavel13' => 'ivan.smolin@touchin.ru' }
|
||||
s.source = { :git => 'https://github.com/TouchInstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
s.source = { :git => 'https://gitlab.ti/touchinstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
|
||||
s.ios.deployment_target = '11.0'
|
||||
s.swift_versions = ['5.3']
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIPagination'
|
||||
s.version = '1.33.0'
|
||||
s.version = '1.39.0'
|
||||
s.summary = 'Generic pagination component.'
|
||||
s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.homepage = 'https://gitlab.ti/touchinstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
s.author = { 'petropavel13' => 'ivan.smolin@touchin.ru' }
|
||||
s.source = { :git => 'https://github.com/TouchInstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
s.source = { :git => 'https://gitlab.ti/touchinstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
|
||||
s.ios.deployment_target = '10.0'
|
||||
s.swift_versions = ['5.3']
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TISwiftUICore'
|
||||
s.version = '1.33.0'
|
||||
s.version = '1.39.0'
|
||||
s.summary = 'Core UI elements: protocols, views and helpers.'
|
||||
s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.homepage = 'https://gitlab.ti/touchinstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
s.author = { 'petropavel13' => 'ivan.smolin@touchin.ru' }
|
||||
s.source = { :git => 'https://github.com/TouchInstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
s.source = { :git => 'https://gitlab.ti/touchinstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
|
||||
s.ios.deployment_target = '13.0'
|
||||
s.swift_versions = ['5.3']
|
||||
|
|
|
|||
|
|
@ -1,14 +1,21 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TISwiftUtils'
|
||||
s.version = '1.33.0'
|
||||
s.version = '1.39.0'
|
||||
s.summary = 'Bunch of useful helpers for Swift development.'
|
||||
s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.homepage = 'https://gitlab.ti/touchinstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
s.author = { 'petropavel13' => 'ivan.smolin@touchin.ru' }
|
||||
s.source = { :git => 'https://github.com/TouchInstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
s.source = { :git => 'https://gitlab.ti/touchinstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
|
||||
s.ios.deployment_target = '9.0'
|
||||
s.swift_versions = ['5.3']
|
||||
|
||||
s.source_files = s.name + '/Sources/**/*'
|
||||
sources = 'Sources/**/*.swift'
|
||||
if ENV["DEVELOPMENT_INSTALL"] # installing using :path =>
|
||||
s.source_files = sources
|
||||
s.exclude_files = s.name + '.app'
|
||||
else
|
||||
s.source_files = s.name + '/' + sources
|
||||
s.exclude_files = s.name + '/*.app'
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
//
|
||||
// Copyright (c) 2023 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
import TableKit
|
||||
import TIUIKitCore
|
||||
|
||||
extension TableRow: AppearanceConfigurable where CellType: AppearanceConfigurable {
|
||||
private static var configureAppearanceActionId: String {
|
||||
"TableRowConfigureAppearanceActionId"
|
||||
}
|
||||
|
||||
public func with(appearance: CellType.Appearance) -> Self {
|
||||
configure(appearance: appearance)
|
||||
return self
|
||||
}
|
||||
|
||||
public func configure(appearance: CellType.Appearance) {
|
||||
removeAction(forActionId: Self.configureAppearanceActionId)
|
||||
|
||||
let action = TableRowAction<CellType>(.configure) { options in
|
||||
options.cell?.configure(appearance: appearance)
|
||||
}
|
||||
|
||||
action.id = Self.configureAppearanceActionId
|
||||
on(action)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
//
|
||||
// Copyright (c) 2023 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
import TableKit
|
||||
import TIUIElements
|
||||
import TIUIKitCore
|
||||
import UIKit
|
||||
|
||||
public extension WrappableView where Self: ConfigurableView {
|
||||
typealias InTableRow = TableKit.TableRow<Self.InTableCell>
|
||||
typealias InSeparatableRow = TableKit.TableRow<Self.InSeparatableTableCell>
|
||||
}
|
||||
|
||||
public extension WrappableView where Self: UITableViewCell & ConfigurableCell {
|
||||
typealias TableRow = TableKit.TableRow<Self>
|
||||
}
|
||||
|
|
@ -31,23 +31,23 @@ public extension Array where Element == SeparatorRowBox {
|
|||
}
|
||||
|
||||
/// Configure separators from SeparatorRowBox array
|
||||
/// - parameter extreme: Configuration that will be used for extreme values, for first or last row
|
||||
/// - parameter middle: Configuration for intermediate rows
|
||||
func configureSeparators(extreme extremeSeparatorConfiguration: SeparatorConfiguration,
|
||||
middle middleSeparatorConfiguration: SeparatorConfiguration) {
|
||||
/// - parameter extreme: Appearance that will be used for extreme values, for first or last row
|
||||
/// - parameter middle: Apearance for intermediate rows
|
||||
func configureSeparators(extreme extremeSeparatorsAppearance: SeparatorAppearance,
|
||||
middle middleSeparatorsAppearance: SeparatorAppearance) {
|
||||
|
||||
configureSeparators(first: extremeSeparatorConfiguration,
|
||||
middle: middleSeparatorConfiguration,
|
||||
last: extremeSeparatorConfiguration)
|
||||
configureSeparators(first: extremeSeparatorsAppearance,
|
||||
middle: middleSeparatorsAppearance,
|
||||
last: extremeSeparatorsAppearance)
|
||||
}
|
||||
|
||||
/// Configure separators from SeparatorRowBox array
|
||||
/// - parameter first: Configuration of the top separator of the first row
|
||||
/// - parameter middle: Configuration of the separators between the rows
|
||||
/// - parameter last: Configuration of the bottom separator of the last row
|
||||
func configureSeparators(first firstSeparatorConfiguration: SeparatorConfiguration,
|
||||
middle middleSeparatorConfiguration: SeparatorConfiguration,
|
||||
last lastSeparatorConfiguration: SeparatorConfiguration) {
|
||||
/// - parameter first: Appearance of the top separator of the first row
|
||||
/// - parameter middle: Appearance of the separators between the rows
|
||||
/// - parameter last: Appearance of the bottom separator of the last row
|
||||
func configureSeparators(first firstSeparatorsAppearance: SeparatorAppearance,
|
||||
middle middleSeparatorsAppearance: SeparatorAppearance,
|
||||
last lastSeparatorsAppearance: SeparatorAppearance) {
|
||||
|
||||
if isEmpty {
|
||||
return
|
||||
|
|
@ -55,12 +55,12 @@ public extension Array where Element == SeparatorRowBox {
|
|||
|
||||
switch count {
|
||||
case 1:
|
||||
first?.set(separatorType: .full(firstSeparatorConfiguration, lastSeparatorConfiguration))
|
||||
first?.set(separatorType: .full(top: firstSeparatorsAppearance, bottom: lastSeparatorsAppearance))
|
||||
|
||||
default:
|
||||
dropFirst().dropLast().forEach { $0.set(separatorType: .bottom(middleSeparatorConfiguration)) }
|
||||
first?.set(separatorType: .full(firstSeparatorConfiguration, middleSeparatorConfiguration))
|
||||
last?.set(separatorType: .bottom(lastSeparatorConfiguration))
|
||||
dropFirst().dropLast().forEach { $0.set(separatorType: .bottom(middleSeparatorsAppearance)) }
|
||||
first?.set(separatorType: .full(top: firstSeparatorsAppearance, bottom: middleSeparatorsAppearance))
|
||||
last?.set(separatorType: .bottom(lastSeparatorsAppearance))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
//
|
||||
// Copyright (c) 2023 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
import TableKit
|
||||
import TIUIElements
|
||||
import TIUIKitCore
|
||||
|
||||
extension ContainerTableViewCell: ConfigurableCell where View: ConfigurableView {
|
||||
public func configure(with viewModel: View.ViewModelType) {
|
||||
wrappedView.configure(with: viewModel)
|
||||
}
|
||||
}
|
||||
|
|
@ -23,28 +23,29 @@
|
|||
import TableKit
|
||||
import TIUIElements
|
||||
|
||||
private let configureSeparatorActionId = "TableRowConfigureSeparatorActionId"
|
||||
extension TableRow: SeparatorsConfigurable where CellType: SeparatorsConfigurable {
|
||||
private static var configureSeparatorsActionId: String {
|
||||
"TableRowConfigureSeparatorsActionId"
|
||||
}
|
||||
|
||||
public extension TableRow where CellType: SeparatorConfigurable {
|
||||
|
||||
func with(separatorType: ViewSeparatorType) -> Self {
|
||||
set(separatorType: separatorType)
|
||||
public func with(separators: SeparatorsConfiguration) -> Self {
|
||||
configureSeparators(with: separators)
|
||||
return self
|
||||
}
|
||||
|
||||
func set(separatorType: ViewSeparatorType) {
|
||||
removeAction(forActionId: configureSeparatorActionId)
|
||||
public func configureSeparators(with separatorsConfiguration: SeparatorsConfiguration) {
|
||||
removeAction(forActionId: Self.configureSeparatorsActionId)
|
||||
|
||||
let action = TableRowAction<CellType>(.configure) {
|
||||
$0.cell?.configureSeparators(with: separatorType)
|
||||
let action = TableRowAction<CellType>(.configure) { options in
|
||||
options.cell?.configureSeparators(with: separatorsConfiguration)
|
||||
}
|
||||
|
||||
action.id = configureSeparatorActionId
|
||||
action.id = Self.configureSeparatorsActionId
|
||||
on(action)
|
||||
}
|
||||
}
|
||||
|
||||
public extension TableRow where CellType: SeparatorConfigurable {
|
||||
public extension TableRow where CellType: SeparatorsConfigurable {
|
||||
|
||||
/// TableRow typed as SeparatorRowBox
|
||||
var separatorRowBox: SeparatorRowBox {
|
||||
|
|
|
|||
|
|
@ -21,21 +21,21 @@
|
|||
//
|
||||
|
||||
import TableKit
|
||||
import TIUIElements
|
||||
import TISwiftUtils
|
||||
import TIUIElements
|
||||
|
||||
/// Class that used to configure separators when multiply cells presented in one section
|
||||
public final class SeparatorRowBox {
|
||||
private let setSeparatorHandler: ParameterClosure<ViewSeparatorType>
|
||||
private let setSeparatorHandler: ParameterClosure<SeparatorsConfiguration>
|
||||
|
||||
public func set(separatorType: ViewSeparatorType) {
|
||||
public func set(separatorType: SeparatorsConfiguration) {
|
||||
setSeparatorHandler(separatorType)
|
||||
}
|
||||
|
||||
public let row: Row
|
||||
|
||||
public init<T>(row: TableRow<T>) where T: SeparatorConfigurable {
|
||||
public init<T>(row: TableRow<T>) where T: SeparatorsConfigurable {
|
||||
self.row = row
|
||||
setSeparatorHandler = row.set
|
||||
setSeparatorHandler = row.configureSeparators(with:)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TITableKitUtils'
|
||||
s.version = '1.33.0'
|
||||
s.version = '1.39.0'
|
||||
s.summary = 'Set of helpers for TableKit classes.'
|
||||
s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.homepage = 'https://gitlab.ti/touchinstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
s.author = { 'petropavel13' => 'ivan.smolin@touchin.ru' }
|
||||
s.source = { :git => 'https://github.com/TouchInstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
s.source = { :git => 'https://gitlab.ti/touchinstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
|
||||
s.ios.deployment_target = '11.0'
|
||||
s.swift_versions = ['5.3']
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TITransitions'
|
||||
s.version = '1.33.0'
|
||||
s.version = '1.34.1'
|
||||
s.summary = 'Set of custom transitions to present controller. '
|
||||
s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.homepage = 'https://gitlab.ti/touchinstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
s.author = { 'Loupehope' => 'vladislav.suhomlinov@touchin.ru' }
|
||||
s.source = { :git => 'https://github.com/TouchInstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
s.source = { :git => 'https://gitlab.ti/touchinstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
|
||||
s.ios.deployment_target = '11.0'
|
||||
s.swift_versions = ['5.0']
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
ENV["DEVELOPMENT_INSTALL"] = "true"
|
||||
|
||||
target 'TIUIElements' do
|
||||
platform :ios, 11.0
|
||||
use_frameworks!
|
||||
|
||||
pod 'TIUIElements', :path => '../../../../TIUIElements/TIUIElements.podspec'
|
||||
pod 'TIUIKitCore', :path => '../../../../TIUIKitCore/TIUIKitCore.podspec'
|
||||
pod 'TISwiftUtils', :path => '../../../../TISwiftUtils/TISwiftUtils.podspec'
|
||||
end
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
//
|
||||
// Copyright (c) 2023 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
import TIUIKitCore
|
||||
import UIKit
|
||||
|
||||
extension UIButton {
|
||||
open class BaseAppearance<Layout: ViewLayout>: UIView.BaseAppearance<Layout> {
|
||||
|
||||
public var textAttributes: BaseTextAttributes?
|
||||
public var contentInsets: UIEdgeInsets
|
||||
public var titleInsets: UIEdgeInsets
|
||||
public var imageInsets: UIEdgeInsets
|
||||
|
||||
public init(layout: Layout = .defaultLayout,
|
||||
backgroundColor: UIColor = .clear,
|
||||
border: UIViewBorder = .init(),
|
||||
shadow: UIViewShadow? = nil,
|
||||
textAttributes: BaseTextAttributes? = nil,
|
||||
contentInsets: UIEdgeInsets = .zero,
|
||||
titleInsets: UIEdgeInsets = .zero,
|
||||
imageInsets: UIEdgeInsets = .zero) {
|
||||
|
||||
self.textAttributes = textAttributes
|
||||
self.contentInsets = contentInsets
|
||||
self.titleInsets = titleInsets
|
||||
self.imageInsets = imageInsets
|
||||
|
||||
super.init(layout: layout,
|
||||
backgroundColor: backgroundColor,
|
||||
border: border,
|
||||
shadow: shadow)
|
||||
}
|
||||
}
|
||||
|
||||
public final class DefaultAppearance: BaseAppearance<UIView.DefaultWrappedLayout>, WrappedViewAppearance {
|
||||
public static var defaultAppearance: Self {
|
||||
Self()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
//
|
||||
// Copyright (c) 2023 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
import TIUIKitCore
|
||||
import UIKit
|
||||
|
||||
extension UIButton {
|
||||
public func configureUIButton(appearance: UIButton.BaseAppearance<some ViewLayout>) {
|
||||
appearance.textAttributes?
|
||||
.configure(button: self,
|
||||
with: titleLabel?.attributedText?.string ?? titleLabel?.text)
|
||||
|
||||
if #available(iOS 15, *) {
|
||||
configuration?.contentInsets = .init(insets: appearance.contentInsets)
|
||||
|
||||
if configuration?.imagePlacement == .leading {
|
||||
let padding = appearance.titleInsets.left + appearance.imageInsets.right
|
||||
configuration?.imagePadding = padding
|
||||
}
|
||||
|
||||
if configuration?.imagePlacement == .trailing {
|
||||
let padding = appearance.titleInsets.right + appearance.imageInsets.left
|
||||
configuration?.imagePadding = padding
|
||||
}
|
||||
|
||||
} else {
|
||||
contentEdgeInsets = appearance.contentInsets
|
||||
titleEdgeInsets = appearance.titleInsets
|
||||
imageEdgeInsets = appearance.imageInsets
|
||||
}
|
||||
|
||||
super.configureUIView(appearance: appearance)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
//
|
||||
// Copyright (c) 2023 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
import TIUIKitCore
|
||||
import UIKit
|
||||
|
||||
extension UILabel {
|
||||
open class BaseAppearance<Layout: ViewLayout>: UIView.BaseAppearance<Layout> {
|
||||
|
||||
public var textAttributes: BaseTextAttributes?
|
||||
|
||||
public init(layout: Layout = .defaultLayout,
|
||||
backgroundColor: UIColor = .clear,
|
||||
border: UIViewBorder = .init(),
|
||||
shadow: UIViewShadow? = nil,
|
||||
textAttributes: BaseTextAttributes? = nil) {
|
||||
|
||||
self.textAttributes = textAttributes
|
||||
|
||||
super.init(layout: layout,
|
||||
backgroundColor: backgroundColor,
|
||||
border: border,
|
||||
shadow: shadow)
|
||||
}
|
||||
}
|
||||
|
||||
public final class DefaultAppearance: BaseAppearance<UIView.DefaultWrappedLayout>, WrappedViewAppearance {
|
||||
public static var defaultAppearance: DefaultAppearance {
|
||||
DefaultAppearance()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// Copyright (c) 2020 Touch Instinct
|
||||
// Copyright (c) 2023 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
|
|
@ -20,17 +20,15 @@
|
|||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
import TIUIKitCore
|
||||
import UIKit
|
||||
|
||||
public struct SeparatorConfiguration {
|
||||
extension UILabel {
|
||||
public func configureUILabel(appearance: BaseAppearance<some ViewLayout>) {
|
||||
appearance.textAttributes?
|
||||
.configure(label: self,
|
||||
with: attributedText?.string ?? text)
|
||||
|
||||
public let color: UIColor
|
||||
public let insets: UIEdgeInsets
|
||||
public let height: CGFloat
|
||||
|
||||
public init(color: UIColor, insets: UIEdgeInsets = .zero, height: CGFloat = 1) {
|
||||
self.color = color
|
||||
self.insets = insets
|
||||
self.height = height
|
||||
super.configureUIView(appearance: appearance)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
//
|
||||
// Copyright (c) 2023 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
import TIUIKitCore
|
||||
import UIKit
|
||||
|
||||
extension UIView {
|
||||
public func configureUIView(appearance: BaseAppearance<some ViewLayout>) {
|
||||
backgroundColor = appearance.backgroundColor
|
||||
layer.masksToBounds = true
|
||||
layer.maskedCorners = appearance.border.roundedCorners
|
||||
layer.cornerRadius = appearance.border.cornerRadius
|
||||
layer.borderWidth = appearance.border.width
|
||||
layer.borderColor = appearance.border.color.cgColor
|
||||
|
||||
guard let shadow = appearance.shadow else {
|
||||
return
|
||||
}
|
||||
|
||||
layer.shadowOpacity = shadow.opacity
|
||||
layer.shadowOffset = shadow.offset
|
||||
layer.shadowColor = shadow.color.cgColor
|
||||
layer.shadowRadius = shadow.radius
|
||||
clipsToBounds = false
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,164 @@
|
|||
//
|
||||
// Copyright (c) 2023 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
import TIUIKitCore
|
||||
import UIKit
|
||||
|
||||
extension UIView {
|
||||
|
||||
// MARK: - Layout Variations
|
||||
|
||||
public struct NoLayout: ViewLayout {
|
||||
public static var defaultLayout: Self {
|
||||
Self()
|
||||
}
|
||||
}
|
||||
|
||||
open class BaseSizeLayout {
|
||||
public var size: CGSize
|
||||
|
||||
public init(size: CGSize = .infinity) {
|
||||
self.size = size
|
||||
}
|
||||
}
|
||||
|
||||
public final class DefaultLayout: BaseSizeLayout, SizeViewLayout {
|
||||
public static var defaultLayout: Self {
|
||||
Self()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - WrappedView Layout
|
||||
|
||||
open class BaseWrappedLayout: BaseSizeLayout {
|
||||
public var centerOffset: UIOffset
|
||||
public var insets: UIEdgeInsets
|
||||
|
||||
public init(insets: UIEdgeInsets = .zero,
|
||||
size: CGSize = .infinity,
|
||||
centerOffset: UIOffset = .nan) {
|
||||
|
||||
self.centerOffset = centerOffset
|
||||
self.insets = insets
|
||||
|
||||
super.init(size: size)
|
||||
}
|
||||
}
|
||||
|
||||
open class BaseSpecedWrappedLayout: BaseWrappedLayout {
|
||||
public var spacing: CGFloat
|
||||
|
||||
public init(insets: UIEdgeInsets = .zero,
|
||||
size: CGSize = .infinity,
|
||||
centerOffset: UIOffset = .nan,
|
||||
spacing: CGFloat = .zero) {
|
||||
self.spacing = spacing
|
||||
|
||||
super.init(insets: insets, size: size, centerOffset: centerOffset)
|
||||
}
|
||||
}
|
||||
|
||||
public final class DefaultWrappedLayout: BaseWrappedLayout, WrappedViewLayout {
|
||||
public static var defaultLayout: Self {
|
||||
Self()
|
||||
}
|
||||
}
|
||||
|
||||
public final class DefaultSpacedWrappedLayout: BaseSpecedWrappedLayout, SpacedWrappedViewLayout {
|
||||
public static var defaultLayout: Self {
|
||||
Self()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Appearance Variations
|
||||
|
||||
open class BaseAppearance<Layout: ViewLayout> {
|
||||
public var layout: Layout
|
||||
|
||||
public var backgroundColor: UIColor
|
||||
public var border: UIViewBorder
|
||||
public var shadow: UIViewShadow?
|
||||
|
||||
public init(layout: Layout = .defaultLayout,
|
||||
backgroundColor: UIColor = .clear,
|
||||
border: UIViewBorder = .init(),
|
||||
shadow: UIViewShadow? = nil) {
|
||||
|
||||
self.layout = layout
|
||||
self.backgroundColor = backgroundColor
|
||||
self.border = border
|
||||
self.shadow = shadow
|
||||
}
|
||||
}
|
||||
|
||||
public final class DefaultAppearance: BaseAppearance<DefaultLayout>, ViewAppearance {
|
||||
public static var defaultAppearance: Self {
|
||||
Self()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - WrappedView Appearance
|
||||
|
||||
open class BaseWrappedViewHolderAppearance<SubviewAppearance: WrappedViewAppearance,
|
||||
Layout: ViewLayout>: BaseAppearance<Layout> {
|
||||
|
||||
public var subviewAppearance: SubviewAppearance
|
||||
|
||||
public init(layout: Layout = .defaultLayout,
|
||||
backgroundColor: UIColor = .clear,
|
||||
border: UIViewBorder = .init(),
|
||||
shadow: UIViewShadow? = nil,
|
||||
subviewAppearance: SubviewAppearance = .defaultAppearance) {
|
||||
|
||||
self.subviewAppearance = subviewAppearance
|
||||
|
||||
super.init(layout: layout,
|
||||
backgroundColor: backgroundColor,
|
||||
border: border,
|
||||
shadow: shadow)
|
||||
}
|
||||
}
|
||||
|
||||
public final class DefaultWrappedViewHolderAppearance<SubviewAppearance: WrappedViewAppearance,
|
||||
Layout: ViewLayout>: BaseWrappedViewHolderAppearance<SubviewAppearance, Layout>,
|
||||
WrappedViewHolderAppearance {
|
||||
public static var defaultAppearance: Self {
|
||||
Self()
|
||||
}
|
||||
}
|
||||
|
||||
public final class DefaultWrappedAppearance: BaseAppearance<DefaultWrappedLayout>, WrappedViewAppearance {
|
||||
public static var defaultAppearance: Self {
|
||||
Self()
|
||||
}
|
||||
}
|
||||
|
||||
public final class DefaultSpacedWrappedAppearance: BaseAppearance<DefaultSpacedWrappedLayout>, WrappedViewAppearance {
|
||||
public static var defaultAppearance: Self {
|
||||
Self()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension UIView.DefaultWrappedViewHolderAppearance: WrappedViewAppearance where Layout: WrappedViewLayout {
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// Copyright (c) 2020 Touch Instinct
|
||||
// Copyright (c) 2023 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
|
|
@ -22,7 +22,8 @@
|
|||
|
||||
import UIKit
|
||||
|
||||
open class BaseSeparatorCell: BaseInitializableCell, SeparatorConfigurable {
|
||||
open class ContainerSeparatorTableViewCell<View: UIView>: ContainerTableViewCell<View>, SeparatorsConfigurable {
|
||||
|
||||
private lazy var topSeparatorView = createTopSeparator()
|
||||
private lazy var bottomSeparatorView = createBottomSeparator()
|
||||
|
||||
|
|
@ -52,23 +53,25 @@ open class BaseSeparatorCell: BaseInitializableCell, SeparatorConfigurable {
|
|||
contentView.addSubview(bottomSeparatorView)
|
||||
}
|
||||
|
||||
public func configureSeparators(with separatorType: ViewSeparatorType) {
|
||||
topSeparatorView.isHidden = separatorType.topIsHidden
|
||||
bottomSeparatorView.isHidden = separatorType.bottomIsHidden
|
||||
// MARK: - SeparatorsConfigurable
|
||||
|
||||
switch separatorType {
|
||||
public func configureSeparators(with separatorsConfiguration: SeparatorsConfiguration) {
|
||||
topSeparatorView.isHidden = separatorsConfiguration.topIsHidden
|
||||
bottomSeparatorView.isHidden = separatorsConfiguration.bottomIsHidden
|
||||
|
||||
switch separatorsConfiguration {
|
||||
case .none:
|
||||
break
|
||||
|
||||
case let .bottom(configuration):
|
||||
updateBottomSeparator(with: configuration)
|
||||
case let .bottom(appearance):
|
||||
updateBottomSeparator(with: appearance)
|
||||
|
||||
case let .top(configuration):
|
||||
updateTopSeparator(with: configuration)
|
||||
case let .top(appearance):
|
||||
updateTopSeparator(with: appearance)
|
||||
|
||||
case let .full(topConfiguration, bottomConfiguration):
|
||||
updateTopSeparator(with: topConfiguration)
|
||||
updateBottomSeparator(with: bottomConfiguration)
|
||||
case let .full(topAppearance, bottomAppearance):
|
||||
updateTopSeparator(with: topAppearance)
|
||||
updateBottomSeparator(with: bottomAppearance)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -127,26 +130,26 @@ open class BaseSeparatorCell: BaseInitializableCell, SeparatorConfigurable {
|
|||
$0.translatesAutoresizingMaskIntoConstraints = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension BaseSeparatorCell {
|
||||
func updateTopSeparator(with configuration: SeparatorConfiguration) {
|
||||
topSeparatorView.backgroundColor = configuration.color
|
||||
// MARK: - Private
|
||||
|
||||
topViewHeightConstraint?.constant = configuration.height
|
||||
private func updateTopSeparator(with appearance: SeparatorAppearance) {
|
||||
topSeparatorView.configureUIView(appearance: appearance)
|
||||
|
||||
topViewTopConstraint?.constant = configuration.insets.top
|
||||
topViewLeftConstraint?.constant = configuration.insets.left
|
||||
topViewRightConstraint?.constant = configuration.insets.right
|
||||
topViewHeightConstraint?.constant = appearance.layout.size.height
|
||||
|
||||
topViewTopConstraint?.constant = appearance.layout.insets.top
|
||||
topViewLeftConstraint?.constant = appearance.layout.insets.left
|
||||
topViewRightConstraint?.constant = appearance.layout.insets.right
|
||||
}
|
||||
|
||||
func updateBottomSeparator(with configuration: SeparatorConfiguration) {
|
||||
bottomSeparatorView.backgroundColor = configuration.color
|
||||
private func updateBottomSeparator(with appearance: SeparatorAppearance) {
|
||||
bottomSeparatorView.configureUIView(appearance: appearance)
|
||||
|
||||
bottomViewHeightConstraint?.constant = configuration.height
|
||||
bottomViewHeightConstraint?.constant = appearance.layout.size.height
|
||||
|
||||
bottomViewBottomConstraint?.constant = configuration.insets.bottom
|
||||
bottomViewLeftConstraint?.constant = configuration.insets.left
|
||||
bottomViewRightConstraint?.constant = configuration.insets.right
|
||||
bottomViewBottomConstraint?.constant = appearance.layout.insets.bottom
|
||||
bottomViewLeftConstraint?.constant = appearance.layout.insets.left
|
||||
bottomViewRightConstraint?.constant = appearance.layout.insets.right
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
//
|
||||
// Copyright (c) 2023 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
import TIUIKitCore
|
||||
import UIKit
|
||||
|
||||
public final class SeparatorLayout: UIView.BaseWrappedLayout, WrappedViewLayout {
|
||||
public static var defaultLayout: Self {
|
||||
Self()
|
||||
}
|
||||
|
||||
public init(insets: UIEdgeInsets = .zero,
|
||||
size: CGSize = .fixedHeight(0.5)) {
|
||||
|
||||
super.init(insets: insets,
|
||||
size: size,
|
||||
centerOffset: .nan)
|
||||
}
|
||||
}
|
||||
|
||||
public final class SeparatorAppearance: UIView.BaseAppearance<SeparatorLayout>, ViewAppearance {
|
||||
public static var defaultAppearance: Self {
|
||||
Self(backgroundColor: .lightGray)
|
||||
}
|
||||
}
|
||||
|
|
@ -20,6 +20,6 @@
|
|||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
public protocol SeparatorConfigurable {
|
||||
func configureSeparators(with separatorType: ViewSeparatorType)
|
||||
public protocol SeparatorsConfigurable {
|
||||
func configureSeparators(with separatorsConfiguration: SeparatorsConfiguration)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,22 +20,14 @@
|
|||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
public enum ViewSeparatorType {
|
||||
|
||||
/// All separators for view is hidden
|
||||
public enum SeparatorsConfiguration {
|
||||
case none
|
||||
|
||||
/// Show only top separator
|
||||
case top(SeparatorConfiguration)
|
||||
|
||||
/// Show only bottom separator
|
||||
case bottom(SeparatorConfiguration)
|
||||
|
||||
/// First configuration for top, second for bottom
|
||||
case full(SeparatorConfiguration, SeparatorConfiguration)
|
||||
case top(SeparatorAppearance)
|
||||
case bottom(SeparatorAppearance)
|
||||
case full(top: SeparatorAppearance, bottom: SeparatorAppearance)
|
||||
}
|
||||
|
||||
public extension ViewSeparatorType {
|
||||
public extension SeparatorsConfiguration {
|
||||
|
||||
/// Determine if bottom separator is hidden.
|
||||
var bottomIsHidden: Bool {
|
||||
|
|
@ -48,7 +40,7 @@ public extension ViewSeparatorType {
|
|||
}
|
||||
|
||||
/// Returns top configuration if type is top or full.
|
||||
var topConfiguration: SeparatorConfiguration? {
|
||||
var topConfiguration: SeparatorAppearance? {
|
||||
switch self {
|
||||
case let .top(configuration), let .full(configuration, _):
|
||||
return configuration
|
||||
|
|
@ -59,7 +51,7 @@ public extension ViewSeparatorType {
|
|||
}
|
||||
|
||||
/// Returns bottom configuration if type is bottom or full.
|
||||
var bottomConfiguration: SeparatorConfiguration? {
|
||||
var bottomConfiguration: SeparatorAppearance? {
|
||||
switch self {
|
||||
case let .bottom(configuration), let .full(_, configuration):
|
||||
return configuration
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
//
|
||||
// Copyright (c) 2023 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
import TIUIKitCore
|
||||
import UIKit
|
||||
|
||||
extension WrappedViewLayout {
|
||||
func setupSize(widthConstraint: NSLayoutConstraint?,
|
||||
heightConstraint: NSLayoutConstraint?) {
|
||||
|
||||
if size.width.isFinite {
|
||||
widthConstraint?.constant = size.width
|
||||
widthConstraint?.isActive = true
|
||||
} else {
|
||||
widthConstraint?.isActive = false
|
||||
}
|
||||
|
||||
if size.height.isFinite {
|
||||
heightConstraint?.constant = size.height
|
||||
heightConstraint?.isActive = true
|
||||
} else {
|
||||
heightConstraint?.isActive = false
|
||||
}
|
||||
}
|
||||
|
||||
func setupCenterYOffset(centerYConstraint: NSLayoutConstraint?,
|
||||
topConstraint: NSLayoutConstraint?,
|
||||
bottomConstraint: NSLayoutConstraint?) {
|
||||
|
||||
let centerYOffset = centerOffset.vertical
|
||||
|
||||
if centerYOffset.isFinite {
|
||||
centerYConstraint?.constant = centerYOffset
|
||||
centerYConstraint?.isActive = true
|
||||
topConstraint?.isActive = false
|
||||
bottomConstraint?.isActive = false
|
||||
|
||||
} else {
|
||||
topConstraint?.constant = insets.top
|
||||
bottomConstraint?.constant = -insets.bottom
|
||||
centerYConstraint?.isActive = false
|
||||
topConstraint?.isActive = true
|
||||
bottomConstraint?.isActive = true
|
||||
}
|
||||
}
|
||||
|
||||
func setupCenterXOffset(centerXConstraint: NSLayoutConstraint?,
|
||||
leadingConstraint: NSLayoutConstraint?,
|
||||
trailingConstraint: NSLayoutConstraint?) {
|
||||
|
||||
let centerXOffset = centerOffset.horizontal
|
||||
|
||||
if centerXOffset.isFinite {
|
||||
centerXConstraint?.constant = centerXOffset
|
||||
centerXConstraint?.isActive = true
|
||||
leadingConstraint?.isActive = false
|
||||
trailingConstraint?.isActive = false
|
||||
|
||||
} else {
|
||||
leadingConstraint?.constant = insets.left
|
||||
trailingConstraint?.constant = -insets.right
|
||||
centerXConstraint?.isActive = false
|
||||
leadingConstraint?.isActive = true
|
||||
trailingConstraint?.isActive = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
//
|
||||
// Copyright (c) 2023 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
import TIUIKitCore
|
||||
import UIKit
|
||||
|
||||
open class BaseListItemAppearance<LeadingViewAppearance: WrappedViewAppearance,
|
||||
MiddleViewAppearance: WrappedViewAppearance,
|
||||
TrailingViewAppearance: WrappedViewAppearance>: UIView.BaseAppearance<UIView.DefaultWrappedLayout> {
|
||||
|
||||
public var leadingViewAppearance: LeadingViewAppearance
|
||||
public var middleViewAppearance: MiddleViewAppearance
|
||||
public var trailingAppearance: TrailingViewAppearance
|
||||
|
||||
public init(layout: UIView.DefaultWrappedLayout = .defaultLayout,
|
||||
backgroundColor: UIColor = .clear,
|
||||
border: UIViewBorder = .init(),
|
||||
shadow: UIViewShadow? = nil,
|
||||
leadingViewAppearance: LeadingViewAppearance = .defaultAppearance,
|
||||
middleViewAppearance: MiddleViewAppearance = .defaultAppearance,
|
||||
trailingViewAppearance: TrailingViewAppearance = .defaultAppearance) {
|
||||
|
||||
self.leadingViewAppearance = leadingViewAppearance
|
||||
self.middleViewAppearance = middleViewAppearance
|
||||
self.trailingAppearance = trailingViewAppearance
|
||||
|
||||
super.init(layout: layout,
|
||||
backgroundColor: backgroundColor,
|
||||
border: border,
|
||||
shadow: shadow)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,258 @@
|
|||
//
|
||||
// Copyright (c) 2023 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
import TIUIKitCore
|
||||
import UIKit
|
||||
|
||||
open class BaseListItemView<LeadingView: UIView,
|
||||
MiddleView: UIView,
|
||||
TrailingView: UIView>: BaseInitializableView {
|
||||
|
||||
public let leadingView = LeadingView()
|
||||
public let middleView = MiddleView()
|
||||
public let trailingView = TrailingView()
|
||||
|
||||
// MARK: - Constraints
|
||||
|
||||
public var leadingViewLeadingToSuperviewConstraint: NSLayoutConstraint?
|
||||
public var leadingViewTopConstraint: NSLayoutConstraint?
|
||||
public var leadingViewBottomConstraint: NSLayoutConstraint?
|
||||
public var leadingViewCenterYConstraint: NSLayoutConstraint?
|
||||
public var leadingViewWidthConstraint: NSLayoutConstraint?
|
||||
public var leadingViewHeightConstraint: NSLayoutConstraint?
|
||||
|
||||
public var middleViewLeadingToSuperViewConstraint: NSLayoutConstraint?
|
||||
public var middleViewLeadingConstraint: NSLayoutConstraint?
|
||||
public var middleViewTopConstraint: NSLayoutConstraint?
|
||||
public var middleViewTrailingToSuperViewConstraint: NSLayoutConstraint?
|
||||
public var middleViewBottomConstraint: NSLayoutConstraint?
|
||||
public var middleViewCenterYConstraint: NSLayoutConstraint?
|
||||
public var middleViewWidthConstraint: NSLayoutConstraint?
|
||||
public var middleViewHeightConstraint: NSLayoutConstraint?
|
||||
|
||||
public var trailingViewLeadingToMiddleViewConstraint: NSLayoutConstraint?
|
||||
public var trailingViewTopConstraint: NSLayoutConstraint?
|
||||
public var trailingViewTrailingToSuperviewConstraint: NSLayoutConstraint?
|
||||
public var trailingViewBottomConstraint: NSLayoutConstraint?
|
||||
public var trailingViewCenterYConstraint: NSLayoutConstraint?
|
||||
public var trailingViewWidthConstraint: NSLayoutConstraint?
|
||||
public var trailingViewHeightConstraint: NSLayoutConstraint?
|
||||
|
||||
// MARK: - Public Properties
|
||||
|
||||
public var leadingViewConstraints: [NSLayoutConstraint?] {
|
||||
[
|
||||
leadingViewLeadingToSuperviewConstraint,
|
||||
leadingViewTopConstraint,
|
||||
leadingViewBottomConstraint,
|
||||
leadingViewCenterYConstraint,
|
||||
leadingViewHeightConstraint,
|
||||
leadingViewWidthConstraint
|
||||
]
|
||||
}
|
||||
|
||||
public var trailingViewConstraints: [NSLayoutConstraint?] {
|
||||
[
|
||||
trailingViewLeadingToMiddleViewConstraint,
|
||||
trailingViewTopConstraint,
|
||||
trailingViewBottomConstraint,
|
||||
trailingViewTrailingToSuperviewConstraint,
|
||||
trailingViewCenterYConstraint,
|
||||
trailingViewHeightConstraint,
|
||||
trailingViewWidthConstraint,
|
||||
]
|
||||
}
|
||||
|
||||
open var isLeadingViewHidden: Bool {
|
||||
leadingView.isHidden
|
||||
}
|
||||
|
||||
open var isTrailingViewHidden: Bool {
|
||||
trailingView.isHidden
|
||||
}
|
||||
|
||||
// MARK: - BaseInitializableView
|
||||
|
||||
open override func addViews() {
|
||||
super.addViews()
|
||||
|
||||
addSubviews(leadingView, middleView, trailingView)
|
||||
}
|
||||
|
||||
open override func configureLayout() {
|
||||
super.configureLayout()
|
||||
|
||||
[leadingView, middleView, trailingView]
|
||||
.forEach { $0.translatesAutoresizingMaskIntoConstraints = false }
|
||||
|
||||
leadingViewLeadingToSuperviewConstraint = leadingView.leadingAnchor.constraint(equalTo: leadingAnchor)
|
||||
leadingViewTopConstraint = leadingView.topAnchor.constraint(equalTo: topAnchor)
|
||||
leadingViewBottomConstraint = leadingView.bottomAnchor.constraint(equalTo: bottomAnchor)
|
||||
leadingViewCenterYConstraint = leadingView.centerYAnchor.constraint(equalTo: centerYAnchor)
|
||||
leadingViewWidthConstraint = leadingView.widthAnchor.constraint(equalToConstant: .zero)
|
||||
leadingViewHeightConstraint = leadingView.heightAnchor.constraint(equalToConstant: .zero)
|
||||
|
||||
middleViewLeadingToSuperViewConstraint = middleView.leadingAnchor.constraint(equalTo: leadingAnchor)
|
||||
middleViewLeadingConstraint = middleView.leadingAnchor.constraint(equalTo: leadingView.trailingAnchor)
|
||||
middleViewCenterYConstraint = middleView.centerYAnchor.constraint(equalTo: centerYAnchor)
|
||||
middleViewTopConstraint = middleView.topAnchor.constraint(equalTo: topAnchor)
|
||||
middleViewTrailingToSuperViewConstraint = middleView.trailingAnchor.constraint(equalTo: trailingAnchor)
|
||||
middleViewBottomConstraint = middleView.bottomAnchor.constraint(equalTo: bottomAnchor)
|
||||
middleViewWidthConstraint = middleView.widthAnchor.constraint(equalToConstant: .zero)
|
||||
middleViewHeightConstraint = middleView.heightAnchor.constraint(equalToConstant: .zero)
|
||||
|
||||
trailingViewLeadingToMiddleViewConstraint = trailingView.leadingAnchor.constraint(equalTo: middleView.trailingAnchor)
|
||||
trailingViewTopConstraint = trailingView.topAnchor.constraint(equalTo: topAnchor)
|
||||
trailingViewTrailingToSuperviewConstraint = trailingView.trailingAnchor.constraint(equalTo: trailingAnchor)
|
||||
trailingViewBottomConstraint = trailingView.bottomAnchor.constraint(equalTo: bottomAnchor)
|
||||
trailingViewCenterYConstraint = trailingView.centerYAnchor.constraint(equalTo: centerYAnchor)
|
||||
trailingViewWidthConstraint = trailingView.widthAnchor.constraint(equalToConstant: .zero)
|
||||
trailingViewHeightConstraint = trailingView.heightAnchor.constraint(equalToConstant: .zero)
|
||||
|
||||
NSLayoutConstraint.activate([
|
||||
leadingViewLeadingToSuperviewConstraint,
|
||||
leadingViewTopConstraint,
|
||||
leadingViewBottomConstraint,
|
||||
|
||||
middleViewLeadingConstraint,
|
||||
|
||||
trailingViewTopConstraint,
|
||||
trailingViewBottomConstraint,
|
||||
trailingViewLeadingToMiddleViewConstraint,
|
||||
trailingViewTrailingToSuperviewConstraint,
|
||||
].compactMap { $0 })
|
||||
|
||||
NSLayoutConstraint.deactivate([
|
||||
leadingViewCenterYConstraint,
|
||||
leadingViewWidthConstraint,
|
||||
leadingViewHeightConstraint,
|
||||
|
||||
middleViewLeadingToSuperViewConstraint,
|
||||
middleViewTopConstraint,
|
||||
middleViewTrailingToSuperViewConstraint,
|
||||
middleViewBottomConstraint,
|
||||
middleViewCenterYConstraint,
|
||||
middleViewWidthConstraint,
|
||||
middleViewHeightConstraint,
|
||||
|
||||
trailingViewCenterYConstraint,
|
||||
trailingViewHeightConstraint,
|
||||
trailingViewWidthConstraint,
|
||||
].compactMap { $0 })
|
||||
}
|
||||
|
||||
// MARK: - Public methods
|
||||
|
||||
public func configureBaseListItem(appearance: BaseListItemAppearance<some WrappedViewAppearance,
|
||||
some WrappedViewAppearance,
|
||||
some WrappedViewAppearance>) {
|
||||
|
||||
configureUIView(appearance: appearance)
|
||||
|
||||
updateLeadingViewLayout(leadingViewLayout: appearance.leadingViewAppearance.layout,
|
||||
middleViewLayout: appearance.middleViewAppearance.layout)
|
||||
|
||||
updateMiddleViewLayout(containerLayout: appearance.layout,
|
||||
middleViewLayout: appearance.middleViewAppearance.layout)
|
||||
|
||||
updateTrailingViewLayout(trailingViewLayout: appearance.trailingAppearance.layout,
|
||||
middleViewLayout: appearance.middleViewAppearance.layout)
|
||||
}
|
||||
|
||||
// MARK: - Private methdos
|
||||
|
||||
private func updateLeadingViewLayout(leadingViewLayout: WrappedViewLayout,
|
||||
middleViewLayout: WrappedViewLayout) {
|
||||
|
||||
let leadingToSuperviewContraint: NSLayoutConstraint?
|
||||
let leadingViewLeftInset: CGFloat
|
||||
|
||||
if isLeadingViewHidden {
|
||||
NSLayoutConstraint.deactivate(leadingViewConstraints.compactMap { $0 })
|
||||
middleViewLeadingToSuperViewConstraint?.isActive = true
|
||||
|
||||
leadingToSuperviewContraint = middleViewLeadingToSuperViewConstraint
|
||||
leadingViewLeftInset = middleViewLayout.insets.left
|
||||
|
||||
} else {
|
||||
middleViewLeadingConstraint?.constant = leadingViewLayout.insets.right + middleViewLayout.insets.left
|
||||
leadingViewLeadingToSuperviewConstraint?.isActive = true
|
||||
middleViewLeadingConstraint?.isActive = true
|
||||
middleViewLeadingToSuperViewConstraint?.isActive = false
|
||||
|
||||
leadingViewLayout.setupCenterYOffset(centerYConstraint: leadingViewCenterYConstraint,
|
||||
topConstraint: leadingViewTopConstraint,
|
||||
bottomConstraint: leadingViewBottomConstraint)
|
||||
|
||||
leadingViewLayout.setupSize(widthConstraint: leadingViewWidthConstraint,
|
||||
heightConstraint: leadingViewHeightConstraint)
|
||||
|
||||
leadingToSuperviewContraint = leadingViewLeadingToSuperviewConstraint
|
||||
leadingViewLeftInset = leadingViewLayout.insets.left
|
||||
}
|
||||
|
||||
leadingToSuperviewContraint?.constant = leadingViewLeftInset
|
||||
}
|
||||
|
||||
private func updateMiddleViewLayout(containerLayout: ViewLayout,
|
||||
middleViewLayout: WrappedViewLayout) {
|
||||
|
||||
middleViewLayout.setupCenterYOffset(centerYConstraint: middleViewCenterYConstraint,
|
||||
topConstraint: middleViewTopConstraint,
|
||||
bottomConstraint: middleViewBottomConstraint)
|
||||
|
||||
middleViewLayout.setupSize(widthConstraint: middleViewWidthConstraint,
|
||||
heightConstraint: middleViewHeightConstraint)
|
||||
}
|
||||
|
||||
private func updateTrailingViewLayout(trailingViewLayout: WrappedViewLayout,
|
||||
middleViewLayout: WrappedViewLayout) {
|
||||
|
||||
let trailingToSuperviewConstraint: NSLayoutConstraint?
|
||||
let trailingViewRightInset: CGFloat
|
||||
|
||||
if isTrailingViewHidden {
|
||||
NSLayoutConstraint.deactivate(trailingViewConstraints.compactMap { $0 })
|
||||
middleViewTrailingToSuperViewConstraint?.isActive = true
|
||||
|
||||
trailingToSuperviewConstraint = middleViewTrailingToSuperViewConstraint
|
||||
trailingViewRightInset = middleViewLayout.insets.right
|
||||
|
||||
} else {
|
||||
trailingViewLeadingToMiddleViewConstraint?.constant = middleViewLayout.insets.right + trailingViewLayout.insets.left
|
||||
trailingViewLeadingToMiddleViewConstraint?.isActive = true
|
||||
middleViewTrailingToSuperViewConstraint?.isActive = false
|
||||
|
||||
trailingViewLayout.setupCenterYOffset(centerYConstraint: trailingViewCenterYConstraint,
|
||||
topConstraint: trailingViewTopConstraint,
|
||||
bottomConstraint: trailingViewBottomConstraint)
|
||||
|
||||
trailingViewLayout.setupSize(widthConstraint: trailingViewWidthConstraint,
|
||||
heightConstraint: trailingViewWidthConstraint)
|
||||
|
||||
trailingToSuperviewConstraint = trailingViewTrailingToSuperviewConstraint
|
||||
trailingViewRightInset = trailingViewLayout.insets.right
|
||||
}
|
||||
|
||||
trailingToSuperviewConstraint?.constant = -trailingViewRightInset
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
//
|
||||
// Copyright (c) 2023 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import QuartzCore
|
||||
|
||||
open class BaseSkeletonsAnimationConfiguration {
|
||||
|
||||
public var duration: CFTimeInterval
|
||||
public var timingFunction: CAMediaTimingFunction?
|
||||
|
||||
public init(duration: CFTimeInterval = 1, timingFunction: CAMediaTimingFunction? = nil) {
|
||||
self.duration = duration
|
||||
self.timingFunction = timingFunction
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
//
|
||||
// Copyright (c) 2023 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
import QuartzCore
|
||||
|
||||
open class DirectionalSkeletonsAnimationConfiguration: BaseSkeletonsAnimationConfiguration {
|
||||
|
||||
public var direction: SkeletonsAnimationDirection
|
||||
|
||||
public init(direction: SkeletonsAnimationDirection = .leftToRight,
|
||||
duration: CFTimeInterval = 1.5,
|
||||
timingFunction: CAMediaTimingFunction? = nil) {
|
||||
|
||||
self.direction = direction
|
||||
|
||||
super.init(duration: duration, timingFunction: timingFunction)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
//
|
||||
// Copyright (c) 2023 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
import QuartzCore
|
||||
|
||||
open class SkeletonsAnimationBuilder {
|
||||
|
||||
public static func createDirectionalGradientAnimation(_ conf: DirectionalSkeletonsAnimationConfiguration) -> CAAnimationGroup {
|
||||
|
||||
let startPointAnimation = CABasicAnimation(keyPath: #keyPath(CAGradientLayer.startPoint))
|
||||
startPointAnimation.fromValue = conf.direction.startPoint.from
|
||||
startPointAnimation.toValue = conf.direction.startPoint.to
|
||||
|
||||
let endPointAnimation = CABasicAnimation(keyPath: #keyPath(CAGradientLayer.endPoint))
|
||||
endPointAnimation.fromValue = conf.direction.endPoint.from
|
||||
endPointAnimation.toValue = conf.direction.endPoint.to
|
||||
|
||||
let animationGroup = CAAnimationGroup()
|
||||
animationGroup.timingFunction = conf.timingFunction
|
||||
animationGroup.duration = conf.duration
|
||||
animationGroup.animations = [startPointAnimation, endPointAnimation]
|
||||
animationGroup.repeatCount = .infinity
|
||||
|
||||
return animationGroup
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
//
|
||||
// Copyright (c) 2023 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
import struct CoreGraphics.CGPoint
|
||||
|
||||
typealias GradientAnimationAnchorPoints = (from: CGPoint, to: CGPoint)
|
||||
|
||||
public enum SkeletonsAnimationDirection {
|
||||
case leftToRight
|
||||
case rightToLeft
|
||||
case topToBottom
|
||||
case bottomToTop
|
||||
case topLeftToBottomRight
|
||||
case topRightToBottomLeft
|
||||
case bottomLeftToTopRight
|
||||
case bottomRightToTopLeft
|
||||
|
||||
var startPoint: GradientAnimationAnchorPoints {
|
||||
switch self {
|
||||
case .leftToRight:
|
||||
return (from: CGPoint(x: -1, y: 0.5), to: CGPoint(x: 1, y: 0.5))
|
||||
|
||||
case .rightToLeft:
|
||||
return (from: Self.leftToRight.startPoint.to, to: Self.leftToRight.startPoint.from)
|
||||
|
||||
case .topToBottom:
|
||||
return (from: CGPoint(x: 0.5, y: -1), to: CGPoint(x: 0.5, y: 1))
|
||||
|
||||
case .bottomToTop:
|
||||
return (from: Self.topToBottom.startPoint.to, to: Self.topToBottom.startPoint.from)
|
||||
|
||||
case .topLeftToBottomRight:
|
||||
return (from: CGPoint(x: -1, y: -1), to: CGPoint(x: 1, y: 1))
|
||||
|
||||
case .topRightToBottomLeft:
|
||||
return (from: Self.bottomLeftToTopRight.startPoint.to, to: Self.bottomLeftToTopRight.startPoint.from)
|
||||
|
||||
case .bottomLeftToTopRight:
|
||||
return (from: CGPoint(x: -1, y: 2), to: CGPoint(x: 1, y: 0))
|
||||
|
||||
case .bottomRightToTopLeft:
|
||||
return (from: Self.topLeftToBottomRight.startPoint.to, to: Self.topLeftToBottomRight.startPoint.from)
|
||||
}
|
||||
}
|
||||
|
||||
var endPoint: GradientAnimationAnchorPoints {
|
||||
switch self {
|
||||
case .leftToRight:
|
||||
return (from: CGPoint(x: 0, y: 0.5), to: CGPoint(x: 2, y: 0.5))
|
||||
|
||||
case .rightToLeft:
|
||||
return (from: Self.leftToRight.endPoint.to, to: Self.leftToRight.endPoint.from)
|
||||
|
||||
case .topToBottom:
|
||||
return (from: CGPoint(x: 0.5, y: 0), to: CGPoint(x: 0.5, y: 2))
|
||||
|
||||
case .bottomToTop:
|
||||
return (from: Self.topToBottom.endPoint.to, to: Self.topToBottom.endPoint.from)
|
||||
|
||||
case .topLeftToBottomRight:
|
||||
return (from: CGPoint(x: 0, y: 0), to: CGPoint(x: 2, y: 2))
|
||||
|
||||
case .topRightToBottomLeft:
|
||||
return (from: Self.bottomLeftToTopRight.endPoint.to, to: Self.bottomLeftToTopRight.endPoint.from)
|
||||
|
||||
case .bottomLeftToTopRight:
|
||||
return (from: CGPoint(x: 0, y: 1), to: CGPoint(x: 2, y: -1))
|
||||
|
||||
case .bottomRightToTopLeft:
|
||||
return (from: Self.topLeftToBottomRight.endPoint.to, to: Self.topLeftToBottomRight.endPoint.from)
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue