Compare commits
65 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0e952505a9 | |||
| 43fca160be | |||
| 3453526d51 | |||
| 5ae4a97450 | |||
| e9a9f8add1 | |||
| 615b955965 | |||
| 943963d8c7 | |||
| f0c7d0abb9 | |||
| 46bb7d49f7 | |||
| 50d52c70a4 | |||
| 85d52cd837 | |||
| 18c90f0c63 | |||
| 15bfb95e8a | |||
| e87a0a40b6 | |||
| 341ed54032 | |||
| bdf75322b8 | |||
| 0534cba5df | |||
| 831b2f144f | |||
| 8bff1e5c24 | |||
| 4f1ca8157a | |||
| 8d6589004b | |||
| 3993d4d003 | |||
| f2e8d4411f | |||
| 3c2848ffd1 | |||
| f162d50891 | |||
| 9f34fcb70c | |||
| fed617321f | |||
| 139082d366 | |||
| ca43421a8f | |||
| 08975badd3 | |||
| 72759913ae | |||
| 410f53dc1c | |||
| 7916101d7b | |||
| 17313e2bd3 | |||
| fc09d21272 | |||
| 1b85dfa196 | |||
| 830bceb3cb | |||
| 8b467a2b59 | |||
| 7656a3f287 | |||
| aa2f484b6b | |||
| fd21f9adb7 | |||
| ffb3e42939 | |||
| a59bacaf89 | |||
| ab373c569a | |||
| 29c7b63352 | |||
| 45a2479733 | |||
| 36ec1c3d12 | |||
| 59832f0e4b | |||
| f77b041bce | |||
| 07ddae83a0 | |||
| 7ae64d9350 | |||
| 51b9c84daa | |||
| 211dcf48a6 | |||
| e3ae3d440e | |||
| 87b208dc1b | |||
| 9449d4d9d6 | |||
| 5249c14314 | |||
| f95ba4f1e4 | |||
| ad9dfd7f5a | |||
| 6d7e1a010b | |||
| 262727193c | |||
| a17e142cfa | |||
| 99591d784f | |||
| c4ff71566c | |||
| 1418eeda0f |
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
Pod::Spec.new do |s|
|
||||
s.name = 'ContainerControllerSwift'
|
||||
s.version = '1.0.9'
|
||||
s.version = '1.1.7'
|
||||
s.summary = 'This is a swipe-panel from application: https://www.apple.com/ios/maps/'
|
||||
|
||||
# This description is used to generate tags and improve search results.
|
||||
@@ -33,7 +33,7 @@ TODO: Add long description of the pod here.
|
||||
# s.ios.deployment_target = '13.0'
|
||||
s.platform = :ios, "13.0"
|
||||
|
||||
s.source_files = 'ContainerControllerSwift/**/*.{swift}'
|
||||
s.source_files = 'Sources/**/*.{swift}'
|
||||
|
||||
s.framework = "UIKit"
|
||||
# s.ios.framework = 'UIKit'
|
||||
|
||||
Binary file not shown.
@@ -1,45 +0,0 @@
|
||||
#
|
||||
# Be sure to run `pod lib lint ContainerControllerSwift.podspec' to ensure this is a
|
||||
# valid spec before submitting.
|
||||
#
|
||||
# Any lines starting with a # are optional, but their use is encouraged
|
||||
# To learn more about a Podspec see https://guides.cocoapods.org/syntax/podspec.html
|
||||
#
|
||||
|
||||
Pod::Spec.new do |s|
|
||||
s.name = 'ContainerControllerSwift'
|
||||
s.version = '1.0.2'
|
||||
s.summary = 'This is a swipe-panel from application: https://www.apple.com/ios/maps/'
|
||||
|
||||
# This description is used to generate tags and improve search results.
|
||||
# * Think: What does it do? Why did you write it? What is the focus?
|
||||
# * Try to keep it short, snappy and to the point.
|
||||
# * Write the description between the DESC delimiters below.
|
||||
# * Finally, don't worry about the indent, CocoaPods strips it!
|
||||
|
||||
s.description = <<-DESC
|
||||
TODO: Add long description of the pod here.
|
||||
'This is a swipe-panel from application: https://www.apple.com/ios/maps/'
|
||||
DESC
|
||||
|
||||
s.homepage = 'https://github.com/mrustaa/ContainerController'
|
||||
# s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2'
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
s.author = { 'rustamburger@gmail.com' => 'rustamburger@gmail.com' }
|
||||
s.source = { :git => 'https://github.com/mrustaa/ContainerController.git', :tag => s.version.to_s }
|
||||
# s.social_media_url = 'https://twitter.com/<TWITTER_USERNAME>'
|
||||
|
||||
s.swift_version = '5.2.4'
|
||||
s.ios.deployment_target = '13.0'
|
||||
|
||||
s.source_files = 'ContainerControllerSwift/*.{swift}'
|
||||
s.source_files = 'ContainerControllerSwift/**/*.{swift}'
|
||||
|
||||
# s.resource_bundles = {
|
||||
# 'ContainerControllerSwift' => ['ContainerControllerSwift/Assets/*.png']
|
||||
# }
|
||||
|
||||
# s.public_header_files = 'Pod/Classes/**/*.h'
|
||||
# s.frameworks = 'UIKit', 'MapKit'
|
||||
# s.dependency 'AFNetworking', '~> 2.3'
|
||||
end
|
||||
@@ -1,36 +0,0 @@
|
||||
//
|
||||
// ContainerView.swift
|
||||
// PatternsSwift
|
||||
//
|
||||
// Created by mrustaa on 21/04/2020.
|
||||
// Copyright © 2020 mrustaa. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
public protocol ContainerControllerDelegate {
|
||||
|
||||
/// Reports rotation and orientation changes
|
||||
func containerControllerRotation(_ containerController: ContainerController)
|
||||
|
||||
/// Reports a click on the background shadow
|
||||
func containerControllerShadowClick(_ containerController: ContainerController)
|
||||
|
||||
/// Reports the changes current position of the container, after its use
|
||||
func containerControllerMove(_ containerController: ContainerController, position: CGFloat, type: ContainerMoveType, animation: Bool)
|
||||
|
||||
}
|
||||
|
||||
public extension ContainerControllerDelegate {
|
||||
|
||||
func containerControllerRotation(_ containerController: ContainerController) {
|
||||
}
|
||||
|
||||
|
||||
func containerControllerShadowClick(_ containerController: ContainerController) {
|
||||
}
|
||||
|
||||
func containerControllerMove(_ containerController: ContainerController, position: CGFloat, type: ContainerMoveType, animation: Bool) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
//
|
||||
// ContainerTypes.swift
|
||||
// PatternsSwift
|
||||
//
|
||||
// Created by mrustaa on 21/04/2020.
|
||||
// Copyright © 2020 mrustaa. All rights reserved.
|
||||
//
|
||||
|
||||
typealias ContainerCompletion = () -> Void
|
||||
|
||||
public enum ContainerMoveType {
|
||||
case top
|
||||
case middle
|
||||
case bottom
|
||||
case hide
|
||||
case custom
|
||||
}
|
||||
|
||||
public enum ContainerFromType {
|
||||
case pan
|
||||
case scroll
|
||||
case scrollBorder
|
||||
case rotation
|
||||
case tracking
|
||||
case custom
|
||||
}
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
//
|
||||
// irbis-Bridging-Header.h
|
||||
// irbis
|
||||
//
|
||||
// Created by Aleksandr Lobyncev on 07.04.2021.
|
||||
// Copyright © 2021 ИТСофт. All rights reserved.
|
||||
//
|
||||
|
||||
//#import "AppDelegate.h"
|
||||
|
||||
#import "NSObject+Extension.h"
|
||||
|
||||
//#import "Coffee.h"
|
||||
|
||||
//#import <Foundation/Foundation.h>
|
||||
//#import <Firebase/Firebase.h>
|
||||
//#import <GoogleMaps/GoogleMaps.h>
|
||||
//#import "Constants.h"
|
||||
//#import "ChangeAppIcon.h"
|
||||
//#import "CoffeeFilterType.h"
|
||||
//#import "CoffeeInstagram.h"
|
||||
//#import "Alert.h"
|
||||
////#import "AppDelegate.h"
|
||||
//
|
||||
////MARK: - CoffeeOld
|
||||
//
|
||||
//#import "UIView+Frame.h"
|
||||
//#import "Utils.h"
|
||||
//#import "NSString+JSONString.h"
|
||||
//#import "NSDate+Utils.h"
|
||||
//
|
||||
//#import "KGLocalizationManager.h"
|
||||
//#import "KGNotificationsManager.h"
|
||||
//
|
||||
//#import "CustomSegmentView.h"
|
||||
//
|
||||
//#import <MessageUI/MessageUI.h>
|
||||
//#import <MessageUI/MFMailComposeViewController.h>
|
||||
//
|
||||
//#import "Reachability.h"
|
||||
//#import "ReachabilityAddons.h"
|
||||
//
|
||||
////MARK: - CoffeeNew
|
||||
//
|
||||
//#import "AddonsNew.h"
|
||||
//
|
||||
////MARK: - Xib / Designable Views
|
||||
//
|
||||
//#import "DesignView.h"
|
||||
//#import "DesignButton.h"
|
||||
//#import "DesignSegmentBar.h"
|
||||
//
|
||||
////MARK: - Table Block
|
||||
//
|
||||
//#import "TableItem.h"
|
||||
//#import "BlockTableView.h"
|
||||
//
|
||||
////MARK: - Tag Styled
|
||||
//
|
||||
//#import "CollectionItem.h"
|
||||
//#import "TagsStyledView.h"
|
||||
//
|
||||
////MARK: - Xib / Storyboard loadNib
|
||||
//
|
||||
//#import "XibView.h"
|
||||
//#import "XibCell.h"
|
||||
//#import "XibCollectionCell.h"
|
||||
//#import "StoryboardController.h"
|
||||
//
|
||||
////MARK: - Container View
|
||||
//
|
||||
//#import "ContainerTypes.h"
|
||||
//#import "ContainerView.h" // View
|
||||
//#import "ContainerData.h" // Data
|
||||
//#import "ContainerViewController.h" // Screens
|
||||
//#import "ContainerMapViewController.h"
|
||||
//
|
||||
////MARK: - Coffee Data
|
||||
//
|
||||
//#import "CoffeeUtils.h" // utils
|
||||
//
|
||||
///// models
|
||||
//#import "CoffeeNew.h"
|
||||
//@class Coffee;
|
||||
//#import "Coffee.h"
|
||||
//#import "CoffeeInfoIP.h"
|
||||
//#import "CoffeeAddress.h"
|
||||
//#import "CoffeeCountry.h"
|
||||
//#import "CoffeeEvent.h"
|
||||
//#import "CoffeeMarketStore.h"
|
||||
//#import "CoffeeMarket.h"
|
||||
//#import "CoffeeAdmin.h"
|
||||
//
|
||||
///// container data
|
||||
////#import "CoffeeOldListContainerData.h"
|
||||
////#import "CoffeeOldDetailsContainerData.h"
|
||||
////#import "CoffeeOld2ListContainerData.h"
|
||||
////#import "CoffeeOld2DetailsContainerData.h"
|
||||
//#import "CoffeeListContainerData.h"
|
||||
//#import "CoffeeDetailsContainerData.h"
|
||||
//
|
||||
///// managers
|
||||
//#import "ConfigManager.h"
|
||||
//#import "DownloadManager.h"
|
||||
//#import "DownloadInfoSite.h"
|
||||
//#import "CoffeeMapGetJSON.h"
|
||||
//#import "CoffeeDataManager.h"
|
||||
//#import "CoffeeMapManager.h"
|
||||
//
|
||||
//#define DATA_MGR [CoffeeDataManager managers]
|
||||
//#define MAP_MGR [CoffeeMapManager managers]
|
||||
//
|
||||
////MARK: - UI
|
||||
//
|
||||
///// cells
|
||||
//#import "MenuCells.h"
|
||||
//#import "TextTitleCell.h" // text
|
||||
//#import "CoffeeSegmentCell.h"
|
||||
//#import "CoffeeMenuAboutCell.h"
|
||||
//#import "CoffeeMenuTextCell.h"
|
||||
//#import "CoffeeCell.h" // coffee
|
||||
////#import "CoffeeDetailsCell.h"
|
||||
//#import "CoffeeDetailsTitleCell.h"
|
||||
//#import "CoffeeDetailsRouteCell.h"
|
||||
//#import "CoffeeDetailsContentCell.h"
|
||||
//#import "CoffeeDetailsInstaCell.h"
|
||||
//#import "CoffeeDetailsAddressCell.h"
|
||||
//#import "CoffeeDetailsInfoNewCell.h"
|
||||
//#import "CoffeeDetailsInfoCell.h"
|
||||
//#import "CoffeeListEventCell.h" // events
|
||||
//#import "CoffeeDetailsEventTitleCell.h"
|
||||
//#import "CoffeeDetailsEventPhotoCell.h"
|
||||
//#import "CoffeeDetailsEventInfoCell.h"
|
||||
//#import "CoffeeMarketTitleCell.h" // market
|
||||
//#import "CoffeeMarketNameCell.h"
|
||||
//#import "CoffeeMarketSectionCell.h"
|
||||
//#import "CoffeeMarketSubtitleCell.h"
|
||||
//#import "CoffeeMarketInfoCell.h"
|
||||
//#import "CoffeeMenuSocItemsCell.h"
|
||||
//#import "CoffeeMenuSectionCell.h"
|
||||
//#import "CoffeeListEmptyCell.h" // empty
|
||||
//#import "GTRideMapCell.h"
|
||||
//#import "CoffeeMapPlaceEventCell.h"
|
||||
//
|
||||
///// cells collection
|
||||
//#import "CoffeeMenuSocCell.h"
|
||||
//#import "CoffeeTagCell.h"
|
||||
//#import "CoffeeMarketCell.h"
|
||||
//#import "CoffeeMarketEmptyTagCell.h"
|
||||
//#import "CoffeeMarketSectionTagCell.h"
|
||||
//#import "CoffeeMarketOneStoreTagCell.h"
|
||||
//#import "CoffeeMarketStoresTagCell.h"
|
||||
//
|
||||
///// view
|
||||
//#import "CoffeeHeaderView.h" // coffee
|
||||
//#import "CoffeeHeaderGripView.h"
|
||||
//#import "CoffeeHeaderGripTitleView.h"
|
||||
//#import "CoffeeHeaderSearchBarView.h"
|
||||
//#import "CoffeeDetailsHeaderView.h"
|
||||
//#import "CoffeeDetailsTitleHeaderView.h"
|
||||
//#import "CoffeeNavBarView.h"
|
||||
////#import "CoffeeTabBarOld2View.h"
|
||||
//#import "CoffeeTabBarView.h"
|
||||
//#import "CoffeeListEmptyView.h"
|
||||
//
|
||||
///// screen
|
||||
//#import "WorkspaceController.h"
|
||||
//#import "CoffeeViewController.h" // coffee
|
||||
//#import "FLAnimatedImage.h"
|
||||
//#import "InstagramManager.h"
|
||||
////#import "CoffeeOld2ViewController.h"
|
||||
////#import "CoffeeOldViewController.h"
|
||||
//#import "CoffeeMenuViewController.h"
|
||||
//
|
||||
//#import "CoffeeEventsViewController.h"
|
||||
//#import "CoffeeEventsDetailsViewController.h"
|
||||
//#import "CoffeeMarketsViewController.h"
|
||||
//#import "CoffeeMarketsDetailsViewController.h"
|
||||
//#import "DbCoffeeImagesViewController.h"
|
||||
//#import "CoffeeNewsFeedViewController.h"
|
||||
//#import "ContainerMapRedViewController.h"
|
||||
//#import "CoffeeAdminViewController.h"
|
||||
//#import "CoffeePushViewController.h"
|
||||
//#import "SendPush.h"
|
||||
//
|
||||
//
|
||||
//#import "SafariViewController.h"
|
||||
//#import "WebViewController.h"
|
||||
File diff suppressed because it is too large
Load Diff
+12
-5
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1150"
|
||||
LastUpgradeVersion = "1540"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@@ -15,7 +15,7 @@
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "607FACCF1AFB9204008FA782"
|
||||
BuildableName = "ContainerControllerSwift_Example.app"
|
||||
BuildableName = "ContainerController.app"
|
||||
BlueprintName = "ContainerControllerSwift_Example"
|
||||
ReferencedContainer = "container:ContainerControllerSwift.xcodeproj">
|
||||
</BuildableReference>
|
||||
@@ -45,7 +45,7 @@
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "607FACCF1AFB9204008FA782"
|
||||
BuildableName = "ContainerControllerSwift_Example.app"
|
||||
BuildableName = "ContainerController.app"
|
||||
BlueprintName = "ContainerControllerSwift_Example"
|
||||
ReferencedContainer = "container:ContainerControllerSwift.xcodeproj">
|
||||
</BuildableReference>
|
||||
@@ -78,11 +78,18 @@
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "607FACCF1AFB9204008FA782"
|
||||
BuildableName = "ContainerControllerSwift_Example.app"
|
||||
BuildableName = "ContainerController.app"
|
||||
BlueprintName = "ContainerControllerSwift_Example"
|
||||
ReferencedContainer = "container:ContainerControllerSwift.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<EnvironmentVariables>
|
||||
<EnvironmentVariable
|
||||
key = "IDEPreferLogStreaming"
|
||||
value = "YES"
|
||||
isEnabled = "YES">
|
||||
</EnvironmentVariable>
|
||||
</EnvironmentVariables>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
@@ -95,7 +102,7 @@
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "607FACCF1AFB9204008FA782"
|
||||
BuildableName = "ContainerControllerSwift_Example.app"
|
||||
BuildableName = "ContainerController.app"
|
||||
BlueprintName = "ContainerControllerSwift_Example"
|
||||
ReferencedContainer = "container:ContainerControllerSwift.xcodeproj">
|
||||
</BuildableReference>
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "group:ContainerControllerSwift.xcodeproj">
|
||||
location = "group:..">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:/Users/rustam/.cocoapods/repos/ContainerControllerSwift/Example/ContainerControllerSwift.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:Pods/Pods.xcodeproj">
|
||||
|
||||
@@ -1,46 +1,42 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="16097" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_0" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
|
||||
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" id="iN0-l3-epB">
|
||||
<rect key="frame" x="0.0" y="0.0" width="480" height="480"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text=" Copyright (c) 2015 CocoaPods. All rights reserved." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="8ie-xW-0ye">
|
||||
<rect key="frame" x="20" y="439" width="441" height="21"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="ContainerControllerSwift" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="kId-c2-rCX">
|
||||
<rect key="frame" x="20" y="140" width="441" height="43"/>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="ContainerController Swift" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="kId-c2-rCX">
|
||||
<rect key="frame" x="20" y="169" width="280" height="43"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="36"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="screenLandscape5.png" translatesAutoresizingMaskIntoConstraints="NO" id="c2h-J4-UI4">
|
||||
<rect key="frame" x="15" y="60" width="290" height="508"/>
|
||||
</imageView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="kId-c2-rCX" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="bottom" multiplier="1/3" constant="1" id="5cJ-9S-tgC"/>
|
||||
<constraint firstItem="c2h-J4-UI4" firstAttribute="height" secondItem="iN0-l3-epB" secondAttribute="height" multiplier="0.894089" id="6Pl-Y0-TbW"/>
|
||||
<constraint firstItem="c2h-J4-UI4" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="15" id="8cY-so-aq9"/>
|
||||
<constraint firstAttribute="centerX" secondItem="kId-c2-rCX" secondAttribute="centerX" id="Koa-jz-hwk"/>
|
||||
<constraint firstAttribute="bottom" secondItem="8ie-xW-0ye" secondAttribute="bottom" constant="20" id="Kzo-t9-V3l"/>
|
||||
<constraint firstItem="8ie-xW-0ye" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="MfP-vx-nX0"/>
|
||||
<constraint firstAttribute="centerX" secondItem="8ie-xW-0ye" secondAttribute="centerX" id="ZEH-qu-HZ9"/>
|
||||
<constraint firstAttribute="trailing" secondItem="c2h-J4-UI4" secondAttribute="trailing" constant="15" id="Tlx-R0-BaC"/>
|
||||
<constraint firstItem="kId-c2-rCX" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="fvb-Df-36g"/>
|
||||
<constraint firstAttribute="bottom" secondItem="c2h-J4-UI4" secondAttribute="bottom" id="nuV-b0-dKE"/>
|
||||
</constraints>
|
||||
<nil key="simulatedStatusBarMetrics"/>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<point key="canvasLocation" x="548" y="455"/>
|
||||
<point key="canvasLocation" x="544.79999999999995" y="453.69458128078821"/>
|
||||
</view>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="screenLandscape5.png" width="685.5" height="302.5"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
||||
@@ -1,33 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16097" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="aV9-EX-QWJ">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="cIu-62-xtU">
|
||||
<device id="retina5_9" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22685"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="tne-QT-ifu">
|
||||
<objects>
|
||||
<viewController id="BYZ-38-t0r" customClass="ViewController" customModule="ContainerControllerSwift_Example" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="623"/>
|
||||
<viewController id="BYZ-38-t0r" customClass="ViewController" customModule="ContainerController" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="8bC-Xf-vdC">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="718"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="UYm-tY-WGa" customClass="TableAdapterView" customModule="ContainerControllerSwift">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="623"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||
<tableView clipsSubviews="YES" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" contentViewInsetsToSafeArea="NO" translatesAutoresizingMaskIntoConstraints="NO" id="UYm-tY-WGa" customClass="TableAdapterView" customModule="ContainerControllerSwift">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="718"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||
</tableView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||
<viewLayoutGuide key="safeArea" id="W4d-9e-HAg"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||
<constraints>
|
||||
<constraint firstItem="6Tk-OE-BBY" firstAttribute="bottom" secondItem="UYm-tY-WGa" secondAttribute="bottom" id="Usw-ep-vba"/>
|
||||
<constraint firstItem="UYm-tY-WGa" firstAttribute="top" secondItem="6Tk-OE-BBY" secondAttribute="top" id="qmS-hQ-rSa"/>
|
||||
<constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="UYm-tY-WGa" secondAttribute="trailing" id="vQK-T0-eQf"/>
|
||||
<constraint firstItem="UYm-tY-WGa" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" id="wXp-OO-MUn"/>
|
||||
<constraint firstAttribute="bottom" secondItem="UYm-tY-WGa" secondAttribute="bottom" id="kha-w5-3Yg"/>
|
||||
<constraint firstItem="UYm-tY-WGa" firstAttribute="top" secondItem="8bC-Xf-vdC" secondAttribute="top" id="qmS-hQ-rSa"/>
|
||||
<constraint firstAttribute="trailing" secondItem="UYm-tY-WGa" secondAttribute="trailing" id="vQK-T0-eQf"/>
|
||||
<constraint firstItem="UYm-tY-WGa" firstAttribute="leading" secondItem="8bC-Xf-vdC" secondAttribute="leading" id="wXp-OO-MUn"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
|
||||
</view>
|
||||
<navigationItem key="navigationItem" id="x48-lm-dG7"/>
|
||||
<connections>
|
||||
@@ -38,21 +40,32 @@
|
||||
</objects>
|
||||
<point key="canvasLocation" x="132" y="137.18140929535232"/>
|
||||
</scene>
|
||||
<!--Light Content Navigation Controller-->
|
||||
<scene sceneID="2WA-6t-2aK">
|
||||
<!--Navigation Controller-->
|
||||
<scene sceneID="jWl-Kd-Y9J">
|
||||
<objects>
|
||||
<navigationController id="aV9-EX-QWJ" customClass="LightContentNavigationController" customModule="ContainerControllerSwift_Example" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" translucent="NO" id="QWK-gZ-mYK">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
|
||||
<navigationController id="cIu-62-xtU" sceneMemberID="viewController">
|
||||
<navigationBar key="navigationBar" contentMode="scaleToFill" translucent="NO" id="y83-TR-htY">
|
||||
<rect key="frame" x="0.0" y="50" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<color key="barTintColor" systemColor="secondaryLabelColor"/>
|
||||
<navigationBarAppearance key="compactAppearance"/>
|
||||
</navigationBar>
|
||||
<connections>
|
||||
<segue destination="BYZ-38-t0r" kind="relationship" relationship="rootViewController" id="SPa-ED-zkK"/>
|
||||
<segue destination="BYZ-38-t0r" kind="relationship" relationship="rootViewController" id="Lpq-ZC-BiY"/>
|
||||
</connections>
|
||||
</navigationController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="Uhq-S7-WHN" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="bJL-5a-VyA" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-631" y="137"/>
|
||||
<point key="canvasLocation" x="-855" y="137"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<systemColor name="secondaryLabelColor">
|
||||
<color red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</systemColor>
|
||||
<systemColor name="systemBackgroundColor">
|
||||
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</systemColor>
|
||||
</resources>
|
||||
</document>
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
extension UIColor {
|
||||
var redValue: CGFloat{ return CIColor(color: self).red }
|
||||
var greenValue: CGFloat{ return CIColor(color: self).green }
|
||||
var blueValue: CGFloat{ return CIColor(color: self).blue }
|
||||
var alphaValue: CGFloat{ return CIColor(color: self).alpha }
|
||||
|
||||
|
||||
|
||||
static let playFullPanelOld = UIColor(named: "PlayFullPanelOld")!
|
||||
static let playFullBackground = UIColor(named: "PlayFullBackground")!
|
||||
static let playFullBackgroundOld = UIColor(named: "PlayFullBackgroundOld")!
|
||||
static let playlistColor = UIColor(named: "PlaylistColor")!
|
||||
static let playMusicColor = UIColor(named: "PlayMusicColor")! // red
|
||||
static let sportColor = UIColor(named: "SportColor")!
|
||||
}
|
||||
|
||||
class Colors {
|
||||
|
||||
class func hex(_ hex: Int) -> UIColor {
|
||||
let mask = 0xFF
|
||||
let r = CGFloat((hex >> 16) & mask) / 255
|
||||
let g = CGFloat((hex >> 8) & mask) / 255
|
||||
let b = CGFloat((hex) & mask) / 255
|
||||
return UIColor(red: r, green: g, blue: b, alpha: 1)
|
||||
}
|
||||
|
||||
|
||||
class func hexStr(_ hex: String) -> UIColor {
|
||||
let input = hex.replacingOccurrences(of: "#", with: "").uppercased()
|
||||
var a: CGFloat = 1.0, r: CGFloat = 0.0, b: CGFloat = 0.0, g: CGFloat = 0.0
|
||||
func colorComponent(from string: String, start: Int, length: Int) -> CGFloat {
|
||||
let substring = (string as NSString).substring(with: NSRange(location: start, length: length))
|
||||
let fullHex = length == 2 ? substring : "\(substring)\(substring)"
|
||||
var hexComponent: UInt64 = 0
|
||||
Scanner(string: fullHex).scanHexInt64(&hexComponent)
|
||||
return CGFloat(Double(hexComponent) / 255.0)
|
||||
}
|
||||
switch (input.count) {
|
||||
case 3 /* #RGB */:
|
||||
r = colorComponent(from: input, start: 0, length: 1)
|
||||
g = colorComponent(from: input, start: 1, length: 1)
|
||||
b = colorComponent(from: input, start: 2, length: 1)
|
||||
case 4 /* #ARGB */:
|
||||
a = colorComponent(from: input, start: 0, length: 1)
|
||||
r = colorComponent(from: input, start: 1, length: 1)
|
||||
g = colorComponent(from: input, start: 2, length: 1)
|
||||
b = colorComponent(from: input, start: 3, length: 1)
|
||||
case 6 /* #RRGGBB */:
|
||||
r = colorComponent(from: input, start: 0, length: 2)
|
||||
g = colorComponent(from: input, start: 2, length: 2)
|
||||
b = colorComponent(from: input, start: 4, length: 2)
|
||||
case 8 /* #AARRGGBB */:
|
||||
a = colorComponent(from: input, start: 0, length: 2)
|
||||
r = colorComponent(from: input, start: 2, length: 2)
|
||||
g = colorComponent(from: input, start: 4, length: 2)
|
||||
b = colorComponent(from: input, start: 6, length: 2)
|
||||
default:
|
||||
break
|
||||
}
|
||||
return UIColor(red: r, green: g, blue: b, alpha: a)
|
||||
}
|
||||
|
||||
class func rgba( _ red: CGFloat, _ green: CGFloat, _ blue: CGFloat, _ alpha: CGFloat) -> UIColor {
|
||||
return UIColor(red: red / 255, green: green / 255, blue: blue / 255, alpha: alpha)
|
||||
}
|
||||
|
||||
class func rgb( _ red: CGFloat, _ green: CGFloat, _ blue: CGFloat) -> UIColor {
|
||||
return rgba(red, green, blue, 1)
|
||||
}
|
||||
|
||||
class func grayLevel(_ gray: CGFloat) -> UIColor {
|
||||
return rgb(gray * 255, gray * 255, gray * 255)
|
||||
}
|
||||
|
||||
class func blackAlpha(_ alpha: CGFloat) -> UIColor {
|
||||
return rgba(0, 0, 0, alpha)
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
static public let lightGray = blackAlpha(0.1)
|
||||
static public let slightlyDark = blackAlpha(0.3)
|
||||
static public let halfBlack = blackAlpha(0.5)
|
||||
|
||||
static public let black = grayLevel(0) // 0 %
|
||||
static public let gray = grayLevel(127) // 49 %
|
||||
static public let lightInactiveGray = grayLevel(178) // 69 %
|
||||
static public let silver = grayLevel(229) // 89 %
|
||||
static public let inactiveGray = grayLevel(246) // 96 %
|
||||
static public let white = grayLevel(255) // 100 %
|
||||
|
||||
static public let transparentGray = rgba(225, 225, 225, 0.3)
|
||||
static public let lightOrange = rgba(255, 105, 0, 0.1)
|
||||
|
||||
static public let red = rgb(255, 59, 48)
|
||||
static public let blue = rgb(44, 174, 233)
|
||||
static public let yellow = rgb(254, 219, 6)
|
||||
static public let orange = rgb(255, 105, 0)
|
||||
static public let purple = rgb(128, 0, 128)
|
||||
static public let gold = rgb(226, 201, 127)
|
||||
static public let beige = rgb(245, 245, 220)
|
||||
static public let brand = rgb(255, 105, 0)
|
||||
static public let approveGreen = rgb(49, 183, 0)
|
||||
static public let darkBlue = rgb(74, 144, 226)
|
||||
static public let semidarkBlue = rgb(0, 107, 202)
|
||||
static public let darkGray = rgb(142, 142, 147)
|
||||
static public let lightYellow = rgb(254, 229, 6)
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.129",
|
||||
"green" : "0.086",
|
||||
"red" : "0.078"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.129",
|
||||
"green" : "0.086",
|
||||
"red" : "0.078"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.137",
|
||||
"green" : "0.122",
|
||||
"red" : "0.118"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.137",
|
||||
"green" : "0.122",
|
||||
"red" : "0.118"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.157",
|
||||
"green" : "0.129",
|
||||
"red" : "0.125"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.157",
|
||||
"green" : "0.129",
|
||||
"red" : "0.125"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.365",
|
||||
"green" : "0.267",
|
||||
"red" : "0.922"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.365",
|
||||
"green" : "0.267",
|
||||
"red" : "0.922"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.937",
|
||||
"green" : "0.518",
|
||||
"red" : "0.310"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.937",
|
||||
"green" : "0.518",
|
||||
"red" : "0.310"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.000",
|
||||
"green" : "0.541",
|
||||
"red" : "1.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.000",
|
||||
"green" : "0.541",
|
||||
"red" : "1.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
-273
@@ -1,273 +0,0 @@
|
||||
//
|
||||
// ExamplesAddTableViewController.swift
|
||||
// ContainerControllerSwift
|
||||
//
|
||||
// Created by mrustaa on 09.06.2020.
|
||||
// Copyright © 2020 mrustaa. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import ContainerControllerSwift
|
||||
|
||||
class ExamplesAddTableViewController: StoryboardController {
|
||||
|
||||
var containerTable: ContainerController!
|
||||
var containerCollection: ContainerController!
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
|
||||
let layout = ContainerLayout()
|
||||
layout.backgroundShadowShow = true
|
||||
layout.positions = ContainerPosition(top: 70, middle: 250, bottom: 100)
|
||||
containerTable = ContainerController(addTo: self, layout: layout)
|
||||
containerTable.view.cornerRadius = 15
|
||||
containerTable.view.addShadow()
|
||||
containerTable.add(scrollView: addTableView())
|
||||
|
||||
|
||||
let layoutC = ContainerLayout()
|
||||
layoutC.positions = ContainerPosition(top: 100, middle: 250, bottom: 70)
|
||||
containerCollection = ContainerController(addTo: self, layout: layoutC)
|
||||
containerCollection.view.cornerRadius = 15
|
||||
containerCollection.view.addShadow()
|
||||
containerCollection.add(scrollView: addCollectionView())
|
||||
}
|
||||
|
||||
// override var prefersStatusBarHidden: Bool {
|
||||
// return true
|
||||
// }
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
containerTable.move(type: .top, completion: { [weak self] in
|
||||
guard let _self = self else { return }
|
||||
_self.containerCollection.move(type: .middle)
|
||||
})
|
||||
}
|
||||
|
||||
func addTableView() -> UITableView {
|
||||
|
||||
let tableView = UITableView()
|
||||
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
|
||||
tableView.delegate = self
|
||||
tableView.dataSource = self
|
||||
return tableView
|
||||
}
|
||||
|
||||
func addCollectionView() -> UICollectionView {
|
||||
|
||||
let layout = UICollectionViewFlowLayout()
|
||||
|
||||
let padding: CGFloat = 15
|
||||
layout.sectionInset = UIEdgeInsets(top: padding, left: padding, bottom: padding, right: padding)
|
||||
layout.minimumLineSpacing = padding
|
||||
layout.minimumInteritemSpacing = padding
|
||||
|
||||
let colletion = UICollectionView(frame: CGRect.zero, collectionViewLayout: layout)
|
||||
colletion.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "cell")
|
||||
colletion.backgroundColor = .clear
|
||||
colletion.delegate = self
|
||||
colletion.dataSource = self
|
||||
return colletion
|
||||
}
|
||||
|
||||
func changeViewParametrs() {
|
||||
|
||||
containerTable.view.cornerRadius = 15 // Change cornerRadius global
|
||||
containerTable.view.addShadow(opacity: 0.1) // Add layer shadow
|
||||
containerTable.view.addBlur(style: .dark) // Add background blur UIVisualEffectView
|
||||
}
|
||||
|
||||
func changeViewCustom() {
|
||||
|
||||
// Add custom shadow
|
||||
let layer = containerTable.view.layer
|
||||
layer.shadowOpacity = 0.5
|
||||
layer.shadowColor = UIColor.red.cgColor
|
||||
layer.shadowOffset = CGSize(width: 1, height: 4)
|
||||
layer.shadowRadius = 5
|
||||
|
||||
// Add view in container.view
|
||||
let viewRed = UIView(frame: CGRect(x: 50, y: 50, width: 50, height: 50))
|
||||
viewRed.backgroundColor = .systemRed
|
||||
containerTable.view.addSubview(viewRed)
|
||||
|
||||
// Add view under scrollView container.view
|
||||
let viewGreen = UIView(frame: CGRect(x: 25, y: 25, width: 50, height: 50))
|
||||
viewGreen.backgroundColor = .systemGreen
|
||||
containerTable.view.insertSubview(viewGreen, at: 0)
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Settings
|
||||
|
||||
|
||||
|
||||
func createLayout() {
|
||||
|
||||
let layout = ContainerLayout()
|
||||
layout.startPosition = .hide
|
||||
layout.backgroundShadowShow = false
|
||||
layout.positions = ContainerPosition(top: 70, middle: 250, bottom: 70)
|
||||
|
||||
}
|
||||
|
||||
// MARK: - change settings right away
|
||||
|
||||
func changeRightAway() {
|
||||
|
||||
// Properties
|
||||
containerTable.set(movingEnabled: true)
|
||||
containerTable.set(trackingPosition: false)
|
||||
containerTable.set(footerPadding: 100)
|
||||
|
||||
// Add ScrollInsets Top/Bottom
|
||||
containerTable.set(scrollIndicatorTop: 5) // ↓
|
||||
containerTable.set(scrollIndicatorBottom: 5) // ↑
|
||||
|
||||
// Positions
|
||||
containerTable.set(top: 70) // ↓
|
||||
containerTable.set(middle: 250) // ↑
|
||||
containerTable.set(bottom: 80) // ↑
|
||||
|
||||
// Middle Enable/Disable
|
||||
containerTable.set(middle: 250)
|
||||
containerTable.set(middle: nil)
|
||||
|
||||
// Background Shadow
|
||||
containerTable.set(backgroundShadowShow: true)
|
||||
|
||||
// Insets View
|
||||
containerTable.set(left: 5) // →
|
||||
containerTable.set(right: 5) // ←
|
||||
|
||||
// Landscape params
|
||||
containerTable.setLandscape(top: 30)
|
||||
containerTable.setLandscape(middle: 150)
|
||||
containerTable.setLandscape(bottom: 70)
|
||||
containerTable.setLandscape(middle: nil)
|
||||
|
||||
containerTable.setLandscape(backgroundShadowShow: false)
|
||||
|
||||
containerTable.setLandscape(left: 10)
|
||||
containerTable.setLandscape(right: 100)
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Scroll Delegate
|
||||
|
||||
extension ExamplesAddTableViewController: UIScrollViewDelegate {
|
||||
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
if let table = scrollView as? UITableView {
|
||||
containerTable.scrollViewDidScroll(table)
|
||||
}
|
||||
if let colletion = scrollView as? UICollectionView {
|
||||
containerCollection.scrollViewDidScroll(colletion)
|
||||
}
|
||||
}
|
||||
|
||||
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
|
||||
if let table = scrollView as? UITableView {
|
||||
containerTable.scrollViewWillBeginDragging(table)
|
||||
}
|
||||
if let colletion = scrollView as? UICollectionView {
|
||||
containerCollection.scrollViewWillBeginDragging(colletion)
|
||||
}
|
||||
}
|
||||
|
||||
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
|
||||
if let table = scrollView as? UITableView {
|
||||
containerTable.scrollViewDidEndDecelerating(table)
|
||||
}
|
||||
if let colletion = scrollView as? UICollectionView {
|
||||
containerCollection.scrollViewDidEndDecelerating(colletion)
|
||||
}
|
||||
}
|
||||
|
||||
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
|
||||
if let table = scrollView as? UITableView {
|
||||
containerTable.scrollViewDidEndDragging(table, willDecelerate: decelerate)
|
||||
}
|
||||
if let colletion = scrollView as? UICollectionView {
|
||||
containerCollection.scrollViewDidEndDragging(colletion, willDecelerate: decelerate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Collection DataSource
|
||||
|
||||
extension ExamplesAddTableViewController: UICollectionViewDataSource {
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||
return 17
|
||||
}
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
|
||||
|
||||
let randomInt = Int.random(in: 0..<6)
|
||||
|
||||
var color: UIColor = .systemBlue
|
||||
|
||||
switch randomInt {
|
||||
case 0: color = .systemBlue
|
||||
case 1: color = .systemRed
|
||||
case 2: color = .systemGray
|
||||
case 3: color = .systemGreen
|
||||
case 4: color = .systemYellow
|
||||
case 5: color = .systemOrange
|
||||
default: break
|
||||
}
|
||||
|
||||
cell.backgroundColor = color
|
||||
cell.layer.cornerRadius = 12
|
||||
return cell
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Collection Layout
|
||||
|
||||
extension ExamplesAddTableViewController: UICollectionViewDelegateFlowLayout {
|
||||
|
||||
public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
|
||||
let size = ((ContainerDevice.width / 2) - 1) - 22
|
||||
return CGSize(width: size, height: size)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// MARK: - Table Delegate
|
||||
|
||||
extension ExamplesAddTableViewController: UITableViewDelegate {
|
||||
|
||||
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
|
||||
return 60
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Table DataSource
|
||||
|
||||
extension ExamplesAddTableViewController: UITableViewDataSource {
|
||||
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
return 21
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
|
||||
cell.textLabel?.text = "item \(indexPath.row)"
|
||||
cell.backgroundColor = .clear
|
||||
return cell
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2020 Pavel Puzyrev
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,58 @@
|
||||
# Blurberry
|
||||
|
||||
Transparent blur using UIVisualEffectView without subclassing.
|
||||
|
||||

|
||||
|
||||
[[video demo]](https://vimeo.com/457206677)
|
||||
|
||||
[[appetize demo]](https://appetize.io/app/u2udhgwh0cgm12at0rmgjxtphr)
|
||||
|
||||
## Features
|
||||
- Based on UIVisualEffectView
|
||||
- Supports **iOS 14** and animation blocks
|
||||
- Super safe and super easy
|
||||
|
||||
## Usage
|
||||
|
||||
Just create a `UIVisualEffectView` in any way. Code, storyboards, XIBs, etc.
|
||||
|
||||
Customize it like example bellow via `blur` wrapper:
|
||||
```swift
|
||||
visualEffectView.blur.radius = 5.0
|
||||
visualEffectView.blur.tintColor = .clear
|
||||
```
|
||||
|
||||
Don't forget to set a `tintColor` value, otherwise it will be 30% white like `UIBlurEffect.Style.Light` by default.
|
||||
|
||||
## Installation
|
||||
|
||||
### Cocoapods
|
||||
|
||||
Add the Blurberry pod to your Podfile:
|
||||
```ruby
|
||||
platform :ios, '10.0'
|
||||
|
||||
use_frameworks!
|
||||
|
||||
target 'BlurryApp' do
|
||||
pod 'Blurberry'
|
||||
end
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [x] iOS 10, 11, 12 support
|
||||
- [ ] Other platform support (such as macOS)
|
||||
- [ ] Remove ObjC code or make it private
|
||||
- [ ] Add manager to check blur availability and other service info
|
||||
- [ ] Hide private API class names, method names, etc.
|
||||
|
||||
## Disclaimer
|
||||
|
||||
This framework uses private API, so just keep in mind it before submitting to the AppStore.
|
||||
|
||||
## Requirements
|
||||
|
||||
- iOS 10.0+
|
||||
- Swift 5
|
||||
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// Blurberry.h
|
||||
// Blurberry
|
||||
//
|
||||
// Created by Pavel Puzyrev on 09.09.2020.
|
||||
//
|
||||
// Copyright (c) 2020 Pavel Puzyrev <cannedapp@yahoo.com>
|
||||
//
|
||||
// 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/Foundation.h>
|
||||
|
||||
//! Project version number for Blurberry.
|
||||
FOUNDATION_EXPORT double BlurberryVersionNumber;
|
||||
|
||||
//! Project version string for Blurberry.
|
||||
FOUNDATION_EXPORT const unsigned char BlurberryVersionString[];
|
||||
|
||||
// In this header, you should import all the public headers of your framework using statements like #import <Blurberry/PublicHeader.h>
|
||||
|
||||
#import <Blurberry/NSObject+Extension.h>
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
//
|
||||
// NSObject+Extension.h
|
||||
// Blurberry
|
||||
//
|
||||
// Created by Pavel Puzyrev on 07.09.2020.
|
||||
//
|
||||
// Copyright (c) 2020 Pavel Puzyrev <cannedapp@yahoo.com>
|
||||
//
|
||||
// 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/Foundation.h>
|
||||
#import "ContainerControllerSwift-Swift.h"
|
||||
|
||||
struct BlurWrapper;
|
||||
|
||||
@class UIColor;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface NSObject (Extension)
|
||||
|
||||
#pragma mark *** Values getting ***
|
||||
|
||||
- (nullable NSArray<NSObject *> *)arrayValueByGetterNamed:(NSString *)getterName NS_SWIFT_NAME(arrayValue(getter:));
|
||||
- (nullable NSDictionary<NSString *, id> *)dictionaryValueByGetterNamed:(NSString *)getterName NS_SWIFT_NAME(dictionaryValue(getter:));
|
||||
- (nullable NSString *)stringValueByGetterNamed:(NSString *)getterName NS_SWIFT_NAME(stringValue(getter:));
|
||||
- (nullable UIColor *)colorValueByGetterNamed:(NSString *)getterName NS_SWIFT_NAME(colorValue(getter:));
|
||||
|
||||
#pragma mark *** Values setting ***
|
||||
|
||||
- (void)setIVarValue:(nullable id)value getterNamed:(NSString *)getterName NS_SWIFT_NAME(setIVarValue(value:getter:));
|
||||
- (void)setValueUsingSetter:(nullable id)value getterNamed:(NSString *)getterName NS_SWIFT_NAME(setValueUsingSetter(value:getter:));
|
||||
- (void)setObjectInDictionary:(id)object
|
||||
forKey:(NSString *)key
|
||||
getterNamed:(NSString *)getterName NS_SWIFT_NAME(setObjectInDictionary(object:key:getter:));
|
||||
|
||||
- (nullable id)valueSafeForKey:(NSString *)key NS_SWIFT_NAME(valueSafe(key:));
|
||||
- (void)setValueSafe:(id)value forKey:(NSString *)key NS_SWIFT_NAME(setValueSafe(value:key:));
|
||||
|
||||
#pragma mark *** Methods calling ***
|
||||
|
||||
- (void)callMethodNamed:(NSString *)methodName NS_SWIFT_NAME(callMethod(named:));
|
||||
- (void)callMethodNamed:(NSString *)methodName withObject:(nullable id)argument NS_SWIFT_NAME(callMethod(named:with:));
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
//
|
||||
// NSObject+Extension.m
|
||||
// Blurberry
|
||||
//
|
||||
// Created by Pavel Puzyrev on 07.09.2020.
|
||||
//
|
||||
// Copyright (c) 2020 Pavel Puzyrev <cannedapp@yahoo.com>
|
||||
//
|
||||
// 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 "NSObject+Extension.h"
|
||||
#import <objc/runtime.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
|
||||
//@class BlurWrapper;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@implementation NSObject (Extension)
|
||||
|
||||
#pragma mark - Public Methods
|
||||
|
||||
#pragma mark *** Values getting ***
|
||||
|
||||
- (nullable NSArray<NSObject *> *)arrayValueByGetterNamed:(NSString *)getterName {
|
||||
return [self valueOfType:[NSArray class] getterNamed:getterName];
|
||||
}
|
||||
|
||||
- (nullable NSDictionary<NSString *, id> *)dictionaryValueByGetterNamed:(NSString *)getterName {
|
||||
return [self valueOfType:[NSDictionary class] getterNamed:getterName];
|
||||
}
|
||||
|
||||
- (nullable NSString *)stringValueByGetterNamed:(NSString *)getterName {
|
||||
return [self valueOfType:[NSString class] getterNamed:getterName];
|
||||
}
|
||||
|
||||
- (nullable UIColor *)colorValueByGetterNamed:(NSString *)getterName {
|
||||
return [self valueOfType:[UIColor class] getterNamed:getterName];
|
||||
}
|
||||
|
||||
#pragma mark *** Values setting ***
|
||||
|
||||
- (void)setIVarValue:(nullable id)value getterNamed:(NSString *)getterName {
|
||||
@try {
|
||||
SEL getterSelector = NSSelectorFromString(getterName);
|
||||
SEL setterSelector = NSSelectorFromString([self setterMethodName:getterName]);
|
||||
NSString *ivarName = [NSString stringWithFormat:@"_%@", getterName];
|
||||
if ([self respondsToSelector:getterSelector] && [self respondsToSelector:setterSelector]) {
|
||||
[self setValue:value forKey:ivarName];
|
||||
}
|
||||
} @catch (NSException *exception) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setValueUsingSetter:(nullable id)value getterNamed:(NSString *)getterName {
|
||||
[self callMethodNamed:[self setterMethodName:getterName] withObject:value];
|
||||
}
|
||||
|
||||
- (void)setObjectInDictionary:(id)object forKey:(NSString *)key getterNamed:(NSString *)getterName {
|
||||
NSMutableDictionary *dictionary = [[self dictionaryValueByGetterNamed:getterName] mutableCopy];
|
||||
if (!dictionary) {
|
||||
return;
|
||||
}
|
||||
|
||||
dictionary[key] = object;
|
||||
|
||||
[self callMethodNamed:[self setterMethodName:getterName] withObject:[dictionary copy]];
|
||||
}
|
||||
|
||||
- (nullable id)valueSafeForKey:(NSString *)key {
|
||||
@try {
|
||||
return [self valueForKey:key];
|
||||
} @catch (NSException *exception) {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setValueSafe:(id)value forKey:(NSString *)key {
|
||||
@try {
|
||||
[self setValue:value forKey:key];
|
||||
} @catch (NSException *exception) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark *** Methods calling ***
|
||||
|
||||
- (void)callMethodNamed:(NSString *)methodName {
|
||||
[self callMethodNamed:methodName withObject:nil];
|
||||
}
|
||||
|
||||
- (void)callMethodNamed:(NSString *)methodName withObject:(nullable id)argument {
|
||||
@try {
|
||||
SEL selector = NSSelectorFromString(methodName);
|
||||
if ([self respondsToSelector:selector]) {
|
||||
_Pragma("clang diagnostic push")
|
||||
_Pragma("clang diagnostic ignored \"-Warc-performSelector-leaks\"")
|
||||
if (argument) {
|
||||
[self performSelector:selector withObject:argument];
|
||||
} else {
|
||||
[self performSelector:selector];
|
||||
}
|
||||
_Pragma("clang diagnostic pop")
|
||||
}
|
||||
} @catch (NSException *exception) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Private Methods
|
||||
|
||||
- (nullable id)valueOfType:(Class)type getterNamed:(NSString *)getterName {
|
||||
id typedValue;
|
||||
@try {
|
||||
SEL selector = NSSelectorFromString(getterName);
|
||||
if ([self respondsToSelector:selector]) {
|
||||
_Pragma("clang diagnostic push")
|
||||
_Pragma("clang diagnostic ignored \"-Warc-performSelector-leaks\"")
|
||||
id value = [self performSelector:selector];
|
||||
if ([value isKindOfClass:type]) {
|
||||
typedValue = value;
|
||||
}_Pragma("clang diagnostic pop")
|
||||
}
|
||||
} @catch (NSException *exception) {
|
||||
|
||||
} @finally {
|
||||
return typedValue;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)setterMethodName:(NSString *)getterMethodName {
|
||||
NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en-US"];
|
||||
NSString *firstChar = [getterMethodName substringToIndex:1];
|
||||
NSString *folded = [firstChar stringByFoldingWithOptions:NSDiacriticInsensitiveSearch locale:locale];
|
||||
NSString *pascalCasedResult = [folded.uppercaseString stringByAppendingString:[getterMethodName substringFromIndex:1]];
|
||||
|
||||
return [NSString stringWithFormat:@"set%@:", pascalCasedResult];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,50 @@
|
||||
//
|
||||
// Blurberry.swift
|
||||
// Blurberry
|
||||
//
|
||||
// Created by Pavel Puzyrev on 09.09.2020.
|
||||
//
|
||||
// Copyright (c) 2020 Pavel Puzyrev <cannedapp@yahoo.com>
|
||||
//
|
||||
// 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
|
||||
|
||||
public struct BlurWrapper<Base> {
|
||||
|
||||
public let base: Base
|
||||
|
||||
public init(_ base: Base) {
|
||||
self.base = base
|
||||
}
|
||||
}
|
||||
|
||||
public protocol BlurCompatible: AnyObject { }
|
||||
|
||||
public extension BlurCompatible {
|
||||
|
||||
var blur: BlurWrapper<Self> {
|
||||
get {
|
||||
BlurWrapper(self)
|
||||
}
|
||||
set { }
|
||||
}
|
||||
}
|
||||
|
||||
extension UIVisualEffectView: BlurCompatible { }
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
//
|
||||
// VisualEffectView+Internal.swift
|
||||
// Blurberry
|
||||
//
|
||||
// Created by Pavel Puzyrev on 07.09.2020.
|
||||
//
|
||||
// Copyright (c) 2020 Pavel Puzyrev <cannedapp@yahoo.com>
|
||||
//
|
||||
// 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
|
||||
|
||||
internal extension BlurWrapper where Base: UIVisualEffectView {
|
||||
|
||||
// MARK: iOS 13, 14
|
||||
|
||||
var backdropView: UIView? {
|
||||
base.subviews.first {
|
||||
type(of: $0) == NSClassFromString("_UIVisualEffectBackdropView")
|
||||
}
|
||||
}
|
||||
|
||||
var overlayView: UIView? {
|
||||
base.subviews.first {
|
||||
type(of: $0) == NSClassFromString("_UIVisualEffectSubview")
|
||||
}
|
||||
}
|
||||
|
||||
var gaussianBlurFilter: NSObject? {
|
||||
backdropView?.arrayValue(getter: "filters")?.first {
|
||||
$0.stringValue(getter: "filterType") == "gaussianBlur"
|
||||
}
|
||||
}
|
||||
|
||||
var sourceOverEffect: NSObject? {
|
||||
overlayView?.arrayValue(getter: "viewEffects")?.first {
|
||||
$0.stringValue(getter: "filterType") == "sourceOver"
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: iOS 10, 11, 12
|
||||
|
||||
var blurEffect: NSObject? {
|
||||
guard let effect = base.effect, type(of: effect) == NSClassFromString("_UICustomBlurEffect") else {
|
||||
return nil
|
||||
}
|
||||
return base.effect
|
||||
}
|
||||
|
||||
func updateBlurEffect(blurRadius: CGFloat, tintColor: UIColor?) {
|
||||
let effect = (NSClassFromString("_UICustomBlurEffect") as? UIBlurEffect.Type)?.init()
|
||||
effect?.setValueSafe(value: blurRadius, key: "blurRadius")
|
||||
effect?.setValueSafe(value: tintColor ?? UIColor(white: 1.0, alpha: 0.3), key: "colorTint")
|
||||
effect?.setValueSafe(value: 1.0, key: "scale")
|
||||
effect?.setValueSafe(value: 1.0, key: "colorTintAlpha")
|
||||
base.effect = effect
|
||||
}
|
||||
|
||||
// MARK: Changes
|
||||
|
||||
func prepareForChanges() {
|
||||
if #available(iOS 13, *) {
|
||||
base.effect = UIBlurEffect(style: .light)
|
||||
gaussianBlurFilter?.setIVarValue(value: 1.0, getter: "requestedScaleHint")
|
||||
}
|
||||
}
|
||||
|
||||
func applyChanges() {
|
||||
if #available(iOS 13, *) {
|
||||
backdropView?.callMethod(named: "applyRequestedFilterEffects")
|
||||
}
|
||||
}
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
//
|
||||
// VisualEffectView+Public.swift
|
||||
// Blurberry
|
||||
//
|
||||
// Created by Pavel Puzyrev on 09.09.2020.
|
||||
//
|
||||
// Copyright (c) 2020 Pavel Puzyrev <cannedapp@yahoo.com>
|
||||
//
|
||||
// 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
|
||||
import ContainerControllerSwift
|
||||
|
||||
|
||||
|
||||
public extension BlurWrapper where Base: UIVisualEffectView {
|
||||
|
||||
var radius: CGFloat {
|
||||
get {
|
||||
if #available(iOS 13, *) {
|
||||
return gaussianBlurFilter?.dictionaryValue(getter: "requestedValues")?["inputRadius"] as? CGFloat ?? 0.0
|
||||
} else {
|
||||
return blurEffect?.valueSafe(key: "blurRadius") as? CGFloat ?? 0.0
|
||||
}
|
||||
}
|
||||
set {
|
||||
prepareForChanges()
|
||||
|
||||
if #available(iOS 13, *) {
|
||||
gaussianBlurFilter?.setObjectInDictionary(object: newValue, key: "inputRadius", getter: "requestedValues")
|
||||
} else {
|
||||
updateBlurEffect(blurRadius: newValue, tintColor: tintColor)
|
||||
}
|
||||
|
||||
applyChanges()
|
||||
}
|
||||
}
|
||||
|
||||
var tintColor: UIColor? {
|
||||
get {
|
||||
if #available(iOS 13, *) {
|
||||
return sourceOverEffect?.colorValue(getter: "color")
|
||||
} else {
|
||||
return blurEffect?.colorValue(getter: "colorTint")
|
||||
}
|
||||
}
|
||||
set {
|
||||
prepareForChanges()
|
||||
|
||||
if #available(iOS 13, *) {
|
||||
sourceOverEffect?.setValueUsingSetter(value: newValue, getter: "color")
|
||||
sourceOverEffect?.callMethod(named: "applyRequestedEffectToView:", with: overlayView)
|
||||
} else {
|
||||
updateBlurEffect(blurRadius: radius, tintColor: newValue)
|
||||
}
|
||||
|
||||
applyChanges()
|
||||
}
|
||||
}
|
||||
}
|
||||
+345
@@ -0,0 +1,345 @@
|
||||
//
|
||||
// UIButton+AllAnimations.swift
|
||||
// PlusBank
|
||||
//
|
||||
// Created by Рустам Мотыгуллин on 20.08.2021.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
|
||||
import AVFoundation
|
||||
|
||||
// MARK: Animations
|
||||
|
||||
extension UIButton {
|
||||
|
||||
// MARK: - Alpha
|
||||
|
||||
func updateChangeAlpha(with views: [UIView], visible: Alpha, value: CGFloat, duration: Speed?) {
|
||||
|
||||
var alpha: CGFloat = 1.0
|
||||
if visible != .visible {
|
||||
alpha = value
|
||||
}
|
||||
|
||||
if let duration = duration {
|
||||
UIView.animate(with: duration) {
|
||||
for view in views {
|
||||
view.alpha = alpha
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for view in views {
|
||||
view.alpha = alpha
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Layer Gray
|
||||
|
||||
func updateLayerGray(with views: [UIView], visible: Alpha, value: CGFloat, duration: Speed?) {
|
||||
|
||||
guard let mainView = views.last else { return }
|
||||
let shTag = 31
|
||||
let spechialColor = UIColor(red: 10.0 / 255.0, green: 13.0 / 255.0, blue: 38.0 / 255.0, alpha: value)
|
||||
|
||||
if visible == .visible {
|
||||
|
||||
if let view = mainView.viewWithTag(shTag) {
|
||||
view.alpha = 1
|
||||
|
||||
if let duration = duration {
|
||||
UIView.animate(
|
||||
with: duration,
|
||||
animations: {
|
||||
view.alpha = 0
|
||||
},
|
||||
completion: { fin in
|
||||
view.removeFromSuperview()
|
||||
}
|
||||
)
|
||||
} else {
|
||||
view.alpha = 0
|
||||
view.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if mainView.viewWithTag(shTag) == nil {
|
||||
|
||||
var radius = mainView.layer.cornerRadius
|
||||
if radius == 0 {
|
||||
for sv in mainView.subviews {
|
||||
if sv.layer.cornerRadius != 0 {
|
||||
radius = mainView.layer.cornerRadius
|
||||
}
|
||||
if let desFig = mainView as? DesignFigure {
|
||||
radius = desFig.cornerRadius
|
||||
if radius == -1 {
|
||||
let minSize = min(mainView.frame.width, mainView.bounds.height)
|
||||
radius = minSize / 2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let shadowView = UIView(frame: mainView.bounds)
|
||||
shadowView.tag = shTag
|
||||
shadowView.backgroundColor = spechialColor
|
||||
shadowView.layer.cornerRadius = radius
|
||||
mainView.addSubview(shadowView)
|
||||
shadowView.alpha = ((duration == nil) ? 1 : 0)
|
||||
if let duration = duration {
|
||||
if duration == .zero {
|
||||
shadowView.alpha = 1
|
||||
} else {
|
||||
UIView.animate(with: duration) {
|
||||
shadowView.alpha = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension UIView {
|
||||
|
||||
// MARK: - Flash
|
||||
|
||||
func flash(duration: Speed = .ms200) {
|
||||
|
||||
let flash = CABasicAnimation(keyPath: "opacity")
|
||||
flash.duration = duration.rawValue
|
||||
flash.fromValue = 1
|
||||
flash.toValue = 0.1
|
||||
flash.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
|
||||
flash.autoreverses = true
|
||||
flash.repeatCount = 3
|
||||
|
||||
layer.add(flash, forKey: nil)
|
||||
}
|
||||
|
||||
// MARK: - Change Color
|
||||
|
||||
|
||||
func animationOpacity(duration: CGFloat, value: CGFloat, beginTime: CGFloat = 0.0) {
|
||||
//
|
||||
let pulseAnimation = CABasicAnimation(keyPath: #keyPath(CALayer.opacity))// "opacity")
|
||||
pulseAnimation.fromValue = 0
|
||||
pulseAnimation.toValue = 1 // 0.1
|
||||
pulseAnimation.duration = 100
|
||||
|
||||
pulseAnimation.isRemovedOnCompletion = false
|
||||
pulseAnimation.beginTime = CACurrentMediaTime() + beginTime //CACurrentMediaTime() + AVCoreAnimationBeginTimeAtZero + 1.0
|
||||
pulseAnimation.fillMode = .both
|
||||
pulseAnimation.isAdditive = false
|
||||
// pulseAnimation.repeatCount = .infinit
|
||||
layer.add(pulseAnimation, forKey: "opacityIn")
|
||||
|
||||
|
||||
|
||||
let animtingLayer: CALayer = CALayer(layer: layer)
|
||||
|
||||
|
||||
let pulseAnimation2 = CABasicAnimation(keyPath: #keyPath(CALayer.opacity))// "opacity")
|
||||
pulseAnimation2.fromValue = 1
|
||||
pulseAnimation2.toValue = beginTime // 0.1
|
||||
pulseAnimation2.duration = 100
|
||||
pulseAnimation2.isRemovedOnCompletion = false
|
||||
pulseAnimation2.beginTime = 1.0// AVCoreAnimationBeginTimeAtZero + 0.0
|
||||
pulseAnimation2.fillMode = .both
|
||||
pulseAnimation2.isAdditive = false
|
||||
// pulseAnimation2.repeatCount = .infinit
|
||||
|
||||
|
||||
animtingLayer.add(pulseAnimation2, forKey: "opacityOut")
|
||||
layer.addSublayer(animtingLayer)
|
||||
|
||||
|
||||
layer.opacity = 0.0
|
||||
|
||||
}
|
||||
|
||||
func animationColor(duration: Speed = .s1, colors: [UIColor]) {
|
||||
|
||||
// layer.removeAnimation(forKey: "backgroundColor")
|
||||
// layer.sublayers?.forEach {
|
||||
// $0.mask?.removeAnimation(forKey: "backgroundColor")
|
||||
// }
|
||||
// layer.removeAllAnimations()
|
||||
// layer.removeFromSuperlayer()
|
||||
let darkerColor = colors[0]
|
||||
let lighterColor = colors[1]
|
||||
let pulseAnimation = CABasicAnimation(keyPath: #keyPath(CALayer.backgroundColor))
|
||||
pulseAnimation.fromValue = darkerColor
|
||||
pulseAnimation.toValue = lighterColor
|
||||
pulseAnimation.duration = 1
|
||||
pulseAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
|
||||
pulseAnimation.autoreverses = true
|
||||
pulseAnimation.repeatCount = .infinity
|
||||
pulseAnimation.isRemovedOnCompletion = false
|
||||
|
||||
// let color = CABasicAnimation(keyPath: "backgroundColor")
|
||||
// color.fromValue = colors[0]// UIColor.green.cgColor
|
||||
// color.toValue = colors[1] // UIColor.red.cgColor
|
||||
// color.duration = duration.rawValue
|
||||
// color.beginTime = CACurrentMediaTime() + 0.3
|
||||
//// color.autoreverses = true
|
||||
//// color.repeatDuration = 1
|
||||
// color.autoreverses = true
|
||||
// color.repeatCount = 500
|
||||
// layer.cornerRadius = self.layer.cornerRadius
|
||||
//// layer.add(color, forKey: "animationColor")
|
||||
|
||||
|
||||
|
||||
layer.removeAnimation(forKey: "animationColor")
|
||||
layer.add(pulseAnimation, forKey: "animationColor")
|
||||
backgroundColor = .clear
|
||||
}
|
||||
|
||||
// MARK: - Pulsate
|
||||
|
||||
func pulsate(duration: Speed = .ms200, value: CGFloat = 0.9) {
|
||||
|
||||
let pulse = CASpringAnimation(keyPath: "transform.scale")
|
||||
pulse.duration = duration.rawValue
|
||||
pulse.fromValue = value
|
||||
pulse.toValue = 1.0
|
||||
pulse.autoreverses = true
|
||||
pulse.repeatCount = 2
|
||||
pulse.initialVelocity = 0.5
|
||||
pulse.damping = 1.0
|
||||
|
||||
layer.add(pulse, forKey: "pulse")
|
||||
}
|
||||
|
||||
// MARK: - Pulsate 2
|
||||
|
||||
func pulsateNew(visible: Alpha, value: CGFloat = 0.9) {
|
||||
if visible == .visible {
|
||||
animate(self, transform: .identity)
|
||||
} else {
|
||||
animate(self, transform: CGAffineTransform.identity.scaledBy(x: value, y: value))
|
||||
}
|
||||
}
|
||||
|
||||
private func animate(_ view: UIView, transform: CGAffineTransform) {
|
||||
UIView.animate(withDuration: 0.4,
|
||||
delay: 0,
|
||||
usingSpringWithDamping: 0.5,
|
||||
initialSpringVelocity: 3,
|
||||
options: [.curveEaseInOut],
|
||||
animations: {
|
||||
view.transform = transform
|
||||
}, completion: nil)
|
||||
}
|
||||
|
||||
// MARK: - Shake
|
||||
|
||||
func shake() {
|
||||
let animation = CAKeyframeAnimation(keyPath: "transform.translation.x")
|
||||
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
|
||||
animation.duration = 0.6
|
||||
animation.values = [-20.0, 20.0, -20.0, 20.0, -10.0, 10.0, -5.0, 5.0, 0.0 ]
|
||||
layer.add(animation, forKey: "shake")
|
||||
}
|
||||
|
||||
// MARK: - Shake 2
|
||||
|
||||
func shakeNew(duration: Speed = .ms50) {
|
||||
|
||||
let shake = CABasicAnimation(keyPath: "position")
|
||||
shake.duration = duration.rawValue
|
||||
shake.repeatCount = 2
|
||||
shake.autoreverses = true
|
||||
|
||||
let fromPoint = CGPoint(x: center.x - 5, y: center.y)
|
||||
let fromValue = NSValue(cgPoint: fromPoint)
|
||||
|
||||
let toPoint = CGPoint(x: center.x + 5, y: center.y)
|
||||
let toValue = NSValue(cgPoint: toPoint)
|
||||
|
||||
shake.fromValue = fromValue
|
||||
shake.toValue = toValue
|
||||
|
||||
layer.add(shake, forKey: "position")
|
||||
}
|
||||
|
||||
// MARK: - Android Pulse
|
||||
|
||||
func androidPulseAnimation( duration: Speed = .ms1, color: UIColor, position: CGPoint) {
|
||||
|
||||
let pulse = PulseAnimation(numberOfPulse: 1, radius: 200, postion: position)
|
||||
pulse.animationDuration = duration.rawValue
|
||||
pulse.backgroundColor = color.cgColor // #colorLiteral(red: 0.05282949957, green: 0.5737867104, blue: 1, alpha: 1)
|
||||
layer.insertSublayer(pulse, below: layer)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
class PulseAnimation: CALayer {
|
||||
|
||||
var animationGroup = CAAnimationGroup()
|
||||
var animationDuration: TimeInterval = 1.5
|
||||
var radius: CGFloat = 200
|
||||
var numebrOfPulse: Float = Float.infinity
|
||||
|
||||
override init(layer: Any) {
|
||||
super.init(layer: layer)
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
init(numberOfPulse: Float = Float.infinity, radius: CGFloat, postion: CGPoint){
|
||||
super.init()
|
||||
self.backgroundColor = UIColor.black.cgColor
|
||||
self.contentsScale = UIScreen.main.scale
|
||||
self.opacity = 0
|
||||
self.radius = radius
|
||||
self.numebrOfPulse = numberOfPulse
|
||||
self.position = postion
|
||||
|
||||
self.bounds = CGRect(x: 0, y: 0, width: radius*2, height: radius*2)
|
||||
self.cornerRadius = radius
|
||||
|
||||
DispatchQueue.global(qos: .default).async {
|
||||
self.setupAnimationGroup()
|
||||
DispatchQueue.main.async {
|
||||
self.add(self.animationGroup, forKey: "pulse")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func scaleAnimation() -> CABasicAnimation {
|
||||
let scaleAnimaton = CABasicAnimation(keyPath: "transform.scale.xy")
|
||||
scaleAnimaton.fromValue = NSNumber(value: 0)
|
||||
scaleAnimaton.toValue = NSNumber(value: 1)
|
||||
scaleAnimaton.duration = animationDuration
|
||||
return scaleAnimaton
|
||||
}
|
||||
|
||||
func createOpacityAnimation() -> CAKeyframeAnimation {
|
||||
let opacityAnimiation = CAKeyframeAnimation(keyPath: "opacity")
|
||||
opacityAnimiation.duration = animationDuration
|
||||
opacityAnimiation.values = [0.4,0.8,0]
|
||||
opacityAnimiation.keyTimes = [0,0.3,1]
|
||||
return opacityAnimiation
|
||||
}
|
||||
|
||||
func setupAnimationGroup() {
|
||||
self.animationGroup.duration = animationDuration
|
||||
self.animationGroup.repeatCount = numebrOfPulse
|
||||
let defaultCurve = CAMediaTimingFunction(name: CAMediaTimingFunctionName.default)
|
||||
self.animationGroup.timingFunction = defaultCurve
|
||||
self.animationGroup.animations = [scaleAnimation(),createOpacityAnimation()]
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
//
|
||||
// UIButton+Extention.swift
|
||||
// FigmaConvertXib
|
||||
//
|
||||
// Created by Рустам Мотыгуллин on 06.11.2021.
|
||||
// Copyright © 2021 mrusta. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class Button: UIButton {
|
||||
|
||||
var onPressed: (() -> Void)?
|
||||
var onReleased: (() -> Void)?
|
||||
|
||||
init() {
|
||||
super.init(frame: .zero)
|
||||
addTarget(self, action: #selector(self.pressed), for: .touchDown)
|
||||
addTarget(self, action: #selector(self.released), for: .touchUpInside)
|
||||
addTarget(self, action: #selector(self.released), for: .touchUpOutside)
|
||||
setup()
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder: NSCoder) { fatalError() }
|
||||
|
||||
@objc func setup() {}
|
||||
|
||||
@objc func pressed() {
|
||||
onPressed?()
|
||||
}
|
||||
|
||||
@objc func released() {
|
||||
onReleased?()
|
||||
}
|
||||
}
|
||||
|
||||
/// Simple tap implementation
|
||||
typealias Closure = () -> ()
|
||||
typealias ClosureClick = (_ event: UIControl.Event) -> ()
|
||||
|
||||
|
||||
class ClosureSleeve {
|
||||
let closure: Closure
|
||||
init(_ closure: @escaping Closure) {
|
||||
self.closure = closure
|
||||
}
|
||||
@objc func invoke () {
|
||||
closure()
|
||||
}
|
||||
}
|
||||
|
||||
extension UIControl {
|
||||
func tap(for controlEvents: UIControl.Event = .touchUpInside,
|
||||
_ closure: @escaping Closure) {
|
||||
let sleeve = ClosureSleeve(closure)
|
||||
addTarget(sleeve, action: #selector(ClosureSleeve.invoke), for: controlEvents)
|
||||
objc_setAssociatedObject(self,
|
||||
String(format: "[%d]", arc4random()),
|
||||
sleeve,
|
||||
objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
|
||||
}
|
||||
|
||||
func tapClassic(_ closure: @escaping ClosureClick) {
|
||||
self.tap(for: .touchDown) { // [weak self] in
|
||||
print(" btn tapClassic touchDown")
|
||||
closure(.touchDown)
|
||||
}
|
||||
self.tap(for: .touchUpInside) { // [weak self] in
|
||||
print(" btn tapClassic touchUpInside")
|
||||
closure(.touchUpInside)
|
||||
}
|
||||
self.tap(for: .touchUpOutside) { // [weak self] in
|
||||
print(" btn tapClassic touchUpOutside")
|
||||
closure(.touchUpOutside)
|
||||
}
|
||||
self.tap(for: .touchCancel) { // [weak self] in
|
||||
print(" btn tapClassic touchCancel")
|
||||
closure(.touchCancel)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+362
@@ -0,0 +1,362 @@
|
||||
//
|
||||
// ViewAnimationSelect.swift
|
||||
// PlusBank
|
||||
//
|
||||
// Created by Рустам Мотыгуллин on 22.06.2021.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
@IBDesignable
|
||||
class ButtonAnimationView: UIView {
|
||||
|
||||
@IBInspectable var cornerRadius: CGFloat = 0.0
|
||||
@IBInspectable var animationType: Int = 0
|
||||
|
||||
public var type: UIButton.TapType = .alpha(0.5)
|
||||
private var subviewsAnimation = false
|
||||
public var button: UIButton?
|
||||
|
||||
// MARK: - Initialize
|
||||
|
||||
required public init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
updateSubviews()
|
||||
}
|
||||
|
||||
public init() {
|
||||
super.init(frame: .zero)
|
||||
updateSubviews()
|
||||
}
|
||||
|
||||
override func layoutSubviews() { updateSubviews() }
|
||||
|
||||
public func setAnimation(type: UIButton.TapType, views: [UIView]? = nil) {
|
||||
self.type = type
|
||||
// updateSubviews()
|
||||
|
||||
layer.cornerRadius = cornerRadius
|
||||
|
||||
var nviews: [UIView] = []
|
||||
if let views = views {
|
||||
nviews = views
|
||||
} else {
|
||||
nviews.append(self)
|
||||
subviews.forEach {
|
||||
nviews.append($0)
|
||||
}
|
||||
}
|
||||
|
||||
button?.tapHideAnimation(views: nviews, type: type, callback: { type in
|
||||
if type == .touchUpInside {
|
||||
|
||||
}
|
||||
})
|
||||
subviewsAnimation = true
|
||||
}
|
||||
|
||||
public func updateSubviews() {
|
||||
|
||||
if button == nil {
|
||||
createButton()
|
||||
}
|
||||
|
||||
|
||||
if animationType != 0 {
|
||||
|
||||
let type: UIButton.TapType = {
|
||||
switch animationType {
|
||||
case 1: return .alpha(0.5)
|
||||
case 2: return .layerGray(0.1845)
|
||||
case 3: return .pulsate(new: false, value: 0.9)
|
||||
case 4: return .pulsate(new: true, value: 0.9)
|
||||
case 5: return .shake(new: false)
|
||||
case 6: return .shake(new: true)
|
||||
case 7: return .flash
|
||||
case 8: return .color([.white, .red])
|
||||
case 9: return .androidStyle(color: .white)
|
||||
default: return .alpha(0.5)
|
||||
}
|
||||
}()
|
||||
|
||||
setAnimation(type: type)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public func createButton() {
|
||||
let btn = UIButton(type: .system)
|
||||
btn.frame = bounds
|
||||
btn.backgroundColor = .clear
|
||||
btn.setTitle(nil, for: .normal)
|
||||
btn.contentVerticalAlignment = .center
|
||||
btn.autoresizingMask = [ .flexibleTopMargin,
|
||||
.flexibleBottomMargin,
|
||||
.flexibleLeftMargin,
|
||||
.flexibleRightMargin,
|
||||
.flexibleWidth,
|
||||
.flexibleHeight ]
|
||||
addSubview(btn)
|
||||
self.button = btn
|
||||
}
|
||||
}
|
||||
|
||||
extension UIButton {
|
||||
|
||||
// MARK: Tap Animation Type
|
||||
|
||||
static let setTag = 963
|
||||
|
||||
enum TapType {
|
||||
case alpha(_ value: CGFloat) // 0.5
|
||||
case layerGray(_ value: CGFloat = 0.1845) // 0.1845 add black shadow
|
||||
case pulsate(new: Bool = false, value: CGFloat = 0.9)
|
||||
case shake(new: Bool = false)
|
||||
case flash
|
||||
case color(_ arr: [UIColor] = [.white, .red])
|
||||
case androidStyle(color: UIColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1))
|
||||
}
|
||||
|
||||
// MARK: Set
|
||||
|
||||
func tapHideAnimation(
|
||||
view: UIView?,
|
||||
type: TapType = .alpha(0.0),
|
||||
hideSpeed: Speed? = .ms100,
|
||||
callback: ((UIControl.Event) -> Void)? = nil
|
||||
) {
|
||||
guard let view = view else { return }
|
||||
tapHideAnimation(views: [view], type: type, callback: callback)
|
||||
}
|
||||
|
||||
func tapHideAnimation(
|
||||
views: [UIView],
|
||||
type: TapType = .alpha(0.0),
|
||||
hideSpeed: Speed? = .ms100,
|
||||
callback: ((UIControl.Event) -> Void)? = nil
|
||||
) {
|
||||
|
||||
switch type {
|
||||
case .androidStyle(let color): do {
|
||||
|
||||
var tapAdded: Bool = false
|
||||
gestureRecognizers?.forEach {
|
||||
if $0 is BindableGestureRecognizer {
|
||||
tapAdded = true
|
||||
}
|
||||
}
|
||||
if !tapAdded {
|
||||
|
||||
let tapp = BindableGestureRecognizer { [weak self] sender in
|
||||
|
||||
guard let superview = self?.superview else { return }
|
||||
let touchPoint = sender.location(in: superview)
|
||||
|
||||
guard let dur: Speed = self?.defaultDuration(type: type) else { return }
|
||||
self?.androidPulseAnimation(duration: dur, color: color, position: touchPoint)
|
||||
|
||||
|
||||
if sender.state == .began {
|
||||
callback?(.touchDown)
|
||||
} else if sender.state == .changed {
|
||||
callback?(.touchDown)
|
||||
} else if sender.state == .ended {
|
||||
callback?(.touchUpInside)
|
||||
}
|
||||
// callback?(.touchUpInside)
|
||||
}
|
||||
addGestureRecognizer(tapp)
|
||||
}
|
||||
}
|
||||
default: break
|
||||
}
|
||||
|
||||
|
||||
// if tag != UIButton.setTag { return }
|
||||
// tag = UIButton.setTag
|
||||
|
||||
tap(for: .touchDown) { [weak self] in
|
||||
callback?(.touchDown)
|
||||
self?.hideAnimation(with: views, type: type, event: .touchDown, duration: hideSpeed)
|
||||
}
|
||||
tap(for: .touchUpInside) { [weak self] in
|
||||
callback?(.touchUpInside)
|
||||
self?.hideAnimation(with: views, visible: .visible, type: type, event: .touchUpInside)
|
||||
}
|
||||
tap(for: .touchUpOutside) { [weak self] in
|
||||
callback?(.touchUpOutside)
|
||||
self?.hideAnimation(with: views, visible: .visible, type: type, event: .touchUpOutside)
|
||||
}
|
||||
tap(for: .touchCancel) { [weak self] in
|
||||
callback?(.touchCancel)
|
||||
self?.hideAnimation(with: views, visible: .visible, type: type, event: .touchCancel)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - De-Select
|
||||
|
||||
func deselected(_ views: [UIView]) {
|
||||
hideAnimation(
|
||||
with: views,
|
||||
visible: .visible
|
||||
)
|
||||
}
|
||||
|
||||
func defaultDuration(type: TapType) -> Speed {
|
||||
switch type {
|
||||
case .alpha(_), .layerGray(_): return .ms300
|
||||
case .pulsate(_, _), .flash: return .ms200
|
||||
case .shake(_): return .ms50
|
||||
case .color(_), .androidStyle(_): return .s1
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Animation Filter
|
||||
|
||||
func hideAnimation(
|
||||
with views: [UIView],
|
||||
visible: Alpha = .invisible,
|
||||
type: TapType = .alpha(0.0),
|
||||
event: UIControl.Event = .touchUpInside,
|
||||
duration: Speed? = nil
|
||||
) {
|
||||
|
||||
let dur: Speed = defaultDuration(type: type)
|
||||
switch type {
|
||||
case .alpha(let value):
|
||||
updateChangeAlpha(with: views, visible: visible, value: value, duration: dur)
|
||||
case .layerGray(let value):
|
||||
if event == .touchDown {
|
||||
updateLayerGray(with: views, visible: .invisible, value: value, duration: nil)
|
||||
} else {
|
||||
updateLayerGray(with: views, visible: .visible, value: value, duration: dur)
|
||||
}
|
||||
default: break
|
||||
}
|
||||
|
||||
views.forEach {
|
||||
switch type {
|
||||
case .flash: $0.flash(duration: dur)
|
||||
case .color(let arr): $0.animationColor(duration: dur, colors: arr)
|
||||
case .pulsate(let new, let value):
|
||||
if new { $0.pulsateNew(visible: visible, value: value) }
|
||||
else { $0.pulsate(duration: dur, value: value) }
|
||||
case .shake(let new):
|
||||
if new { $0.shake() }
|
||||
else { $0.shakeNew(duration: dur) }
|
||||
// case .androidClickable:
|
||||
// androidPulseAnimation(duration: dur)
|
||||
default: break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension UIControl.Event {
|
||||
|
||||
func strName() -> String {
|
||||
return enevtName(self)
|
||||
}
|
||||
|
||||
func enevtName(_ event: UIControl.Event) -> String {
|
||||
switch event {
|
||||
case .touchDown: return "touch.Down"
|
||||
case .touchCancel: return "touch.Cancel"
|
||||
case .touchUpOutside: return "touch.Up.Outside"
|
||||
case .touchUpInside: return "touch.Up.Inside"
|
||||
default: return ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typealias Interval = TimeInterval
|
||||
|
||||
enum Speed: Interval {
|
||||
case zero = 0.0
|
||||
case ms1 = 0.001
|
||||
case ms50 = 0.05
|
||||
case ms100 = 0.1
|
||||
case ms200 = 0.2
|
||||
case ms300 = 0.3
|
||||
case ms350 = 0.35
|
||||
case ms400 = 0.4
|
||||
case ms450 = 0.45
|
||||
case ms500 = 0.5
|
||||
case s1 = 1.0
|
||||
case s2 = 2.0
|
||||
case s3 = 3.0
|
||||
case s4 = 4.0
|
||||
case s5 = 5.0
|
||||
}
|
||||
|
||||
enum Delay: Interval {
|
||||
case ms1 = 0.001
|
||||
case ms100 = 0.1
|
||||
case ms200 = 0.2
|
||||
case ms300 = 0.3
|
||||
case ms400 = 0.4
|
||||
case ms500 = 0.5
|
||||
case s1 = 1.0
|
||||
case s2 = 2.0
|
||||
case s3 = 3.0
|
||||
case s4 = 4.0
|
||||
case s5 = 5.0
|
||||
}
|
||||
|
||||
|
||||
enum Alpha: CGFloat {
|
||||
case invisible = 0.0
|
||||
case halfTransluent = 0.5
|
||||
case visible = 1.0
|
||||
}
|
||||
|
||||
extension UIView {
|
||||
func setAlpha(_ alpha: Alpha) {
|
||||
self.alpha = alpha.rawValue
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//MARK: - Override animations
|
||||
extension UIView {
|
||||
|
||||
static func animate(with duration: Speed,
|
||||
animations: @escaping () -> Void) {
|
||||
UIView.animate(withDuration: duration.rawValue, animations: animations)
|
||||
}
|
||||
|
||||
static func animate(with duration: Speed,
|
||||
animations: @escaping () -> Void,
|
||||
completion: ((Bool) -> Void)?) {
|
||||
UIView.animate(withDuration: duration.rawValue,
|
||||
animations: animations,
|
||||
completion: completion)
|
||||
}
|
||||
|
||||
static func animate(with duration: Speed,
|
||||
delay: Delay,
|
||||
options: AnimationOptions,
|
||||
animations: @escaping () -> Void,
|
||||
completion: ((Bool) -> Void)?) {
|
||||
UIView.animate(withDuration: duration.rawValue,
|
||||
delay: delay.rawValue,
|
||||
options: options,
|
||||
animations: animations,
|
||||
completion: completion)
|
||||
}
|
||||
}
|
||||
|
||||
final class BindableGestureRecognizer: UITapGestureRecognizer {
|
||||
private var action: (UITapGestureRecognizer) -> Void
|
||||
|
||||
init(action: @escaping (UITapGestureRecognizer) -> Void) {
|
||||
self.action = action
|
||||
super.init(target: nil, action: nil)
|
||||
self.addTarget(self, action: #selector(execute(_:)))
|
||||
}
|
||||
|
||||
@objc private func execute(_ sender: UITapGestureRecognizer) {
|
||||
action(sender)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
|
||||
|
||||
import UIKit
|
||||
|
||||
class CopyableLabel: UILabel {
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
self.sharedInit()
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
self.sharedInit()
|
||||
}
|
||||
|
||||
func sharedInit() {
|
||||
self.isUserInteractionEnabled = true
|
||||
let gesture = UILongPressGestureRecognizer(target: self, action: #selector(self.showMenu))
|
||||
self.addGestureRecognizer(gesture)
|
||||
}
|
||||
|
||||
@objc func showMenu(_ recognizer: UILongPressGestureRecognizer) {
|
||||
self.becomeFirstResponder()
|
||||
|
||||
let menu = UIMenuController.shared
|
||||
|
||||
let locationOfTouchInLabel = recognizer.location(in: self)
|
||||
|
||||
if !menu.isMenuVisible {
|
||||
var rect = bounds
|
||||
rect.origin = locationOfTouchInLabel
|
||||
rect.size = CGSize(width: 1, height: 1)
|
||||
|
||||
if #available(iOS 13.0, *) {
|
||||
menu.showMenu(from: self, rect: rect)
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override func copy(_ sender: Any?) {
|
||||
let board = UIPasteboard.general
|
||||
|
||||
board.string = text
|
||||
|
||||
let menu = UIMenuController.shared
|
||||
|
||||
if #available(iOS 13.0, *) {
|
||||
menu.showMenu(from: self, rect: bounds)
|
||||
} else {
|
||||
menu.setMenuVisible(false, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
override var canBecomeFirstResponder: Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
|
||||
return action == #selector(UIResponderStandardEditActions.copy)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
|
||||
|
||||
import UIKit
|
||||
|
||||
@IBDesignable
|
||||
class DesignButton: UIButton {
|
||||
|
||||
@IBInspectable var hideAnimation: Bool = true
|
||||
|
||||
//MARK: Fill
|
||||
|
||||
@IBInspectable var fillColor: UIColor = .clear
|
||||
|
||||
//MARK: Gradient
|
||||
|
||||
@IBInspectable var grColor1: UIColor?
|
||||
@IBInspectable var grColor2: UIColor?
|
||||
@IBInspectable var grColor3: UIColor?
|
||||
@IBInspectable var grColor4: UIColor?
|
||||
@IBInspectable var grColor5: UIColor?
|
||||
@IBInspectable var grColor6: UIColor?
|
||||
|
||||
@IBInspectable var grStartPoint: CGPoint = .zero
|
||||
@IBInspectable var grEndPoint: CGPoint = .zero
|
||||
|
||||
//MARK: Inner Shadow
|
||||
|
||||
@IBInspectable var inShColor: UIColor = .clear
|
||||
@IBInspectable var inShRadius: CGFloat = 0.0
|
||||
@IBInspectable var inShOffset: CGSize = .zero
|
||||
|
||||
//MARK: Shadow
|
||||
|
||||
@IBInspectable var shColor: UIColor = .clear
|
||||
@IBInspectable var shRadius: CGFloat = 0.0
|
||||
@IBInspectable var shOffset: CGSize = .zero
|
||||
|
||||
//MARK: Border
|
||||
|
||||
@IBInspectable var brColor: UIColor = .clear
|
||||
@IBInspectable var brWidth: CGFloat = 0.0
|
||||
|
||||
//MARK: Blur
|
||||
|
||||
@IBInspectable var blur: CGFloat = 0.0
|
||||
|
||||
//MARK: Radius
|
||||
|
||||
@IBInspectable var cornerRadius: CGFloat = 0.0
|
||||
|
||||
|
||||
override func draw(_ rect: CGRect) {
|
||||
|
||||
layer.cornerRadius = self.cornerRadius(cornerRadius)
|
||||
layer.backgroundColor = fillColor.cgColor
|
||||
|
||||
addGradient()
|
||||
addInnerShadow()
|
||||
addShadow()
|
||||
addBorder()
|
||||
|
||||
add(blur: blur)
|
||||
}
|
||||
|
||||
private func addGradient() {
|
||||
|
||||
_ = add(gradient: grColor1,
|
||||
color2: grColor2,
|
||||
color3: grColor3,
|
||||
color4: grColor4,
|
||||
color5: grColor5,
|
||||
color6: grColor6,
|
||||
pointStart: grStartPoint,
|
||||
pointEnd: grEndPoint,
|
||||
cornerRadius: cornerRadius)
|
||||
|
||||
}
|
||||
|
||||
private func addInnerShadow() {
|
||||
|
||||
addInnerShadow(color: inShColor,
|
||||
radius: inShRadius,
|
||||
offset: inShOffset,
|
||||
cornerRadius: cornerRadius)
|
||||
|
||||
}
|
||||
|
||||
private func addShadow() {
|
||||
layer.shadowOffset = shOffset
|
||||
layer.shadowOpacity = 1.0
|
||||
layer.shadowRadius = shRadius
|
||||
layer.shadowColor = shColor.cgColor
|
||||
}
|
||||
|
||||
private func addBorder() {
|
||||
layer.borderColor = brColor.cgColor
|
||||
layer.borderWidth = brWidth
|
||||
}
|
||||
|
||||
override var isSelected: Bool {
|
||||
didSet {
|
||||
super.isSelected = isSelected
|
||||
}
|
||||
}
|
||||
|
||||
override var isHighlighted: Bool {
|
||||
didSet {
|
||||
// super.isHighlighted = isHighlighted
|
||||
}
|
||||
}
|
||||
|
||||
override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
|
||||
selectedLayer(show: true)
|
||||
return super.beginTracking(touch, with: event)
|
||||
}
|
||||
|
||||
override func continueTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
|
||||
let point = touch.location(in: self)
|
||||
let someFrame = bounds
|
||||
let highlighted = someFrame.contains(point)
|
||||
selectedLayer(show: highlighted)
|
||||
return super.continueTracking(touch, with: event)
|
||||
}
|
||||
|
||||
override func endTracking(_ touch: UITouch?, with event: UIEvent?) {
|
||||
selectedLayer(show: false)
|
||||
super.endTracking(touch, with: event)
|
||||
}
|
||||
|
||||
override func cancelTracking(with event: UIEvent?) {
|
||||
selectedLayer(show: false)
|
||||
super.cancelTracking(with: event)
|
||||
}
|
||||
|
||||
func radius() -> CGFloat {
|
||||
let minSize = min(frame.size.width, frame.size.height)
|
||||
let radius = ((cornerRadius < 0) ? (minSize / 2) : cornerRadius)
|
||||
return radius
|
||||
}
|
||||
|
||||
func selectedLayer(show: Bool) {
|
||||
|
||||
func selectAnimationHide(show: Bool) {
|
||||
if show {
|
||||
setAlpha(.halfTransluent)
|
||||
} else {
|
||||
UIView.animate(with: .ms350) {
|
||||
self.setAlpha(.visible)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func selectAnimationShadow(show: Bool) {
|
||||
let tag = 1
|
||||
let view = viewWithTag(tag)
|
||||
if show {
|
||||
if view == nil {
|
||||
let view = UIView(frame: bounds)
|
||||
view.backgroundColor = UIColor.init(white: 0.0, alpha: 0.2)
|
||||
view.tag = tag
|
||||
view.layer.cornerRadius = radius()
|
||||
addSubview(view)
|
||||
}
|
||||
} else {
|
||||
if let view = view {
|
||||
UIView.animate(with: .ms350, animations: {
|
||||
view.setAlpha(.invisible)
|
||||
}, completion: { (fin: Bool) in
|
||||
view.removeFromSuperview()
|
||||
})
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if hideAnimation {
|
||||
selectAnimationHide(show: show)
|
||||
} else {
|
||||
selectAnimationShadow(show: show)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,716 @@
|
||||
//
|
||||
// DesignTest.swift
|
||||
// FigmaConvertXib
|
||||
//
|
||||
// Created by Рустам Мотыгуллин on 28.07.2020.
|
||||
// Copyright © 2020 mrusta. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
|
||||
@IBDesignable
|
||||
class DesignFigure: UIView {
|
||||
/// Rectange | Ellipse | Polygon | Star
|
||||
@IBInspectable var figureType: Int = 0
|
||||
@IBInspectable var starRadius: CGFloat = 0
|
||||
@IBInspectable var starCount: Int = 5
|
||||
@IBInspectable var cornerRadius: CGFloat = 0.0
|
||||
@IBInspectable var blur: CGFloat = 0.0
|
||||
@IBInspectable var image: UIImage?
|
||||
/// ScaleToFill | Fit | Fill
|
||||
@IBInspectable var imageMode: Int = 1
|
||||
@IBInspectable var fillColor: UIColor?
|
||||
@IBInspectable var grColor1: UIColor?
|
||||
@IBInspectable var grColor2: UIColor?
|
||||
@IBInspectable var grColor3: UIColor?
|
||||
@IBInspectable var grColor4: UIColor?
|
||||
@IBInspectable var grColor5: UIColor?
|
||||
@IBInspectable var grColor6: UIColor?
|
||||
@IBInspectable var grStartPoint: CGPoint = .zero
|
||||
@IBInspectable var grEndPoint: CGPoint = .zero
|
||||
/// Default: Linear
|
||||
@IBInspectable var grRadial: Bool = false
|
||||
@IBInspectable var grDrawsOptions: Bool = true
|
||||
@IBInspectable var grDebug: Bool = false
|
||||
@IBInspectable var grPointPercent: Bool = true
|
||||
@IBInspectable var grBlendMode: Int = 0
|
||||
@IBInspectable var brColor: UIColor = .clear
|
||||
@IBInspectable var brWidth: CGFloat = 0.0
|
||||
@IBInspectable var brDash: Int = 0
|
||||
@IBInspectable var inShColor: UIColor = .clear
|
||||
@IBInspectable var inShRadius: CGFloat = 0.0
|
||||
@IBInspectable var inShOffset: CGSize = .zero
|
||||
@IBInspectable var shColor: UIColor = .clear
|
||||
@IBInspectable var shRadius: CGFloat = 0.0
|
||||
@IBInspectable var shOffset: CGSize = .zero
|
||||
|
||||
func setup() {
|
||||
|
||||
if let foundView = viewWithTag(55) {
|
||||
foundView.removeFromSuperview()
|
||||
}
|
||||
|
||||
let d = DesignFigure_(frame: bounds)
|
||||
d.backgroundColor = .clear
|
||||
d.tag = 55
|
||||
d.figureType = figureType
|
||||
d.starRadius = starRadius
|
||||
d.starCount = starCount
|
||||
d.cornerRadius = cornerRadius
|
||||
d.blur = blur
|
||||
d.image = image
|
||||
d.imageMode = imageMode
|
||||
d.fillColor = fillColor
|
||||
d.grColor1 = grColor1
|
||||
d.grColor2 = grColor2
|
||||
d.grColor3 = grColor3
|
||||
d.grColor4 = grColor4
|
||||
d.grColor5 = grColor5
|
||||
d.grColor6 = grColor6
|
||||
d.grStartPoint = grStartPoint
|
||||
d.grEndPoint = grEndPoint
|
||||
d.grRadial = grRadial
|
||||
d.grDrawsOptions = grDrawsOptions
|
||||
d.grDebug = grDebug
|
||||
d.grPointPercent = grPointPercent
|
||||
d.grBlendMode = grBlendMode
|
||||
d.brWidth = brWidth
|
||||
d.brColor = brColor
|
||||
d.brDash = brDash
|
||||
d.inShColor = inShColor
|
||||
d.inShRadius = inShRadius
|
||||
d.inShOffset = inShOffset
|
||||
d.shColor = shColor
|
||||
d.shRadius = shRadius
|
||||
d.shOffset = shOffset
|
||||
insertSubview(d, at: 0)
|
||||
|
||||
if grDebug {
|
||||
if let foundView = viewWithTag(44) {
|
||||
foundView.removeFromSuperview()
|
||||
}
|
||||
|
||||
let d = DesignFigureDebug(frame: bounds)
|
||||
d.backgroundColor = .clear
|
||||
d.tag = 44
|
||||
d.grStartPoint = grStartPoint
|
||||
d.grEndPoint = grEndPoint
|
||||
d.grPointPercent = grPointPercent
|
||||
|
||||
insertSubview(d, at: 1)
|
||||
}
|
||||
}
|
||||
|
||||
override func layoutSubviews() { setup() }
|
||||
|
||||
}
|
||||
|
||||
@IBDesignable
|
||||
class DesignFigureDebug: UIView {
|
||||
|
||||
@IBInspectable var grStartPoint: CGPoint = .zero
|
||||
@IBInspectable var grEndPoint: CGPoint = .zero
|
||||
@IBInspectable var grPointPercent: Bool = true
|
||||
|
||||
override func draw(_ rect: CGRect) {
|
||||
super.draw(rect)
|
||||
|
||||
addGradientDebugPoints()
|
||||
}
|
||||
|
||||
func addGradientDebugPoints() {
|
||||
|
||||
var start = grStartPoint
|
||||
var end = grStartPoint
|
||||
|
||||
if grPointPercent {
|
||||
end = CGPoint(x: grEndPoint.x * frame.width, y: grEndPoint.y * frame.height)
|
||||
start = CGPoint(x: grStartPoint.x * frame.width, y: grStartPoint.y * frame.height)
|
||||
}
|
||||
|
||||
guard let context = UIGraphicsGetCurrentContext() else { return }
|
||||
|
||||
let radius: CGFloat = 5
|
||||
|
||||
context.setBlendMode(CGBlendMode.normal)
|
||||
context.setLineWidth(3.0)
|
||||
|
||||
|
||||
context.setFillColor(UIColor.black.cgColor)
|
||||
|
||||
context.setStrokeColor(UIColor.systemRed.cgColor)
|
||||
|
||||
let circleRect = CGRect(x: start.x - radius,
|
||||
y: start.y - radius,
|
||||
width: radius * 2,
|
||||
height: radius * 2)
|
||||
|
||||
context.fillEllipse(in: circleRect)
|
||||
context.strokeEllipse(in: circleRect)
|
||||
|
||||
context.setStrokeColor(UIColor.green.cgColor)
|
||||
|
||||
let circleRect2 = CGRect(x: end.x - radius,
|
||||
y: end.y - radius,
|
||||
width: radius * 2,
|
||||
height: radius * 2)
|
||||
|
||||
context.fillEllipse(in: circleRect2)
|
||||
context.strokeEllipse(in: circleRect2)
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
enum DesignFigureType: Int {
|
||||
case rectange = 0
|
||||
case ellipse = 1
|
||||
case polygon = 2
|
||||
case star = 3
|
||||
}
|
||||
|
||||
@IBDesignable
|
||||
class DesignFigure_: UIView {
|
||||
|
||||
@IBInspectable var figureType: Int = 0 /// Rectange Ellipse Polygon Star
|
||||
|
||||
@IBInspectable var starRadius: CGFloat = 0
|
||||
@IBInspectable var starCount: Int = 5
|
||||
//MARK: Radius
|
||||
|
||||
@IBInspectable var cornerRadius: CGFloat = 0.0
|
||||
|
||||
//MARK: Blur
|
||||
|
||||
@IBInspectable var blur: CGFloat = 0.0
|
||||
|
||||
//MARK: Image
|
||||
|
||||
@IBInspectable var image: UIImage?
|
||||
@IBInspectable var imageMode: Int = 1 /// Scale to fill / Aspect fit / fill
|
||||
|
||||
//MARK: Fill
|
||||
|
||||
@IBInspectable var fillColor: UIColor?
|
||||
|
||||
//MARK: Gradient
|
||||
|
||||
@IBInspectable var grColor1: UIColor?
|
||||
@IBInspectable var grColor2: UIColor?
|
||||
@IBInspectable var grColor3: UIColor?
|
||||
@IBInspectable var grColor4: UIColor?
|
||||
@IBInspectable var grColor5: UIColor?
|
||||
@IBInspectable var grColor6: UIColor?
|
||||
|
||||
@IBInspectable var grStartPoint: CGPoint = .zero
|
||||
@IBInspectable var grEndPoint: CGPoint = .zero
|
||||
|
||||
@IBInspectable var grRadial: Bool = false /// default: linear
|
||||
@IBInspectable var grDrawsOptions: Bool = true
|
||||
@IBInspectable var grDebug: Bool = false
|
||||
@IBInspectable var grPointPercent: Bool = true
|
||||
@IBInspectable var grBlendMode: Int = 0
|
||||
|
||||
//MARK: Border
|
||||
|
||||
@IBInspectable var brColor: UIColor = .clear
|
||||
@IBInspectable var brWidth: CGFloat = 0.0
|
||||
@IBInspectable var brDash: Int = 0
|
||||
|
||||
//MARK: Inner Shadow
|
||||
|
||||
@IBInspectable var inShColor: UIColor = .clear
|
||||
@IBInspectable var inShRadius: CGFloat = 0.0
|
||||
@IBInspectable var inShOffset: CGSize = .zero
|
||||
|
||||
//MARK: Shadow
|
||||
|
||||
@IBInspectable var shColor: UIColor = .clear
|
||||
@IBInspectable var shRadius: CGFloat = 0.0
|
||||
@IBInspectable var shOffset: CGSize = .zero
|
||||
|
||||
|
||||
|
||||
//MARK: - Draw
|
||||
|
||||
override func draw(_ rect: CGRect) {
|
||||
super.draw(rect)
|
||||
|
||||
guard let bezier = figurePath(bounds) else { return }
|
||||
|
||||
addFill(bezier: bezier)
|
||||
|
||||
addImage(bezier: bezier)
|
||||
|
||||
addGradient(bezier: bezier)
|
||||
|
||||
addInnerShadow(bezier: bezier)
|
||||
addBorder()
|
||||
addShadow()
|
||||
|
||||
addBlur()
|
||||
}
|
||||
|
||||
//MARK: - Figure Type
|
||||
|
||||
private func figurePath(_ rect: CGRect) -> UIBezierPath? {
|
||||
|
||||
let starR = starRadius / 50
|
||||
if starCount < 1 { starCount = 1 }
|
||||
|
||||
switch figureType {
|
||||
case 1: return UIBezierPath(ovalIn: rect)
|
||||
case 2: return star(polygon: true, rect: rect, radius: starR, pointsOnStar: starCount)
|
||||
case 3: return star(rect: rect, radius: starR, pointsOnStar: starCount)
|
||||
default: return rectangle(rect: rect, radius: cornerRadius)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Border / Stroke
|
||||
|
||||
private func addBorder() {
|
||||
|
||||
if brWidth < 0 { brWidth = 0 }
|
||||
|
||||
guard let context = UIGraphicsGetCurrentContext() else { return }
|
||||
|
||||
// brColor.setStroke()
|
||||
|
||||
guard let bezier = figurePath(bounds) else { return }
|
||||
|
||||
context.saveGState()
|
||||
|
||||
if brDash > 0 {
|
||||
|
||||
UIColor.clear.setFill()
|
||||
bezier.fill()
|
||||
|
||||
brColor.setStroke()
|
||||
bezier.lineWidth = brWidth * 2
|
||||
|
||||
let dashPattern : [CGFloat] = [ CGFloat(brDash), CGFloat(brDash)]
|
||||
bezier.setLineDash(dashPattern, count: 2, phase: 0)
|
||||
bezier.stroke()
|
||||
|
||||
} else {
|
||||
|
||||
brColor.setStroke()
|
||||
|
||||
bezier.lineWidth = brWidth * 2
|
||||
bezier.stroke()
|
||||
}
|
||||
|
||||
|
||||
|
||||
bezier.stroke()
|
||||
bezier.stroke()
|
||||
|
||||
context.restoreGState()
|
||||
context.restoreGState()
|
||||
}
|
||||
|
||||
|
||||
private func addImage(bezier: UIBezierPath) {
|
||||
|
||||
guard let image = image else { return }
|
||||
guard let context = UIGraphicsGetCurrentContext() else { return }
|
||||
|
||||
var f: CGRect = CGRect(x: 0, y: -bounds.height, width: bounds.width, height: bounds.height)
|
||||
|
||||
if imageMode == 1 || imageMode == 2 {
|
||||
|
||||
var w: CGFloat = 0
|
||||
var h: CGFloat = 0
|
||||
|
||||
let percent = bounds.height / image.size.height
|
||||
w = image.size.width * percent
|
||||
h = bounds.height
|
||||
|
||||
if (imageMode == 1) && (w > bounds.width) ||
|
||||
(imageMode == 2) && (bounds.width > w) {
|
||||
|
||||
let percent = bounds.width / image.size.width
|
||||
h = image.size.height * percent
|
||||
w = bounds.width
|
||||
}
|
||||
|
||||
let x = (bounds.width / 2) - (w / 2)
|
||||
let y = (-h + (((bounds.height / 2) - (h / 2)) * (-1)))
|
||||
|
||||
f = CGRect(x: x, y: y, width: w, height: h)
|
||||
}
|
||||
|
||||
context.saveGState()
|
||||
bezier.addClip()
|
||||
context.scaleBy(x: 1, y: -1)
|
||||
context.draw(image.cgImage!, in: f, byTiling: false)
|
||||
context.restoreGState()
|
||||
|
||||
}
|
||||
|
||||
//MARK: - Fill
|
||||
|
||||
private func addFill(bezier: UIBezierPath) {
|
||||
|
||||
guard let fillColor = fillColor else { return }
|
||||
guard let context = UIGraphicsGetCurrentContext() else { return }
|
||||
|
||||
bezier.close()
|
||||
context.saveGState()
|
||||
fillColor.setFill()
|
||||
bezier.fill()
|
||||
context.restoreGState()
|
||||
}
|
||||
|
||||
//MARK: - Gradient
|
||||
|
||||
private func addGradient(bezier: UIBezierPath) {
|
||||
|
||||
guard let context = UIGraphicsGetCurrentContext() else { return }
|
||||
|
||||
var colors: [CGColor] = []
|
||||
|
||||
if let c = grColor1 { colors.append(c.cgColor) }
|
||||
if let c = grColor2 { colors.append(c.cgColor) }
|
||||
if let c = grColor3 { colors.append(c.cgColor) }
|
||||
if let c = grColor4 { colors.append(c.cgColor) }
|
||||
if let c = grColor5 { colors.append(c.cgColor) }
|
||||
if let c = grColor6 { colors.append(c.cgColor) }
|
||||
|
||||
if colors.count < 2 {
|
||||
colors.append(UIColor.clear.cgColor)
|
||||
colors.append(UIColor.clear.cgColor)
|
||||
} else if colors.count < 1 {
|
||||
if grColor1 != nil {
|
||||
colors.append(UIColor.clear.cgColor)
|
||||
} else {
|
||||
colors.insert(UIColor.clear.cgColor, at: 0)
|
||||
}
|
||||
}
|
||||
|
||||
guard let gradient = CGGradient(colorsSpace: nil,
|
||||
colors: colors as CFArray,
|
||||
locations: nil) else { return }
|
||||
|
||||
let bMode = CGBlendMode(rawValue: CGBlendMode.RawValue(grBlendMode)) ?? CGBlendMode.sourceAtop
|
||||
context.setBlendMode(bMode)
|
||||
|
||||
let options: CGGradientDrawingOptions =
|
||||
grDrawsOptions ? [ .drawsBeforeStartLocation, .drawsAfterEndLocation ] : [ ]
|
||||
|
||||
var grEndPointR: CGPoint = grEndPoint
|
||||
var grStartPointR: CGPoint = grStartPoint
|
||||
if grPointPercent {
|
||||
grEndPointR = CGPoint(x: (grEndPoint.x * frame.width), y: (grEndPoint.y * frame.height))
|
||||
grStartPointR = CGPoint(x: (grStartPoint.x * frame.width), y: (grStartPoint.y * frame.height))
|
||||
}
|
||||
|
||||
bezier.close()
|
||||
context.saveGState()
|
||||
bezier.addClip()
|
||||
|
||||
if grRadial {
|
||||
|
||||
let x: CGFloat = (grEndPointR.x - grStartPointR.x)
|
||||
let y: CGFloat = (grEndPointR.y - grStartPointR.y)
|
||||
let distance: CGFloat = sqrt((x * x) + (y * y))
|
||||
|
||||
context.drawRadialGradient(gradient,
|
||||
startCenter: grStartPointR,
|
||||
startRadius: 0,
|
||||
endCenter: grStartPointR,
|
||||
endRadius: distance,
|
||||
options: options)
|
||||
} else {
|
||||
|
||||
context.drawLinearGradient(gradient,
|
||||
start: grStartPointR,
|
||||
end: grEndPointR,
|
||||
options: options)
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - Shadow
|
||||
|
||||
private func addShadow() {
|
||||
|
||||
if shRadius < 0 { shRadius = 0 }
|
||||
|
||||
layer.shadowOffset = shOffset
|
||||
layer.shadowOpacity = 1.0
|
||||
layer.shadowRadius = shRadius
|
||||
layer.shadowColor = shColor.cgColor
|
||||
}
|
||||
|
||||
private func addShadowContext(bezier: UIBezierPath) {
|
||||
|
||||
guard let context = UIGraphicsGetCurrentContext() else { return }
|
||||
|
||||
bezier.close()
|
||||
|
||||
//// Shadow Declarations
|
||||
let shadow = NSShadow()
|
||||
shadow.shadowColor = shColor
|
||||
shadow.shadowOffset = shOffset
|
||||
shadow.shadowBlurRadius = shRadius * 2
|
||||
|
||||
context.saveGState()
|
||||
|
||||
context.setShadow(offset: shadow.shadowOffset,
|
||||
blur: shadow.shadowBlurRadius,
|
||||
color: (shadow.shadowColor as! UIColor).cgColor)
|
||||
|
||||
context.restoreGState()
|
||||
}
|
||||
|
||||
//MARK: - Inner Shadow
|
||||
|
||||
private func addInnerShadow(bezier: UIBezierPath) {
|
||||
|
||||
if inShRadius < 0 { inShRadius = 0 }
|
||||
|
||||
let innerShadow = NSShadow()
|
||||
innerShadow.shadowColor = inShColor
|
||||
innerShadow.shadowOffset = inShOffset
|
||||
innerShadow.shadowBlurRadius = inShRadius * 2
|
||||
|
||||
guard let context = UIGraphicsGetCurrentContext() else { return }
|
||||
|
||||
////// Star Inner Shadow
|
||||
context.saveGState()
|
||||
context.clip(to: bezier.bounds)
|
||||
context.setShadow(offset: .zero, blur: 0)
|
||||
context.setAlpha((innerShadow.shadowColor as! UIColor).cgColor.alpha)
|
||||
context.beginTransparencyLayer(auxiliaryInfo: nil)
|
||||
|
||||
let starOpaqueShadow = (innerShadow.shadowColor as! UIColor).withAlphaComponent(1)
|
||||
context.setShadow(offset: innerShadow.shadowOffset,
|
||||
blur: innerShadow.shadowBlurRadius,
|
||||
color: starOpaqueShadow.cgColor)
|
||||
|
||||
context.setBlendMode(.sourceOut)
|
||||
context.beginTransparencyLayer(auxiliaryInfo: nil)
|
||||
|
||||
starOpaqueShadow.setFill()
|
||||
|
||||
bezier.fill()
|
||||
|
||||
context.endTransparencyLayer()
|
||||
context.endTransparencyLayer()
|
||||
context.restoreGState()
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Blur
|
||||
|
||||
private func addBlur() {
|
||||
|
||||
guard blur > 0 else { return }
|
||||
|
||||
guard let image = screenShotContext() else { return }
|
||||
|
||||
self.addblur(screen: image, blur: blur)
|
||||
}
|
||||
|
||||
// MARK: Screen Shot Context
|
||||
|
||||
private func screenShotContext() -> UIImage? {
|
||||
|
||||
guard let context = UIGraphicsGetCurrentContext() else { return nil }
|
||||
guard let cgimage: CGImage = context.makeImage() else { return nil }
|
||||
let image = UIImage(cgImage: cgimage)
|
||||
|
||||
return image
|
||||
}
|
||||
|
||||
// MARK: Clear Context
|
||||
|
||||
private func clearContext() {
|
||||
|
||||
let f = CGRect(x: 0, y: 0,
|
||||
width: frame.width * 2,
|
||||
height: frame.height * 2)
|
||||
|
||||
let context = UIGraphicsGetCurrentContext()
|
||||
context?.clear(f)
|
||||
}
|
||||
|
||||
|
||||
private func addblur(screen: UIImage, blur: CGFloat) {
|
||||
|
||||
clearContext()
|
||||
|
||||
func blurImage(image: UIImage) -> UIImage? {
|
||||
|
||||
guard let ciscreen: CIImage = CIImage(image: screen) else { return nil }
|
||||
|
||||
guard let filter: CIFilter = CIFilter(name: "CIGaussianBlur") else { return nil }
|
||||
filter.setDefaults()
|
||||
filter.setValue(ciscreen, forKey: kCIInputImageKey)
|
||||
filter.setValue(blur, forKey: kCIInputRadiusKey)
|
||||
|
||||
|
||||
let bl2 = ((blur * 3) * -1)
|
||||
let bl4 = (blur * 6)
|
||||
|
||||
let contextFrame = CGRect(
|
||||
x: bl2,
|
||||
y: bl2,
|
||||
width: bl4 + image.size.width,
|
||||
height: bl4 + image.size.height
|
||||
)
|
||||
|
||||
// let bl2 = ((blur * 2) * -1)
|
||||
// let bl4 = (blur * 4)
|
||||
// let contextFrame2 = CGRect(x: bl2,
|
||||
// y: bl2,
|
||||
// width: bl4 + (frame.width * 2),
|
||||
// height: bl4 + (frame.height * 2))
|
||||
|
||||
let ciContext = CIContext(options: nil)
|
||||
guard let ci: CIImage = filter.value(forKey: kCIOutputImageKey) as? CIImage else { return nil }
|
||||
guard let cImg = ciContext.createCGImage(ci, from: contextFrame) else { return nil }
|
||||
|
||||
let finalImage = UIImage(cgImage: cImg)
|
||||
|
||||
return finalImage
|
||||
}
|
||||
|
||||
let p2: CGFloat = 1
|
||||
let p4: CGFloat = p2 * 2
|
||||
|
||||
let bl2 = ((blur * p2) * -1)
|
||||
let bl4 = (blur * p4)
|
||||
|
||||
let x: CGFloat = 0
|
||||
let ofW = (x * 1) * -1
|
||||
let ofH = (x * 1) * -1
|
||||
|
||||
let ofW2 = (x * 2)
|
||||
let ofH2 = (x * 2)
|
||||
|
||||
let contextFrame = CGRect(x: bl2 + ofW,
|
||||
y: bl2 + ofH,
|
||||
width: bl4 + ofW2 + frame.width,
|
||||
height: bl4 + ofH2 + frame.height)
|
||||
|
||||
let blurImageView = UIImageView(frame: contextFrame)
|
||||
blurImageView.image = blurImage(image: screen)
|
||||
blurImageView.contentMode = .scaleAspectFill
|
||||
|
||||
|
||||
addSubview(blurImageView)
|
||||
}
|
||||
|
||||
//MARK: - Rectangle Bezier
|
||||
|
||||
func rectangle(rect: CGRect, radius: CGFloat) -> UIBezierPath {
|
||||
|
||||
let r = self.cornerRadius(radius)
|
||||
let w = rect.width
|
||||
let h = rect.height
|
||||
|
||||
let path = UIBezierPath()
|
||||
path.move(to: CGPoint(x: r, y: 0.0))
|
||||
|
||||
path.addLine(to: CGPoint(x: w - r, y: 0.0))
|
||||
path.addArc(withCenter: CGPoint(x: w - r, y: r),
|
||||
radius: r,
|
||||
startAngle: 3.0 * .pi / 2.0,
|
||||
endAngle: 2 * .pi,
|
||||
clockwise: true)
|
||||
|
||||
path.addLine(to: CGPoint(x: w, y: h - r))
|
||||
path.addArc(withCenter: CGPoint(x: w - r, y: h - r),
|
||||
radius: r,
|
||||
startAngle: 0.0,
|
||||
endAngle: .pi / 2.0,
|
||||
clockwise: true)
|
||||
|
||||
path.addLine(to: CGPoint(x: r, y: h))
|
||||
path.addArc(withCenter: CGPoint(x: r, y: h - r),
|
||||
radius: r,
|
||||
startAngle: .pi / 2.0,
|
||||
endAngle: .pi,
|
||||
clockwise: true)
|
||||
|
||||
path.addLine(to: CGPoint(x: 0.0, y: r))
|
||||
path.addArc(withCenter: CGPoint(x: r, y: r),
|
||||
radius: r,
|
||||
startAngle: .pi,
|
||||
endAngle: 3.0 * .pi / 2.0,
|
||||
clockwise: true)
|
||||
|
||||
path.close()
|
||||
return path
|
||||
}
|
||||
|
||||
//MARK: - Star Bezier
|
||||
|
||||
func star(polygon: Bool = false, rect: CGRect, radius: CGFloat, pointsOnStar: Int) -> UIBezierPath {
|
||||
|
||||
let path = UIBezierPath()
|
||||
|
||||
let minSize = max(rect.width, rect.height)
|
||||
var starExtrusion: CGFloat = minSize * 0.18695652173
|
||||
if radius != 0 {
|
||||
starExtrusion = minSize * radius
|
||||
}
|
||||
|
||||
let center: CGPoint = .zero
|
||||
|
||||
var angle: CGFloat = -CGFloat(.pi / 2.0)
|
||||
let angleIncrement = CGFloat(.pi * 2.0 / Double(pointsOnStar))
|
||||
let radius = minSize / 2.0
|
||||
|
||||
var firstPoint = true
|
||||
|
||||
func pointFrom(angle: CGFloat, radius: CGFloat, offset: CGPoint) -> CGPoint {
|
||||
return CGPoint(x: radius * cos(angle) + offset.x,
|
||||
y: radius * sin(angle) + offset.y)
|
||||
}
|
||||
|
||||
for _ in 1...pointsOnStar {
|
||||
|
||||
let point: CGPoint = pointFrom(angle: angle, radius: radius, offset: center)
|
||||
let nextPoint: CGPoint = pointFrom(angle: angle + angleIncrement, radius: radius, offset: center)
|
||||
let midPoint: CGPoint = pointFrom(angle: angle + angleIncrement / 2.0, radius: starExtrusion, offset: center)
|
||||
|
||||
var perY: CGFloat = 1.0
|
||||
var perX: CGFloat = 1.0
|
||||
|
||||
if rect.height < rect.width {
|
||||
perY = (rect.height / minSize)
|
||||
} else {
|
||||
perX = (rect.width / minSize)
|
||||
}
|
||||
|
||||
let p = CGPoint(x: (point.x * perX) + (rect.width / 2.0),
|
||||
y: (point.y * perY) + (rect.height / 2.0))
|
||||
|
||||
let n = CGPoint(x: (nextPoint.x * perX) + (rect.width / 2.0),
|
||||
y: (nextPoint.y * perY) + (rect.height / 2.0))
|
||||
|
||||
let m = CGPoint(x: (midPoint.x * perX) + (rect.width / 2.0),
|
||||
y: (midPoint.y * perY) + (rect.height / 2.0))
|
||||
|
||||
if firstPoint {
|
||||
firstPoint = false
|
||||
path.move(to: p)
|
||||
}
|
||||
|
||||
if !polygon {
|
||||
path.addLine(to: m)
|
||||
}
|
||||
path.addLine(to: n)
|
||||
|
||||
angle += angleIncrement
|
||||
}
|
||||
|
||||
path.close()
|
||||
|
||||
return path
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,716 @@
|
||||
//
|
||||
// DesignTest.swift
|
||||
// FigmaConvertXib
|
||||
//
|
||||
// Created by Рустам Мотыгуллин on 28.07.2020.
|
||||
// Copyright © 2020 mrusta. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
|
||||
@IBDesignable
|
||||
class DesignFigure: UIView {
|
||||
/// Rectange | Ellipse | Polygon | Star
|
||||
@IBInspectable var figureType: Int = 0
|
||||
@IBInspectable var starRadius: CGFloat = 0
|
||||
@IBInspectable var starCount: Int = 5
|
||||
@IBInspectable var cornerRadius: CGFloat = 0.0
|
||||
@IBInspectable var blur: CGFloat = 0.0
|
||||
@IBInspectable var image: UIImage?
|
||||
/// ScaleToFill | Fit | Fill
|
||||
@IBInspectable var imageMode: Int = 1
|
||||
@IBInspectable var fillColor: UIColor?
|
||||
@IBInspectable var grColor1: UIColor?
|
||||
@IBInspectable var grColor2: UIColor?
|
||||
@IBInspectable var grColor3: UIColor?
|
||||
@IBInspectable var grColor4: UIColor?
|
||||
@IBInspectable var grColor5: UIColor?
|
||||
@IBInspectable var grColor6: UIColor?
|
||||
@IBInspectable var grStartPoint: CGPoint = .zero
|
||||
@IBInspectable var grEndPoint: CGPoint = .zero
|
||||
/// Default: Linear
|
||||
@IBInspectable var grRadial: Bool = false
|
||||
@IBInspectable var grDrawsOptions: Bool = true
|
||||
@IBInspectable var grDebug: Bool = false
|
||||
@IBInspectable var grPointPercent: Bool = true
|
||||
@IBInspectable var grBlendMode: Int = 0
|
||||
@IBInspectable var brColor: UIColor = .clear
|
||||
@IBInspectable var brWidth: CGFloat = 0.0
|
||||
@IBInspectable var brDash: Int = 0
|
||||
@IBInspectable var inShColor: UIColor = .clear
|
||||
@IBInspectable var inShRadius: CGFloat = 0.0
|
||||
@IBInspectable var inShOffset: CGSize = .zero
|
||||
@IBInspectable var shColor: UIColor = .clear
|
||||
@IBInspectable var shRadius: CGFloat = 0.0
|
||||
@IBInspectable var shOffset: CGSize = .zero
|
||||
|
||||
func setup() {
|
||||
|
||||
if let foundView = viewWithTag(55) {
|
||||
foundView.removeFromSuperview()
|
||||
}
|
||||
|
||||
let d = DesignFigure_(frame: bounds)
|
||||
d.backgroundColor = .clear
|
||||
d.tag = 55
|
||||
d.figureType = figureType
|
||||
d.starRadius = starRadius
|
||||
d.starCount = starCount
|
||||
d.cornerRadius = cornerRadius
|
||||
d.blur = blur
|
||||
d.image = image
|
||||
d.imageMode = imageMode
|
||||
d.fillColor = fillColor
|
||||
d.grColor1 = grColor1
|
||||
d.grColor2 = grColor2
|
||||
d.grColor3 = grColor3
|
||||
d.grColor4 = grColor4
|
||||
d.grColor5 = grColor5
|
||||
d.grColor6 = grColor6
|
||||
d.grStartPoint = grStartPoint
|
||||
d.grEndPoint = grEndPoint
|
||||
d.grRadial = grRadial
|
||||
d.grDrawsOptions = grDrawsOptions
|
||||
d.grDebug = grDebug
|
||||
d.grPointPercent = grPointPercent
|
||||
d.grBlendMode = grBlendMode
|
||||
d.brWidth = brWidth
|
||||
d.brColor = brColor
|
||||
d.brDash = brDash
|
||||
d.inShColor = inShColor
|
||||
d.inShRadius = inShRadius
|
||||
d.inShOffset = inShOffset
|
||||
d.shColor = shColor
|
||||
d.shRadius = shRadius
|
||||
d.shOffset = shOffset
|
||||
insertSubview(d, at: 0)
|
||||
|
||||
if grDebug {
|
||||
if let foundView = viewWithTag(44) {
|
||||
foundView.removeFromSuperview()
|
||||
}
|
||||
|
||||
let d = DesignFigureDebug(frame: bounds)
|
||||
d.backgroundColor = .clear
|
||||
d.tag = 44
|
||||
d.grStartPoint = grStartPoint
|
||||
d.grEndPoint = grEndPoint
|
||||
d.grPointPercent = grPointPercent
|
||||
|
||||
insertSubview(d, at: 1)
|
||||
}
|
||||
}
|
||||
|
||||
override func layoutSubviews() { setup() }
|
||||
|
||||
}
|
||||
|
||||
@IBDesignable
|
||||
class DesignFigureDebug: UIView {
|
||||
|
||||
@IBInspectable var grStartPoint: CGPoint = .zero
|
||||
@IBInspectable var grEndPoint: CGPoint = .zero
|
||||
@IBInspectable var grPointPercent: Bool = true
|
||||
|
||||
override func draw(_ rect: CGRect) {
|
||||
super.draw(rect)
|
||||
|
||||
addGradientDebugPoints()
|
||||
}
|
||||
|
||||
func addGradientDebugPoints() {
|
||||
|
||||
var start = grStartPoint
|
||||
var end = grStartPoint
|
||||
|
||||
if grPointPercent {
|
||||
end = CGPoint(x: grEndPoint.x * frame.width, y: grEndPoint.y * frame.height)
|
||||
start = CGPoint(x: grStartPoint.x * frame.width, y: grStartPoint.y * frame.height)
|
||||
}
|
||||
|
||||
guard let context = UIGraphicsGetCurrentContext() else { return }
|
||||
|
||||
let radius: CGFloat = 5
|
||||
|
||||
context.setBlendMode(CGBlendMode.normal)
|
||||
context.setLineWidth(3.0)
|
||||
|
||||
|
||||
context.setFillColor(UIColor.black.cgColor)
|
||||
|
||||
context.setStrokeColor(UIColor.systemRed.cgColor)
|
||||
|
||||
let circleRect = CGRect(x: start.x - radius,
|
||||
y: start.y - radius,
|
||||
width: radius * 2,
|
||||
height: radius * 2)
|
||||
|
||||
context.fillEllipse(in: circleRect)
|
||||
context.strokeEllipse(in: circleRect)
|
||||
|
||||
context.setStrokeColor(UIColor.green.cgColor)
|
||||
|
||||
let circleRect2 = CGRect(x: end.x - radius,
|
||||
y: end.y - radius,
|
||||
width: radius * 2,
|
||||
height: radius * 2)
|
||||
|
||||
context.fillEllipse(in: circleRect2)
|
||||
context.strokeEllipse(in: circleRect2)
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
enum DesignFigureType: Int {
|
||||
case rectange = 0
|
||||
case ellipse = 1
|
||||
case polygon = 2
|
||||
case star = 3
|
||||
}
|
||||
|
||||
@IBDesignable
|
||||
class DesignFigure_: UIView {
|
||||
|
||||
@IBInspectable var figureType: Int = 0 /// Rectange Ellipse Polygon Star
|
||||
|
||||
@IBInspectable var starRadius: CGFloat = 0
|
||||
@IBInspectable var starCount: Int = 5
|
||||
//MARK: Radius
|
||||
|
||||
@IBInspectable var cornerRadius: CGFloat = 0.0
|
||||
|
||||
//MARK: Blur
|
||||
|
||||
@IBInspectable var blur: CGFloat = 0.0
|
||||
|
||||
//MARK: Image
|
||||
|
||||
@IBInspectable var image: UIImage?
|
||||
@IBInspectable var imageMode: Int = 1 /// Scale to fill / Aspect fit / fill
|
||||
|
||||
//MARK: Fill
|
||||
|
||||
@IBInspectable var fillColor: UIColor?
|
||||
|
||||
//MARK: Gradient
|
||||
|
||||
@IBInspectable var grColor1: UIColor?
|
||||
@IBInspectable var grColor2: UIColor?
|
||||
@IBInspectable var grColor3: UIColor?
|
||||
@IBInspectable var grColor4: UIColor?
|
||||
@IBInspectable var grColor5: UIColor?
|
||||
@IBInspectable var grColor6: UIColor?
|
||||
|
||||
@IBInspectable var grStartPoint: CGPoint = .zero
|
||||
@IBInspectable var grEndPoint: CGPoint = .zero
|
||||
|
||||
@IBInspectable var grRadial: Bool = false /// default: linear
|
||||
@IBInspectable var grDrawsOptions: Bool = true
|
||||
@IBInspectable var grDebug: Bool = false
|
||||
@IBInspectable var grPointPercent: Bool = true
|
||||
@IBInspectable var grBlendMode: Int = 0
|
||||
|
||||
//MARK: Border
|
||||
|
||||
@IBInspectable var brColor: UIColor = .clear
|
||||
@IBInspectable var brWidth: CGFloat = 0.0
|
||||
@IBInspectable var brDash: Int = 0
|
||||
|
||||
//MARK: Inner Shadow
|
||||
|
||||
@IBInspectable var inShColor: UIColor = .clear
|
||||
@IBInspectable var inShRadius: CGFloat = 0.0
|
||||
@IBInspectable var inShOffset: CGSize = .zero
|
||||
|
||||
//MARK: Shadow
|
||||
|
||||
@IBInspectable var shColor: UIColor = .clear
|
||||
@IBInspectable var shRadius: CGFloat = 0.0
|
||||
@IBInspectable var shOffset: CGSize = .zero
|
||||
|
||||
|
||||
|
||||
//MARK: - Draw
|
||||
|
||||
override func draw(_ rect: CGRect) {
|
||||
super.draw(rect)
|
||||
|
||||
guard let bezier = figurePath(bounds) else { return }
|
||||
|
||||
addFill(bezier: bezier)
|
||||
|
||||
addImage(bezier: bezier)
|
||||
|
||||
addGradient(bezier: bezier)
|
||||
|
||||
addInnerShadow(bezier: bezier)
|
||||
addBorder()
|
||||
addShadow()
|
||||
|
||||
addBlur()
|
||||
}
|
||||
|
||||
//MARK: - Figure Type
|
||||
|
||||
private func figurePath(_ rect: CGRect) -> UIBezierPath? {
|
||||
|
||||
let starR = starRadius / 50
|
||||
if starCount < 1 { starCount = 1 }
|
||||
|
||||
switch figureType {
|
||||
case 1: return UIBezierPath(ovalIn: rect)
|
||||
case 2: return star(polygon: true, rect: rect, radius: starR, pointsOnStar: starCount)
|
||||
case 3: return star(rect: rect, radius: starR, pointsOnStar: starCount)
|
||||
default: return rectangle(rect: rect, radius: cornerRadius)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Border / Stroke
|
||||
|
||||
private func addBorder() {
|
||||
|
||||
if brWidth < 0 { brWidth = 0 }
|
||||
|
||||
guard let context = UIGraphicsGetCurrentContext() else { return }
|
||||
|
||||
// brColor.setStroke()
|
||||
|
||||
guard let bezier = figurePath(bounds) else { return }
|
||||
|
||||
context.saveGState()
|
||||
|
||||
if brDash > 0 {
|
||||
|
||||
UIColor.clear.setFill()
|
||||
bezier.fill()
|
||||
|
||||
brColor.setStroke()
|
||||
bezier.lineWidth = brWidth * 2
|
||||
|
||||
let dashPattern : [CGFloat] = [ CGFloat(brDash), CGFloat(brDash)]
|
||||
bezier.setLineDash(dashPattern, count: 2, phase: 0)
|
||||
bezier.stroke()
|
||||
|
||||
} else {
|
||||
|
||||
brColor.setStroke()
|
||||
|
||||
bezier.lineWidth = brWidth * 2
|
||||
bezier.stroke()
|
||||
}
|
||||
|
||||
|
||||
|
||||
bezier.stroke()
|
||||
bezier.stroke()
|
||||
|
||||
context.restoreGState()
|
||||
context.restoreGState()
|
||||
}
|
||||
|
||||
|
||||
private func addImage(bezier: UIBezierPath) {
|
||||
|
||||
guard let image = image else { return }
|
||||
guard let context = UIGraphicsGetCurrentContext() else { return }
|
||||
|
||||
var f: CGRect = CGRect(x: 0, y: -bounds.height, width: bounds.width, height: bounds.height)
|
||||
|
||||
if imageMode == 1 || imageMode == 2 {
|
||||
|
||||
var w: CGFloat = 0
|
||||
var h: CGFloat = 0
|
||||
|
||||
let percent = bounds.height / image.size.height
|
||||
w = image.size.width * percent
|
||||
h = bounds.height
|
||||
|
||||
if (imageMode == 1) && (w > bounds.width) ||
|
||||
(imageMode == 2) && (bounds.width > w) {
|
||||
|
||||
let percent = bounds.width / image.size.width
|
||||
h = image.size.height * percent
|
||||
w = bounds.width
|
||||
}
|
||||
|
||||
let x = (bounds.width / 2) - (w / 2)
|
||||
let y = (-h + (((bounds.height / 2) - (h / 2)) * (-1)))
|
||||
|
||||
f = CGRect(x: x, y: y, width: w, height: h)
|
||||
}
|
||||
|
||||
context.saveGState()
|
||||
bezier.addClip()
|
||||
context.scaleBy(x: 1, y: -1)
|
||||
context.draw(image.cgImage!, in: f, byTiling: false)
|
||||
context.restoreGState()
|
||||
|
||||
}
|
||||
|
||||
//MARK: - Fill
|
||||
|
||||
private func addFill(bezier: UIBezierPath) {
|
||||
|
||||
guard let fillColor = fillColor else { return }
|
||||
guard let context = UIGraphicsGetCurrentContext() else { return }
|
||||
|
||||
bezier.close()
|
||||
context.saveGState()
|
||||
fillColor.setFill()
|
||||
bezier.fill()
|
||||
context.restoreGState()
|
||||
}
|
||||
|
||||
//MARK: - Gradient
|
||||
|
||||
private func addGradient(bezier: UIBezierPath) {
|
||||
|
||||
guard let context = UIGraphicsGetCurrentContext() else { return }
|
||||
|
||||
var colors: [CGColor] = []
|
||||
|
||||
if let c = grColor1 { colors.append(c.cgColor) }
|
||||
if let c = grColor2 { colors.append(c.cgColor) }
|
||||
if let c = grColor3 { colors.append(c.cgColor) }
|
||||
if let c = grColor4 { colors.append(c.cgColor) }
|
||||
if let c = grColor5 { colors.append(c.cgColor) }
|
||||
if let c = grColor6 { colors.append(c.cgColor) }
|
||||
|
||||
if colors.count < 2 {
|
||||
colors.append(UIColor.clear.cgColor)
|
||||
colors.append(UIColor.clear.cgColor)
|
||||
} else if colors.count < 1 {
|
||||
if grColor1 != nil {
|
||||
colors.append(UIColor.clear.cgColor)
|
||||
} else {
|
||||
colors.insert(UIColor.clear.cgColor, at: 0)
|
||||
}
|
||||
}
|
||||
|
||||
guard let gradient = CGGradient(colorsSpace: nil,
|
||||
colors: colors as CFArray,
|
||||
locations: nil) else { return }
|
||||
|
||||
let bMode = CGBlendMode(rawValue: CGBlendMode.RawValue(grBlendMode)) ?? CGBlendMode.sourceAtop
|
||||
context.setBlendMode(bMode)
|
||||
|
||||
let options: CGGradientDrawingOptions =
|
||||
grDrawsOptions ? [ .drawsBeforeStartLocation, .drawsAfterEndLocation ] : [ ]
|
||||
|
||||
var grEndPointR: CGPoint = grEndPoint
|
||||
var grStartPointR: CGPoint = grStartPoint
|
||||
if grPointPercent {
|
||||
grEndPointR = CGPoint(x: (grEndPoint.x * frame.width), y: (grEndPoint.y * frame.height))
|
||||
grStartPointR = CGPoint(x: (grStartPoint.x * frame.width), y: (grStartPoint.y * frame.height))
|
||||
}
|
||||
|
||||
bezier.close()
|
||||
context.saveGState()
|
||||
bezier.addClip()
|
||||
|
||||
if grRadial {
|
||||
|
||||
let x: CGFloat = (grEndPointR.x - grStartPointR.x)
|
||||
let y: CGFloat = (grEndPointR.y - grStartPointR.y)
|
||||
let distance: CGFloat = sqrt((x * x) + (y * y))
|
||||
|
||||
context.drawRadialGradient(gradient,
|
||||
startCenter: grStartPointR,
|
||||
startRadius: 0,
|
||||
endCenter: grStartPointR,
|
||||
endRadius: distance,
|
||||
options: options)
|
||||
} else {
|
||||
|
||||
context.drawLinearGradient(gradient,
|
||||
start: grStartPointR,
|
||||
end: grEndPointR,
|
||||
options: options)
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - Shadow
|
||||
|
||||
private func addShadow() {
|
||||
|
||||
if shRadius < 0 { shRadius = 0 }
|
||||
|
||||
layer.shadowOffset = shOffset
|
||||
layer.shadowOpacity = 1.0
|
||||
layer.shadowRadius = shRadius
|
||||
layer.shadowColor = shColor.cgColor
|
||||
}
|
||||
|
||||
private func addShadowContext(bezier: UIBezierPath) {
|
||||
|
||||
guard let context = UIGraphicsGetCurrentContext() else { return }
|
||||
|
||||
bezier.close()
|
||||
|
||||
//// Shadow Declarations
|
||||
let shadow = NSShadow()
|
||||
shadow.shadowColor = shColor
|
||||
shadow.shadowOffset = shOffset
|
||||
shadow.shadowBlurRadius = shRadius * 2
|
||||
|
||||
context.saveGState()
|
||||
|
||||
context.setShadow(offset: shadow.shadowOffset,
|
||||
blur: shadow.shadowBlurRadius,
|
||||
color: (shadow.shadowColor as! UIColor).cgColor)
|
||||
|
||||
context.restoreGState()
|
||||
}
|
||||
|
||||
//MARK: - Inner Shadow
|
||||
|
||||
private func addInnerShadow(bezier: UIBezierPath) {
|
||||
|
||||
if inShRadius < 0 { inShRadius = 0 }
|
||||
|
||||
let innerShadow = NSShadow()
|
||||
innerShadow.shadowColor = inShColor
|
||||
innerShadow.shadowOffset = inShOffset
|
||||
innerShadow.shadowBlurRadius = inShRadius * 2
|
||||
|
||||
guard let context = UIGraphicsGetCurrentContext() else { return }
|
||||
|
||||
////// Star Inner Shadow
|
||||
context.saveGState()
|
||||
context.clip(to: bezier.bounds)
|
||||
context.setShadow(offset: .zero, blur: 0)
|
||||
context.setAlpha((innerShadow.shadowColor as! UIColor).cgColor.alpha)
|
||||
context.beginTransparencyLayer(auxiliaryInfo: nil)
|
||||
|
||||
let starOpaqueShadow = (innerShadow.shadowColor as! UIColor).withAlphaComponent(1)
|
||||
context.setShadow(offset: innerShadow.shadowOffset,
|
||||
blur: innerShadow.shadowBlurRadius,
|
||||
color: starOpaqueShadow.cgColor)
|
||||
|
||||
context.setBlendMode(.sourceOut)
|
||||
context.beginTransparencyLayer(auxiliaryInfo: nil)
|
||||
|
||||
starOpaqueShadow.setFill()
|
||||
|
||||
bezier.fill()
|
||||
|
||||
context.endTransparencyLayer()
|
||||
context.endTransparencyLayer()
|
||||
context.restoreGState()
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Blur
|
||||
|
||||
private func addBlur() {
|
||||
|
||||
guard blur > 0 else { return }
|
||||
|
||||
guard let image = screenShotContext() else { return }
|
||||
|
||||
self.addblur(screen: image, blur: blur)
|
||||
}
|
||||
|
||||
// MARK: Screen Shot Context
|
||||
|
||||
private func screenShotContext() -> UIImage? {
|
||||
|
||||
guard let context = UIGraphicsGetCurrentContext() else { return nil }
|
||||
guard let cgimage: CGImage = context.makeImage() else { return nil }
|
||||
let image = UIImage(cgImage: cgimage)
|
||||
|
||||
return image
|
||||
}
|
||||
|
||||
// MARK: Clear Context
|
||||
|
||||
private func clearContext() {
|
||||
|
||||
let f = CGRect(x: 0, y: 0,
|
||||
width: frame.width * 2,
|
||||
height: frame.height * 2)
|
||||
|
||||
let context = UIGraphicsGetCurrentContext()
|
||||
context?.clear(f)
|
||||
}
|
||||
|
||||
|
||||
private func addblur(screen: UIImage, blur: CGFloat) {
|
||||
|
||||
clearContext()
|
||||
|
||||
func blurImage(image: UIImage) -> UIImage? {
|
||||
|
||||
guard let ciscreen: CIImage = CIImage(image: screen) else { return nil }
|
||||
|
||||
guard let filter: CIFilter = CIFilter(name: "CIGaussianBlur") else { return nil }
|
||||
filter.setDefaults()
|
||||
filter.setValue(ciscreen, forKey: kCIInputImageKey)
|
||||
filter.setValue(blur, forKey: kCIInputRadiusKey)
|
||||
|
||||
|
||||
let bl2 = ((blur * 3) * -1)
|
||||
let bl4 = (blur * 6)
|
||||
|
||||
let contextFrame = CGRect(
|
||||
x: bl2,
|
||||
y: bl2,
|
||||
width: bl4 + image.size.width,
|
||||
height: bl4 + image.size.height
|
||||
)
|
||||
|
||||
// let bl2 = ((blur * 2) * -1)
|
||||
// let bl4 = (blur * 4)
|
||||
// let contextFrame2 = CGRect(x: bl2,
|
||||
// y: bl2,
|
||||
// width: bl4 + (frame.width * 2),
|
||||
// height: bl4 + (frame.height * 2))
|
||||
|
||||
let ciContext = CIContext(options: nil)
|
||||
guard let ci: CIImage = filter.value(forKey: kCIOutputImageKey) as? CIImage else { return nil }
|
||||
guard let cImg = ciContext.createCGImage(ci, from: contextFrame) else { return nil }
|
||||
|
||||
let finalImage = UIImage(cgImage: cImg)
|
||||
|
||||
return finalImage
|
||||
}
|
||||
|
||||
let p2: CGFloat = 1
|
||||
let p4: CGFloat = p2 * 2
|
||||
|
||||
let bl2 = ((blur * p2) * -1)
|
||||
let bl4 = (blur * p4)
|
||||
|
||||
let x: CGFloat = 0
|
||||
let ofW = (x * 1) * -1
|
||||
let ofH = (x * 1) * -1
|
||||
|
||||
let ofW2 = (x * 2)
|
||||
let ofH2 = (x * 2)
|
||||
|
||||
let contextFrame = CGRect(x: bl2 + ofW,
|
||||
y: bl2 + ofH,
|
||||
width: bl4 + ofW2 + frame.width,
|
||||
height: bl4 + ofH2 + frame.height)
|
||||
|
||||
let blurImageView = UIImageView(frame: contextFrame)
|
||||
blurImageView.image = blurImage(image: screen)
|
||||
blurImageView.contentMode = .scaleAspectFill
|
||||
|
||||
|
||||
addSubview(blurImageView)
|
||||
}
|
||||
|
||||
//MARK: - Rectangle Bezier
|
||||
|
||||
func rectangle(rect: CGRect, radius: CGFloat) -> UIBezierPath {
|
||||
|
||||
let r = self.cornerRadius(radius)
|
||||
let w = rect.width
|
||||
let h = rect.height
|
||||
|
||||
let path = UIBezierPath()
|
||||
path.move(to: CGPoint(x: r, y: 0.0))
|
||||
|
||||
path.addLine(to: CGPoint(x: w - r, y: 0.0))
|
||||
path.addArc(withCenter: CGPoint(x: w - r, y: r),
|
||||
radius: r,
|
||||
startAngle: 3.0 * .pi / 2.0,
|
||||
endAngle: 2 * .pi,
|
||||
clockwise: true)
|
||||
|
||||
path.addLine(to: CGPoint(x: w, y: h - r))
|
||||
path.addArc(withCenter: CGPoint(x: w - r, y: h - r),
|
||||
radius: r,
|
||||
startAngle: 0.0,
|
||||
endAngle: .pi / 2.0,
|
||||
clockwise: true)
|
||||
|
||||
path.addLine(to: CGPoint(x: r, y: h))
|
||||
path.addArc(withCenter: CGPoint(x: r, y: h - r),
|
||||
radius: r,
|
||||
startAngle: .pi / 2.0,
|
||||
endAngle: .pi,
|
||||
clockwise: true)
|
||||
|
||||
path.addLine(to: CGPoint(x: 0.0, y: r))
|
||||
path.addArc(withCenter: CGPoint(x: r, y: r),
|
||||
radius: r,
|
||||
startAngle: .pi,
|
||||
endAngle: 3.0 * .pi / 2.0,
|
||||
clockwise: true)
|
||||
|
||||
path.close()
|
||||
return path
|
||||
}
|
||||
|
||||
//MARK: - Star Bezier
|
||||
|
||||
func star(polygon: Bool = false, rect: CGRect, radius: CGFloat, pointsOnStar: Int) -> UIBezierPath {
|
||||
|
||||
let path = UIBezierPath()
|
||||
|
||||
let minSize = max(rect.width, rect.height)
|
||||
var starExtrusion: CGFloat = minSize * 0.18695652173
|
||||
if radius != 0 {
|
||||
starExtrusion = minSize * radius
|
||||
}
|
||||
|
||||
let center: CGPoint = .zero
|
||||
|
||||
var angle: CGFloat = -CGFloat(.pi / 2.0)
|
||||
let angleIncrement = CGFloat(.pi * 2.0 / Double(pointsOnStar))
|
||||
let radius = minSize / 2.0
|
||||
|
||||
var firstPoint = true
|
||||
|
||||
func pointFrom(angle: CGFloat, radius: CGFloat, offset: CGPoint) -> CGPoint {
|
||||
return CGPoint(x: radius * cos(angle) + offset.x,
|
||||
y: radius * sin(angle) + offset.y)
|
||||
}
|
||||
|
||||
for _ in 1...pointsOnStar {
|
||||
|
||||
let point: CGPoint = pointFrom(angle: angle, radius: radius, offset: center)
|
||||
let nextPoint: CGPoint = pointFrom(angle: angle + angleIncrement, radius: radius, offset: center)
|
||||
let midPoint: CGPoint = pointFrom(angle: angle + angleIncrement / 2.0, radius: starExtrusion, offset: center)
|
||||
|
||||
var perY: CGFloat = 1.0
|
||||
var perX: CGFloat = 1.0
|
||||
|
||||
if rect.height < rect.width {
|
||||
perY = (rect.height / minSize)
|
||||
} else {
|
||||
perX = (rect.width / minSize)
|
||||
}
|
||||
|
||||
let p = CGPoint(x: (point.x * perX) + (rect.width / 2.0),
|
||||
y: (point.y * perY) + (rect.height / 2.0))
|
||||
|
||||
let n = CGPoint(x: (nextPoint.x * perX) + (rect.width / 2.0),
|
||||
y: (nextPoint.y * perY) + (rect.height / 2.0))
|
||||
|
||||
let m = CGPoint(x: (midPoint.x * perX) + (rect.width / 2.0),
|
||||
y: (midPoint.y * perY) + (rect.height / 2.0))
|
||||
|
||||
if firstPoint {
|
||||
firstPoint = false
|
||||
path.move(to: p)
|
||||
}
|
||||
|
||||
if !polygon {
|
||||
path.addLine(to: m)
|
||||
}
|
||||
path.addLine(to: n)
|
||||
|
||||
angle += angleIncrement
|
||||
}
|
||||
|
||||
path.close()
|
||||
|
||||
return path
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,510 @@
|
||||
//
|
||||
// DesignTest.swift
|
||||
// FigmaConvertXib
|
||||
//
|
||||
// Created by Рустам Мотыгуллин on 28.07.2020.
|
||||
// Copyright © 2020 mrusta. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
|
||||
@IBDesignable
|
||||
class DesignnImageView: UIView {
|
||||
|
||||
@IBInspectable var cornerRadius: CGFloat = 0.0
|
||||
|
||||
@IBInspectable var fillColor: UIColor?
|
||||
@IBInspectable var image: UIImage?
|
||||
@IBInspectable var imageMode: Int = 1
|
||||
@IBInspectable var blur: CGFloat = 0.0
|
||||
|
||||
|
||||
@IBInspectable var brColor: UIColor = .clear
|
||||
@IBInspectable var brWidth: CGFloat = 0.0
|
||||
@IBInspectable var brDash: Int = 0
|
||||
|
||||
func setup() {
|
||||
|
||||
if let foundView = viewWithTag(55) {
|
||||
foundView.removeFromSuperview()
|
||||
}
|
||||
|
||||
let d = DesignFigure_(frame: bounds)
|
||||
d.backgroundColor = .clear
|
||||
d.tag = 55
|
||||
d.cornerRadius = cornerRadius
|
||||
d.blur = blur
|
||||
d.image = image
|
||||
d.imageMode = imageMode
|
||||
d.fillColor = fillColor
|
||||
d.brWidth = brWidth
|
||||
d.brColor = brColor
|
||||
d.brDash = brDash
|
||||
insertSubview(d, at: 0)
|
||||
|
||||
}
|
||||
|
||||
override func layoutSubviews() { setup() }
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@IBDesignable
|
||||
class DesignnGradientView: UIView {
|
||||
|
||||
@IBInspectable var cornerRadius: CGFloat = 0.0
|
||||
|
||||
@IBInspectable var fillColor: UIColor?
|
||||
|
||||
@IBInspectable var brColor: UIColor = .clear
|
||||
@IBInspectable var brWidth: CGFloat = 0.0
|
||||
@IBInspectable var brDash: Int = 0
|
||||
|
||||
@IBInspectable var grColor1: UIColor?
|
||||
@IBInspectable var grColor2: UIColor?
|
||||
@IBInspectable var grColor3: UIColor?
|
||||
@IBInspectable var grStartPoint: CGPoint = .zero
|
||||
@IBInspectable var grEndPoint: CGPoint = .zero
|
||||
@IBInspectable var grRadial: Bool = false /// default: linear
|
||||
|
||||
func setup() {
|
||||
|
||||
if let foundView = viewWithTag(55) {
|
||||
foundView.removeFromSuperview()
|
||||
}
|
||||
|
||||
let d = DesignFigure_(frame: bounds)
|
||||
d.backgroundColor = .clear
|
||||
d.tag = 55
|
||||
d.cornerRadius = cornerRadius
|
||||
// d.blur = blur
|
||||
// d.image = image
|
||||
// d.imageMode = imageMode
|
||||
d.fillColor = fillColor
|
||||
d.brWidth = brWidth
|
||||
d.brColor = brColor
|
||||
d.brDash = brDash
|
||||
|
||||
d.grColor1 = grColor1
|
||||
d.grColor2 = grColor2
|
||||
d.grColor3 = grColor3
|
||||
// d.grColor4 = grColor4
|
||||
// d.grColor5 = grColor5
|
||||
// d.grColor6 = grColor6
|
||||
d.grStartPoint = grStartPoint
|
||||
d.grEndPoint = grEndPoint
|
||||
d.grRadial = grRadial
|
||||
// d.grDrawsOptions = grDrawsOptions
|
||||
// d.grDebug = grDebug
|
||||
// d.grPointPercent = grPointPercent
|
||||
// d.grBlendMode = grBlendMode
|
||||
|
||||
insertSubview(d, at: 0)
|
||||
|
||||
}
|
||||
|
||||
override func layoutSubviews() { setup() }
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@IBDesignable
|
||||
class DesignnShadowView: UIView {
|
||||
|
||||
@IBInspectable var cornerRadius: CGFloat = 0.0
|
||||
|
||||
@IBInspectable var fillColor: UIColor?
|
||||
|
||||
|
||||
@IBInspectable var brColor: UIColor = .clear
|
||||
@IBInspectable var brWidth: CGFloat = 0.0
|
||||
@IBInspectable var brDash: Int = 0
|
||||
|
||||
@IBInspectable var shColor: UIColor = .clear
|
||||
@IBInspectable var shRadius: CGFloat = 0.0
|
||||
@IBInspectable var shOffset: CGSize = .zero
|
||||
|
||||
|
||||
func setup() {
|
||||
|
||||
if let foundView = viewWithTag(55) {
|
||||
foundView.removeFromSuperview()
|
||||
}
|
||||
|
||||
let d = DesignFigure_(frame: bounds)
|
||||
d.backgroundColor = .clear
|
||||
d.tag = 55
|
||||
d.cornerRadius = cornerRadius
|
||||
// d.blur = blur
|
||||
// d.image = image
|
||||
// d.imageMode = imageMode
|
||||
d.fillColor = fillColor
|
||||
d.brWidth = brWidth
|
||||
d.brColor = brColor
|
||||
d.brDash = brDash
|
||||
|
||||
|
||||
d.shColor = shColor
|
||||
d.shRadius = shRadius
|
||||
d.shOffset = shOffset
|
||||
insertSubview(d, at: 0)
|
||||
|
||||
}
|
||||
|
||||
override func layoutSubviews() { setup() }
|
||||
|
||||
}
|
||||
|
||||
|
||||
@IBDesignable
|
||||
class DesignnView: UIView {
|
||||
|
||||
@IBInspectable var cornerRadius: CGFloat = 0.0
|
||||
|
||||
@IBInspectable var fillColor: UIColor?
|
||||
|
||||
|
||||
@IBInspectable var brColor: UIColor = .clear
|
||||
@IBInspectable var brWidth: CGFloat = 0.0
|
||||
@IBInspectable var brDash: Int = 0
|
||||
|
||||
func setup() {
|
||||
|
||||
if let foundView = viewWithTag(55) {
|
||||
foundView.removeFromSuperview()
|
||||
}
|
||||
|
||||
let d = DesignFigure_(frame: bounds)
|
||||
d.backgroundColor = .clear
|
||||
d.tag = 55
|
||||
d.cornerRadius = cornerRadius
|
||||
// d.blur = blur
|
||||
// d.image = image
|
||||
// d.imageMode = imageMode
|
||||
d.fillColor = fillColor
|
||||
d.brWidth = brWidth
|
||||
d.brColor = brColor
|
||||
d.brDash = brDash
|
||||
insertSubview(d, at: 0)
|
||||
|
||||
}
|
||||
|
||||
override func layoutSubviews() { setup() }
|
||||
|
||||
}
|
||||
|
||||
|
||||
@IBDesignable
|
||||
class DesignFillView_: UIView {
|
||||
|
||||
|
||||
@IBInspectable var cornerRadius: CGFloat = 0.0
|
||||
|
||||
//MARK: Blur
|
||||
|
||||
@IBInspectable var blur: CGFloat = 0.0
|
||||
|
||||
//MARK: Image
|
||||
|
||||
@IBInspectable var image: UIImage?
|
||||
@IBInspectable var imageMode: Int = 1 /// Scale to fill / Aspect fit / fill
|
||||
|
||||
//MARK: Fill
|
||||
|
||||
@IBInspectable var fillColor: UIColor?
|
||||
//MARK: Border
|
||||
|
||||
@IBInspectable var brColor: UIColor = .clear
|
||||
@IBInspectable var brWidth: CGFloat = 0.0
|
||||
@IBInspectable var brDash: Int = 0
|
||||
|
||||
|
||||
|
||||
@IBInspectable var shColor: UIColor = .clear
|
||||
@IBInspectable var shRadius: CGFloat = 0.0
|
||||
@IBInspectable var shOffset: CGSize = .zero
|
||||
|
||||
//MARK: - Draw
|
||||
|
||||
override func draw(_ rect: CGRect) {
|
||||
super.draw(rect)
|
||||
|
||||
guard let bezier = figurePath(bounds) else { return }
|
||||
|
||||
addFill(bezier: bezier)
|
||||
addImage(bezier: bezier)
|
||||
addBorder()
|
||||
addShadow()
|
||||
addBlur()
|
||||
}
|
||||
|
||||
//MARK: - Figure Type
|
||||
|
||||
private func figurePath(_ rect: CGRect) -> UIBezierPath? {
|
||||
return rectangle(rect: rect, radius: cornerRadius)
|
||||
}
|
||||
|
||||
|
||||
|
||||
private func addImage(bezier: UIBezierPath) {
|
||||
|
||||
guard let image = image else { return }
|
||||
guard let context = UIGraphicsGetCurrentContext() else { return }
|
||||
|
||||
var f: CGRect = CGRect(x: 0, y: -bounds.height, width: bounds.width, height: bounds.height)
|
||||
|
||||
if imageMode == 1 || imageMode == 2 {
|
||||
|
||||
var w: CGFloat = 0
|
||||
var h: CGFloat = 0
|
||||
|
||||
let percent = bounds.height / image.size.height
|
||||
w = image.size.width * percent
|
||||
h = bounds.height
|
||||
|
||||
if (imageMode == 1) && (w > bounds.width) ||
|
||||
(imageMode == 2) && (bounds.width > w) {
|
||||
|
||||
let percent = bounds.width / image.size.width
|
||||
h = image.size.height * percent
|
||||
w = bounds.width
|
||||
}
|
||||
|
||||
let x = (bounds.width / 2) - (w / 2)
|
||||
let y = (-h + (((bounds.height / 2) - (h / 2)) * (-1)))
|
||||
|
||||
f = CGRect(x: x, y: y, width: w, height: h)
|
||||
}
|
||||
|
||||
context.saveGState()
|
||||
bezier.addClip()
|
||||
context.scaleBy(x: 1, y: -1)
|
||||
context.draw(image.cgImage!, in: f, byTiling: false)
|
||||
context.restoreGState()
|
||||
|
||||
}
|
||||
|
||||
//MARK: - Fill
|
||||
|
||||
private func addFill(bezier: UIBezierPath) {
|
||||
|
||||
guard let fillColor = fillColor else { return }
|
||||
guard let context = UIGraphicsGetCurrentContext() else { return }
|
||||
|
||||
bezier.close()
|
||||
context.saveGState()
|
||||
fillColor.setFill()
|
||||
bezier.fill()
|
||||
context.restoreGState()
|
||||
}
|
||||
|
||||
|
||||
//MARK: - Shadow
|
||||
|
||||
private func addShadow() {
|
||||
|
||||
if shRadius < 0 { shRadius = 0 }
|
||||
|
||||
layer.shadowOffset = shOffset
|
||||
layer.shadowOpacity = 1.0
|
||||
layer.shadowRadius = shRadius
|
||||
layer.shadowColor = shColor.cgColor
|
||||
}
|
||||
|
||||
|
||||
|
||||
// MARK: - Border / Stroke
|
||||
|
||||
private func addBorder() {
|
||||
|
||||
if brWidth < 0 { brWidth = 0 }
|
||||
|
||||
guard let context = UIGraphicsGetCurrentContext() else { return }
|
||||
|
||||
// brColor.setStroke()
|
||||
|
||||
guard let bezier = figurePath(bounds) else { return }
|
||||
|
||||
context.saveGState()
|
||||
|
||||
if brDash > 0 {
|
||||
|
||||
UIColor.clear.setFill()
|
||||
bezier.fill()
|
||||
|
||||
brColor.setStroke()
|
||||
bezier.lineWidth = brWidth * 2
|
||||
|
||||
let dashPattern : [CGFloat] = [ CGFloat(brDash), CGFloat(brDash)]
|
||||
bezier.setLineDash(dashPattern, count: 2, phase: 0)
|
||||
bezier.stroke()
|
||||
|
||||
} else {
|
||||
|
||||
brColor.setStroke()
|
||||
|
||||
bezier.lineWidth = brWidth * 2
|
||||
bezier.stroke()
|
||||
}
|
||||
|
||||
|
||||
|
||||
bezier.stroke()
|
||||
bezier.stroke()
|
||||
|
||||
context.restoreGState()
|
||||
context.restoreGState()
|
||||
}
|
||||
|
||||
|
||||
|
||||
// MARK: - Blur
|
||||
|
||||
private func addBlur() {
|
||||
|
||||
guard blur > 0 else { return }
|
||||
|
||||
guard let image = screenShotContext() else { return }
|
||||
|
||||
self.addblur(screen: image, blur: blur)
|
||||
}
|
||||
|
||||
// MARK: Screen Shot Context
|
||||
|
||||
private func screenShotContext() -> UIImage? {
|
||||
|
||||
guard let context = UIGraphicsGetCurrentContext() else { return nil }
|
||||
guard let cgimage: CGImage = context.makeImage() else { return nil }
|
||||
let image = UIImage(cgImage: cgimage)
|
||||
|
||||
return image
|
||||
}
|
||||
|
||||
// MARK: Clear Context
|
||||
|
||||
private func clearContext() {
|
||||
|
||||
let f = CGRect(x: 0, y: 0,
|
||||
width: frame.width * 2,
|
||||
height: frame.height * 2)
|
||||
|
||||
let context = UIGraphicsGetCurrentContext()
|
||||
context?.clear(f)
|
||||
}
|
||||
|
||||
|
||||
private func addblur(screen: UIImage, blur: CGFloat) {
|
||||
|
||||
clearContext()
|
||||
|
||||
func blurImage(image: UIImage) -> UIImage? {
|
||||
|
||||
guard let ciscreen: CIImage = CIImage(image: screen) else { return nil }
|
||||
|
||||
guard let filter: CIFilter = CIFilter(name: "CIGaussianBlur") else { return nil }
|
||||
filter.setDefaults()
|
||||
filter.setValue(ciscreen, forKey: kCIInputImageKey)
|
||||
filter.setValue(blur, forKey: kCIInputRadiusKey)
|
||||
|
||||
|
||||
let bl2 = ((blur * 3) * -1)
|
||||
let bl4 = (blur * 6)
|
||||
|
||||
let contextFrame = CGRect(
|
||||
x: bl2,
|
||||
y: bl2,
|
||||
width: bl4 + image.size.width,
|
||||
height: bl4 + image.size.height
|
||||
)
|
||||
|
||||
// let bl2 = ((blur * 2) * -1)
|
||||
// let bl4 = (blur * 4)
|
||||
// let contextFrame2 = CGRect(x: bl2,
|
||||
// y: bl2,
|
||||
// width: bl4 + (frame.width * 2),
|
||||
// height: bl4 + (frame.height * 2))
|
||||
|
||||
let ciContext = CIContext(options: nil)
|
||||
guard let ci: CIImage = filter.value(forKey: kCIOutputImageKey) as? CIImage else { return nil }
|
||||
guard let cImg = ciContext.createCGImage(ci, from: contextFrame) else { return nil }
|
||||
|
||||
let finalImage = UIImage(cgImage: cImg)
|
||||
|
||||
return finalImage
|
||||
}
|
||||
|
||||
let p2: CGFloat = 1
|
||||
let p4: CGFloat = p2 * 2
|
||||
|
||||
let bl2 = ((blur * p2) * -1)
|
||||
let bl4 = (blur * p4)
|
||||
|
||||
let x: CGFloat = 0
|
||||
let ofW = (x * 1) * -1
|
||||
let ofH = (x * 1) * -1
|
||||
|
||||
let ofW2 = (x * 2)
|
||||
let ofH2 = (x * 2)
|
||||
|
||||
let contextFrame = CGRect(x: bl2 + ofW,
|
||||
y: bl2 + ofH,
|
||||
width: bl4 + ofW2 + frame.width,
|
||||
height: bl4 + ofH2 + frame.height)
|
||||
|
||||
let blurImageView = UIImageView(frame: contextFrame)
|
||||
blurImageView.image = blurImage(image: screen)
|
||||
blurImageView.contentMode = .scaleAspectFill
|
||||
|
||||
|
||||
addSubview(blurImageView)
|
||||
}
|
||||
|
||||
//MARK: - Rectangle Bezier
|
||||
|
||||
func rectangle(rect: CGRect, radius: CGFloat) -> UIBezierPath {
|
||||
|
||||
let r = self.cornerRadius(radius)
|
||||
let w = rect.width
|
||||
let h = rect.height
|
||||
|
||||
let path = UIBezierPath()
|
||||
path.move(to: CGPoint(x: r, y: 0.0))
|
||||
|
||||
path.addLine(to: CGPoint(x: w - r, y: 0.0))
|
||||
path.addArc(withCenter: CGPoint(x: w - r, y: r),
|
||||
radius: r,
|
||||
startAngle: 3.0 * .pi / 2.0,
|
||||
endAngle: 2 * .pi,
|
||||
clockwise: true)
|
||||
|
||||
path.addLine(to: CGPoint(x: w, y: h - r))
|
||||
path.addArc(withCenter: CGPoint(x: w - r, y: h - r),
|
||||
radius: r,
|
||||
startAngle: 0.0,
|
||||
endAngle: .pi / 2.0,
|
||||
clockwise: true)
|
||||
|
||||
path.addLine(to: CGPoint(x: r, y: h))
|
||||
path.addArc(withCenter: CGPoint(x: r, y: h - r),
|
||||
radius: r,
|
||||
startAngle: .pi / 2.0,
|
||||
endAngle: .pi,
|
||||
clockwise: true)
|
||||
|
||||
path.addLine(to: CGPoint(x: 0.0, y: r))
|
||||
path.addArc(withCenter: CGPoint(x: r, y: r),
|
||||
radius: r,
|
||||
startAngle: .pi,
|
||||
endAngle: 3.0 * .pi / 2.0,
|
||||
clockwise: true)
|
||||
|
||||
path.close()
|
||||
return path
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
//
|
||||
// DesignImageView.swift
|
||||
// FigmaConvertXib
|
||||
//
|
||||
// Created by Рустам Мотыгуллин on 21.07.2020.
|
||||
// Copyright © 2020 mrusta. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
@IBDesignable
|
||||
class DesignImage: UIImageView {
|
||||
|
||||
@IBInspectable var cornerRadius: CGFloat = 0.0
|
||||
|
||||
/// Shadow
|
||||
@IBInspectable var shColor: UIColor = .clear
|
||||
@IBInspectable var shRadius: CGFloat = 0.0
|
||||
@IBInspectable var shOffset: CGSize = .zero
|
||||
|
||||
/// Border
|
||||
@IBInspectable var brColor: UIColor = .clear
|
||||
@IBInspectable var brWidth: CGFloat = 0.0
|
||||
|
||||
override func draw(_ rect: CGRect) {
|
||||
|
||||
super.draw(rect)
|
||||
|
||||
layer.backgroundColor = UIColor.black.cgColor
|
||||
|
||||
let l = CALayer()
|
||||
l.frame = rect
|
||||
l.borderColor = brColor.cgColor
|
||||
l.borderWidth = brWidth
|
||||
l.cornerRadius = cornerRadius
|
||||
layer.addSublayer(l)
|
||||
|
||||
add(shadow: shRadius, offset: shOffset, color: shColor)
|
||||
add(border: brWidth, color: brColor)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@IBDesignable
|
||||
class DesignImageView: UIView {
|
||||
|
||||
//MARK: Properties
|
||||
|
||||
@IBInspectable var image: UIImage?
|
||||
@IBInspectable var contModeFill: Bool = false
|
||||
|
||||
/// Radius
|
||||
@IBInspectable var cornerRadius: CGFloat = 0.0
|
||||
|
||||
/// Blur
|
||||
@IBInspectable var blur: CGFloat = 0.0
|
||||
|
||||
/// Fill
|
||||
@IBInspectable var fillColor: UIColor = .clear
|
||||
|
||||
/// Gradient
|
||||
@IBInspectable var grColor2: UIColor?
|
||||
@IBInspectable var grColor3: UIColor?
|
||||
@IBInspectable var grColor4: UIColor?
|
||||
@IBInspectable var grColor5: UIColor?
|
||||
@IBInspectable var grColor6: UIColor?
|
||||
|
||||
@IBInspectable var grStartPoint: CGPoint = .zero
|
||||
@IBInspectable var grEndPoint: CGPoint = .zero
|
||||
|
||||
/// Inner Shadow
|
||||
@IBInspectable var inShColor: UIColor = .clear
|
||||
@IBInspectable var inShRadius: CGFloat = 0.0
|
||||
@IBInspectable var inShOffset: CGSize = .zero
|
||||
|
||||
/// Shadow
|
||||
@IBInspectable var shColor: UIColor = .clear
|
||||
@IBInspectable var shRadius: CGFloat = 0.0
|
||||
@IBInspectable var shOffset: CGSize = .zero
|
||||
|
||||
/// Border
|
||||
@IBInspectable var brColor: UIColor = .clear
|
||||
@IBInspectable var brWidth: CGFloat = 0.0
|
||||
|
||||
var imageView: UIImageView!
|
||||
|
||||
|
||||
//MARK: - Draw
|
||||
|
||||
override func draw(_ rect: CGRect) {
|
||||
|
||||
let radius = self.cornerRadius(cornerRadius)
|
||||
|
||||
layer.cornerRadius = radius
|
||||
|
||||
layer.backgroundColor = fillColor.cgColor
|
||||
|
||||
addShadow()
|
||||
addBorder()
|
||||
|
||||
addImage()
|
||||
|
||||
addGradient()
|
||||
addInnerShadow()
|
||||
|
||||
add(blur: blur) { [weak self] in
|
||||
|
||||
guard let _self = self else { return }
|
||||
_self.imageView.setAlpha(.invisible)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private func addGradient() {
|
||||
|
||||
guard let glayer = add(gradient: fillColor,
|
||||
color2: grColor2,
|
||||
color3: grColor3,
|
||||
color4: grColor4,
|
||||
color5: grColor5,
|
||||
color6: grColor6,
|
||||
pointStart: grStartPoint,
|
||||
pointEnd: grEndPoint,
|
||||
cornerRadius: cornerRadius) else { return }
|
||||
|
||||
imageView.layer.insertSublayer(glayer, at: 0)
|
||||
|
||||
}
|
||||
|
||||
private func addInnerShadow() {
|
||||
|
||||
addInnerShadow(color: inShColor,
|
||||
radius: inShRadius,
|
||||
offset: inShOffset,
|
||||
cornerRadius: cornerRadius)
|
||||
|
||||
}
|
||||
|
||||
private func addShadow() {
|
||||
layer.shadowOffset = shOffset
|
||||
layer.shadowOpacity = 1.0
|
||||
layer.shadowRadius = shRadius
|
||||
layer.shadowColor = shColor.cgColor
|
||||
}
|
||||
|
||||
private func addBorder() {
|
||||
layer.borderColor = brColor.cgColor
|
||||
layer.borderWidth = brWidth
|
||||
}
|
||||
|
||||
private func addImage() {
|
||||
let f = CGRect(x: 0, y: 0, width: frame.width, height: frame.height)
|
||||
imageView = UIImageView(frame: f)
|
||||
imageView.image = image
|
||||
imageView.contentMode = contModeFill ? .scaleAspectFill : .scaleAspectFit
|
||||
imageView.clipsToBounds = true
|
||||
imageView.layer.cornerRadius = self.cornerRadius(cornerRadius)
|
||||
insertSubview(imageView, at: 0)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
//
|
||||
// DesignInnerShadow.swift
|
||||
// FigmaConvertXib
|
||||
//
|
||||
// Created by Рустам Мотыгуллин on 12.02.2021.
|
||||
// Copyright © 2021 mrusta. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
@IBDesignable
|
||||
class DesignInnerShadow: UIView {
|
||||
|
||||
@IBInspectable var cornerRadius: CGFloat = 0.0
|
||||
@IBInspectable var inShColor: UIColor = .clear
|
||||
@IBInspectable var inShRadius: CGFloat = 0.0
|
||||
@IBInspectable var inShOffset: CGSize = .zero
|
||||
|
||||
@IBInspectable var fillColor: UIColor?
|
||||
|
||||
//MARK: - Draw
|
||||
|
||||
override func draw(_ rect: CGRect) {
|
||||
super.draw(rect)
|
||||
|
||||
addInnerShadow(rect)
|
||||
|
||||
layer.backgroundColor = fillColor?.cgColor
|
||||
layer.cornerRadius = cornerRadius
|
||||
}
|
||||
|
||||
//MARK: - Add Shadow
|
||||
|
||||
func addInnerShadow(_ rect: CGRect) {
|
||||
/// General Declarations
|
||||
|
||||
let context = UIGraphicsGetCurrentContext()!
|
||||
let path = UIBezierPath(roundedRect: rect, cornerRadius: cornerRadius)
|
||||
|
||||
UIColor(white: 1.0, alpha: 0.00001).setFill()
|
||||
|
||||
path.fill()
|
||||
context.saveGState()
|
||||
context.clip(to: path.bounds)
|
||||
context.setAlpha(CGFloat(1.0))
|
||||
context.beginTransparencyLayer(auxiliaryInfo: nil)
|
||||
|
||||
|
||||
let innerShadow = NSShadow()
|
||||
innerShadow.shadowColor = inShColor
|
||||
innerShadow.shadowOffset = inShOffset
|
||||
innerShadow.shadowBlurRadius = inShRadius * 2
|
||||
|
||||
context.setShadow(offset: inShOffset,
|
||||
blur: inShRadius,
|
||||
color: inShColor.cgColor)
|
||||
context.setBlendMode(.sourceOut)
|
||||
context.beginTransparencyLayer(auxiliaryInfo: nil)
|
||||
let ovalOpaqueShadow = UIColor.init(cgColor: inShColor.cgColor)
|
||||
ovalOpaqueShadow.setFill()
|
||||
path.fill()
|
||||
context.endTransparencyLayer()
|
||||
context.endTransparencyLayer()
|
||||
context.restoreGState()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
//
|
||||
// DesignLabel.swift
|
||||
// FigmaConvertXib
|
||||
//
|
||||
// Created by Рустам Мотыгуллин on 25.07.2020.
|
||||
// Copyright © 2020 mrusta. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
@IBDesignable
|
||||
class DesignLabel: UILabel {
|
||||
|
||||
//MARK: Gradient
|
||||
@IBInspectable var grColor1: UIColor?
|
||||
@IBInspectable var grColor2: UIColor?
|
||||
@IBInspectable var grColor3: UIColor?
|
||||
@IBInspectable var grColor4: UIColor?
|
||||
@IBInspectable var grColor5: UIColor?
|
||||
@IBInspectable var grColor6: UIColor?
|
||||
|
||||
@IBInspectable var grStartPoint: CGPoint = .zero
|
||||
@IBInspectable var grEndPoint: CGPoint = .zero
|
||||
|
||||
@IBInspectable var grRadial: Bool = false /// default: linear
|
||||
@IBInspectable var grDrawsOptions: Bool = true
|
||||
@IBInspectable var grDebug: Bool = false
|
||||
@IBInspectable var grPointPercent: Bool = true
|
||||
@IBInspectable var grBlendMode: Int = 20 /// заполнители
|
||||
|
||||
//MARK: - Shadow
|
||||
|
||||
@IBInspectable var shColor: UIColor = .clear
|
||||
@IBInspectable var shRadius: CGFloat = 0.0
|
||||
@IBInspectable var shOffset: CGSize = .zero
|
||||
|
||||
//MARK: Border
|
||||
|
||||
@IBInspectable var brColor: UIColor = .clear
|
||||
@IBInspectable var brWidth: CGFloat = 0.0
|
||||
|
||||
//MARK: - Draw
|
||||
|
||||
override func draw(_ rect: CGRect) {
|
||||
super.draw(rect)
|
||||
shadow()
|
||||
gradient()
|
||||
gradientDebug()
|
||||
border()
|
||||
}
|
||||
|
||||
//MARK: - Add Shadow
|
||||
|
||||
private func shadow() {
|
||||
layer.shadowOpacity = 1.0
|
||||
layer.shadowOffset = shOffset
|
||||
layer.shadowRadius = shRadius
|
||||
layer.shadowColor = shColor.cgColor
|
||||
}
|
||||
|
||||
//MARK: - Add Border
|
||||
|
||||
func border() {
|
||||
|
||||
guard brColor != .clear, brWidth != 0.0 else { return }
|
||||
|
||||
let borderLabel = UILabel(frame: bounds)
|
||||
borderLabel.text = text
|
||||
borderLabel.font = font
|
||||
borderLabel.textColor = .clear
|
||||
borderLabel.numberOfLines = numberOfLines
|
||||
borderLabel.backgroundColor = .clear
|
||||
|
||||
|
||||
let strokeTextAttributes: [NSAttributedString.Key : Any] = [
|
||||
.strokeColor : brColor,
|
||||
.strokeWidth : (-1 * (brWidth * 2)),
|
||||
]
|
||||
|
||||
borderLabel.attributedText = NSAttributedString(string: text ?? "",
|
||||
attributes: strokeTextAttributes)
|
||||
|
||||
addSubview(borderLabel)
|
||||
}
|
||||
|
||||
//MARK: - Add Gradient
|
||||
|
||||
func gradient() {
|
||||
var colors: [CGColor] = []
|
||||
|
||||
if grColor1 != nil || grColor2 != nil || grColor3 != nil || grColor4 != nil || grColor5 != nil {
|
||||
|
||||
if let color1 = grColor1 { colors.append(color1.cgColor) }
|
||||
if let color2 = grColor2 { colors.append(color2.cgColor) }
|
||||
if let color3 = grColor3 { colors.append(color3.cgColor) }
|
||||
if let color4 = grColor4 { colors.append(color4.cgColor) }
|
||||
if let color5 = grColor5 { colors.append(color5.cgColor) }
|
||||
if let color6 = grColor6 { colors.append(color6.cgColor) }
|
||||
|
||||
} else { return }
|
||||
|
||||
guard let gradient = CGGradient(colorsSpace: CGColorSpaceCreateDeviceRGB(),
|
||||
colors: colors as CFArray,
|
||||
locations: nil) else { return }
|
||||
|
||||
guard let context = UIGraphicsGetCurrentContext() else { return }
|
||||
|
||||
let bMode = CGBlendMode(rawValue: CGBlendMode.RawValue(grBlendMode)) ?? CGBlendMode.sourceAtop
|
||||
context.setBlendMode(bMode)
|
||||
|
||||
let options: CGGradientDrawingOptions =
|
||||
grDrawsOptions ? [ .drawsBeforeStartLocation, .drawsAfterEndLocation ] : [ ]
|
||||
|
||||
|
||||
if grPointPercent {
|
||||
grEndPoint = CGPoint(x: grEndPoint.x * frame.width, y: grEndPoint.y * frame.height)
|
||||
grStartPoint = CGPoint(x: grStartPoint.x * frame.width, y: grStartPoint.y * frame.height)
|
||||
}
|
||||
|
||||
//MARK: Gr. Radial / Linear
|
||||
if grRadial {
|
||||
let x: CGFloat = (grEndPoint.x - grStartPoint.x)
|
||||
let y: CGFloat = (grEndPoint.y - grStartPoint.y)
|
||||
let distance: CGFloat = sqrt((x * x) + (y * y))
|
||||
|
||||
context.drawRadialGradient(gradient,
|
||||
startCenter: grStartPoint,
|
||||
startRadius: 0,
|
||||
endCenter: grStartPoint,
|
||||
endRadius: distance,
|
||||
options: options)
|
||||
} else {
|
||||
context.drawLinearGradient(gradient,
|
||||
start: grStartPoint,
|
||||
end: grEndPoint,
|
||||
options: options)
|
||||
}
|
||||
context.saveGState()
|
||||
|
||||
}
|
||||
|
||||
//MARK: - Gr. Debug
|
||||
|
||||
func gradientDebug() {
|
||||
guard let context = UIGraphicsGetCurrentContext() else { return }
|
||||
guard grDebug else { return }
|
||||
|
||||
context.setBlendMode(CGBlendMode.normal)
|
||||
context.setLineWidth(3.0)
|
||||
context.setFillColor(UIColor.black.cgColor)
|
||||
context.setStrokeColor(UIColor.systemRed.cgColor)
|
||||
|
||||
let ellipseRadius: CGFloat = 5
|
||||
let circleRect = CGRect(x: grStartPoint.x - ellipseRadius,
|
||||
y: grStartPoint.y - ellipseRadius,
|
||||
width: ellipseRadius * 2,
|
||||
height: ellipseRadius * 2)
|
||||
|
||||
context.fillEllipse(in: circleRect)
|
||||
context.strokeEllipse(in: circleRect)
|
||||
context.setStrokeColor(UIColor.green.cgColor)
|
||||
|
||||
let circleRect2 = CGRect(x: grEndPoint.x - ellipseRadius,
|
||||
y: grEndPoint.y - ellipseRadius,
|
||||
width: ellipseRadius * 2,
|
||||
height: ellipseRadius * 2)
|
||||
|
||||
context.fillEllipse(in: circleRect2)
|
||||
context.strokeEllipse(in: circleRect2)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
//
|
||||
// DesignTest.swift
|
||||
// FigmaConvertXib
|
||||
//
|
||||
// Created by Рустам Мотыгуллин on 28.07.2020.
|
||||
// Copyright © 2020 mrusta. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
private extension UIButton {
|
||||
|
||||
func style(selected: Bool) {
|
||||
if selected {
|
||||
self.setTitleColor(#colorLiteral(red: 1, green: 1, blue: 1, alpha: 1), for: .normal)
|
||||
} else {
|
||||
self.setTitleColor(#colorLiteral(red: 0.3098039216, green: 0.3098039216, blue: 0.3098039216, alpha: 1), for: .normal)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private extension UIView {
|
||||
|
||||
func selectedView() {
|
||||
|
||||
self.layer.cornerRadius = 8
|
||||
self.layer.backgroundColor = #colorLiteral(red: 0.9960784314, green: 0.3137254902, blue: 0, alpha: 1).cgColor
|
||||
|
||||
self.add(shadow: 6 / 2, offset: CGSize(width: -4, height: -4), color: #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1))
|
||||
|
||||
self.addInnerShadow(color: #colorLiteral(red: 0.4784313725, green: 0.1960784314, blue: 0.01568627451, alpha: 0.3674445752),
|
||||
radius: 9 / 2,
|
||||
offset: CGSize(width: -4, height: -4),
|
||||
cornerRadius: 8)
|
||||
|
||||
self.addInnerShadow(color: #colorLiteral(red: 1, green: 1, blue: 1, alpha: 0.3720056395),
|
||||
radius: 8 / 2,
|
||||
offset: CGSize(width: 4, height: 4),
|
||||
cornerRadius: 8)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@IBDesignable
|
||||
class DesignSegmentBar: UIView {
|
||||
|
||||
@IBInspectable var textItems: String = ""
|
||||
@IBInspectable var index: Int = 0
|
||||
|
||||
private var views: [UIView] = []
|
||||
private var buttons: [UIButton] = []
|
||||
private var stackView: UIStackView!
|
||||
private var seView: UIImageView!
|
||||
|
||||
var changeIndex: ((Int) -> ())?
|
||||
|
||||
override func draw(_ rect: CGRect) {
|
||||
super.draw(rect)
|
||||
|
||||
layer.cornerRadius = 8
|
||||
layer.backgroundColor = #colorLiteral(red: 0.9411764706, green: 0.9411764706, blue: 0.9529411765, alpha: 1).cgColor
|
||||
|
||||
for view in buttons {
|
||||
view.removeFromSuperview()
|
||||
}
|
||||
for view in views {
|
||||
view.removeFromSuperview()
|
||||
}
|
||||
for view in subviews {
|
||||
view.removeFromSuperview()
|
||||
}
|
||||
|
||||
let items = textItems.components(separatedBy: ",")
|
||||
let width = (self.frame.width / CGFloat(items.count))
|
||||
let frame = CGRect(x: (width * CGFloat(index)), y: 0, width: width, height: self.frame.height)
|
||||
|
||||
views = []
|
||||
buttons = []
|
||||
|
||||
for i in 0..<items.count {
|
||||
|
||||
let x = ((width * CGFloat(i+1)) - 0.5)
|
||||
|
||||
if (i+1) != items.count {
|
||||
let separatorView = UIView(frame: CGRect(x: x, y: 11, width: 1, height: self.frame.height - 22))
|
||||
separatorView.backgroundColor = #colorLiteral(red: 0.7411764706, green: 0.7411764706, blue: 0.7411764706, alpha: 1)
|
||||
separatorView.layer.cornerRadius = 0.5
|
||||
addSubview(separatorView)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var i = 0
|
||||
for oneText in items {
|
||||
let x = (width * CGFloat(i))
|
||||
|
||||
let view = UIView(frame: frame)
|
||||
view.set(x: x)
|
||||
let oneButton = UIButton(type: .system)
|
||||
oneButton.frame = view.bounds
|
||||
view.addSubview(oneButton)
|
||||
|
||||
/// Btn Size
|
||||
|
||||
view.backgroundColor = .clear
|
||||
oneButton.backgroundColor = .clear
|
||||
oneButton.style(selected: false)
|
||||
|
||||
/// Btn
|
||||
oneButton.setTitle(oneText , for: .normal)
|
||||
oneButton.contentVerticalAlignment = .center
|
||||
|
||||
if let font = UIFont(name: "TTNorms-Regular", size: 18) {
|
||||
oneButton.titleLabel?.font = font
|
||||
}
|
||||
|
||||
if i == index {
|
||||
oneButton.style(selected: true)
|
||||
}
|
||||
|
||||
oneButton.addTarget(self, action: #selector(buttonAction(_:)), for: .touchUpInside)
|
||||
|
||||
//oneButton.set(width: width)
|
||||
views.append(view)
|
||||
buttons.append(oneButton)
|
||||
//self.addSubview(oneButton)
|
||||
i += 1
|
||||
}
|
||||
|
||||
let stackView = UIStackView()
|
||||
stackView.axis = .horizontal
|
||||
stackView.distribution = .fillEqually
|
||||
|
||||
for btn in views {
|
||||
stackView.addArrangedSubview(btn)
|
||||
}
|
||||
|
||||
|
||||
seView = UIImageView(frame: frame)
|
||||
seView.selectedView()
|
||||
addSubview(seView)
|
||||
|
||||
stackView.translatesAutoresizingMaskIntoConstraints = false
|
||||
self.addSubview(stackView)
|
||||
|
||||
stackView.leftAnchor.constraint(equalTo: self.leftAnchor).isActive = true
|
||||
stackView.rightAnchor.constraint(equalTo: self.rightAnchor).isActive = true
|
||||
stackView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
|
||||
stackView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
|
||||
self.stackView = stackView
|
||||
}
|
||||
|
||||
@objc func buttonAction(_ button: UIButton) {
|
||||
|
||||
var i = 0
|
||||
for btn in buttons {
|
||||
btn.style(selected: false)
|
||||
|
||||
if btn == button {
|
||||
self.index = i
|
||||
changeIndex?(i)
|
||||
let x = (btn.frame.width * CGFloat(i))
|
||||
UIView.animate(withDuration: 0.35) {
|
||||
self.seView.set(x: x)
|
||||
}
|
||||
btn.style(selected: true)
|
||||
}
|
||||
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+200
@@ -0,0 +1,200 @@
|
||||
//
|
||||
// DesignViewExtentions.swift
|
||||
// FigmaConvertXib
|
||||
//
|
||||
// Created by Рустам Мотыгуллин on 20.07.2020.
|
||||
// Copyright © 2020 mrusta. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
extension UIView {
|
||||
func add(shadow radius: CGFloat, offset: CGSize, color: UIColor) {
|
||||
layer.shadowOpacity = 1.0
|
||||
layer.shadowColor = color.cgColor
|
||||
layer.shadowOffset = offset
|
||||
layer.shadowRadius = radius
|
||||
}
|
||||
|
||||
func add(border width: CGFloat, color: UIColor) {
|
||||
layer.borderColor = color.cgColor
|
||||
layer.borderWidth = width
|
||||
}
|
||||
|
||||
//MARK: - Blur
|
||||
|
||||
func add(blur: CGFloat, rect: CGRect? = nil, clearCallback: (() -> Void)? = nil) {
|
||||
|
||||
guard blur > 0 else { return }
|
||||
|
||||
layer.shadowOpacity = 0.0
|
||||
var fr = bounds
|
||||
if let rect = rect {
|
||||
fr = CGRect(x: 0, y: 0, width: rect.width, height: rect.height)
|
||||
}
|
||||
|
||||
UIGraphicsBeginImageContext(fr.size)
|
||||
layer.render(in: UIGraphicsGetCurrentContext()!)
|
||||
guard let imgC = UIGraphicsGetImageFromCurrentImageContext() else { return }
|
||||
UIGraphicsEndImageContext()
|
||||
|
||||
self.blur(screen: imgC, blur: blur, clearCallback: clearCallback)
|
||||
}
|
||||
|
||||
func blur(screen: UIImage, blur: CGFloat, clearCallback: (() -> Void)? = nil) {
|
||||
|
||||
guard let ciscreen: CIImage = CIImage(image: screen) else { return }
|
||||
|
||||
guard let filter: CIFilter = CIFilter(name: "CIGaussianBlur") else { return }
|
||||
filter.setDefaults()
|
||||
filter.setValue(ciscreen, forKey: kCIInputImageKey)
|
||||
filter.setValue(blur, forKey: kCIInputRadiusKey)
|
||||
|
||||
let bl2 = ((blur * 2) * -1)
|
||||
let bl4 = (blur * 4)
|
||||
|
||||
let ofW = (layer.shadowOffset.width * 1) * -1
|
||||
let ofH = (layer.shadowOffset.height * 1) * -1
|
||||
|
||||
let ofW2 = (layer.shadowOffset.width * 2)
|
||||
let ofH2 = (layer.shadowOffset.height * 2)
|
||||
|
||||
let contextFrame = CGRect(x: bl2 + ofW,
|
||||
y: bl2 + ofH,
|
||||
width: frame.width + bl4 + ofW2,
|
||||
height: frame.height + bl4 + ofH2)
|
||||
|
||||
let ciContext = CIContext(options: nil)
|
||||
guard let r = filter.value(forKey: kCIOutputImageKey) as? CIImage else { return }
|
||||
guard let cImg = ciContext.createCGImage(r, from: contextFrame) else { return }
|
||||
|
||||
let finalImage = UIImage(cgImage: cImg)
|
||||
|
||||
let imageFrame = CGRect(x: bl2 + ofW,
|
||||
y: bl2 + ofH,
|
||||
width: frame.width + bl4 + ofW2,
|
||||
height: frame.height + bl4 + ofH2)
|
||||
|
||||
let blurImageView = UIImageView(frame: imageFrame)
|
||||
blurImageView.image = finalImage
|
||||
blurImageView.contentMode = .scaleAspectFit
|
||||
|
||||
clearAllFills()
|
||||
|
||||
clearCallback?()
|
||||
|
||||
addSubview(blurImageView)
|
||||
|
||||
}
|
||||
|
||||
//MARK: - Blur Clear All
|
||||
|
||||
func clearAllFills() {
|
||||
|
||||
if let sublayers = layer.sublayers {
|
||||
for layer in sublayers {
|
||||
|
||||
layer.backgroundColor = UIColor.clear.cgColor
|
||||
if let gradientLayer = layer as? CAGradientLayer {
|
||||
gradientLayer.removeFromSuperlayer()
|
||||
} else if layer.name == "InnerShadow" {
|
||||
layer.removeFromSuperlayer()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layer.backgroundColor = UIColor.clear.cgColor
|
||||
|
||||
func clearShadow() {
|
||||
layer.shadowOffset = .zero
|
||||
layer.shadowOpacity = 0.0
|
||||
layer.shadowRadius = 0.0
|
||||
layer.shadowColor = UIColor.clear.cgColor
|
||||
}
|
||||
|
||||
func clearBorder() {
|
||||
layer.borderColor = UIColor.clear.cgColor
|
||||
layer.borderWidth = 0.0
|
||||
}
|
||||
|
||||
clearBorder()
|
||||
}
|
||||
|
||||
//MARK: - Inner Shadow
|
||||
|
||||
func addInnerShadow(color: UIColor,
|
||||
radius: CGFloat,
|
||||
offset: CGSize,
|
||||
cornerRadius: CGFloat,
|
||||
alpha: Float = 1.0) {
|
||||
|
||||
let cornerRadius = self.cornerRadius(cornerRadius)
|
||||
|
||||
let innerShadow = CALayer()
|
||||
innerShadow.name = "InnerShadow"
|
||||
innerShadow.frame = bounds
|
||||
|
||||
let path = UIBezierPath(roundedRect: innerShadow.bounds.insetBy(dx: -bounds.width, dy: -bounds.height), cornerRadius:cornerRadius)
|
||||
let cutout = UIBezierPath(roundedRect: innerShadow.bounds, cornerRadius:cornerRadius).reversing()
|
||||
|
||||
path.append(cutout)
|
||||
innerShadow.shadowPath = path.cgPath
|
||||
innerShadow.masksToBounds = true
|
||||
|
||||
innerShadow.shadowColor = color.cgColor
|
||||
innerShadow.shadowOffset = offset
|
||||
innerShadow.shadowRadius = radius
|
||||
innerShadow.cornerRadius = cornerRadius
|
||||
innerShadow.shadowOpacity = alpha
|
||||
layer.addSublayer(innerShadow)
|
||||
}
|
||||
|
||||
func add(gradient
|
||||
color1: UIColor? = nil,
|
||||
color2: UIColor? = nil,
|
||||
color3: UIColor? = nil,
|
||||
color4: UIColor? = nil,
|
||||
color5: UIColor? = nil,
|
||||
color6: UIColor? = nil,
|
||||
pointStart: CGPoint,
|
||||
pointEnd: CGPoint,
|
||||
cornerRadius: CGFloat) -> CAGradientLayer? {
|
||||
|
||||
if color1 != nil || color2 != nil || color3 != nil || color4 != nil || color5 != nil || color6 != nil {
|
||||
|
||||
var colors: [CGColor] = []
|
||||
// colors.append(fillColor.cgColor)
|
||||
if let color1 = color1 { colors.append(color1.cgColor) }
|
||||
if let color2 = color2 { colors.append(color2.cgColor) }
|
||||
if let color3 = color3 { colors.append(color3.cgColor) }
|
||||
if let color4 = color4 { colors.append(color4.cgColor) }
|
||||
if let color5 = color5 { colors.append(color5.cgColor) }
|
||||
if let color6 = color6 { colors.append(color6.cgColor) }
|
||||
|
||||
let glayer = CAGradientLayer()
|
||||
glayer.name = "Gradient"
|
||||
glayer.frame = bounds
|
||||
glayer.colors = colors
|
||||
glayer.startPoint = pointStart
|
||||
glayer.endPoint = pointEnd
|
||||
glayer.cornerRadius = self.cornerRadius(cornerRadius)
|
||||
layer.insertSublayer(glayer, at: 0)
|
||||
|
||||
layer.backgroundColor = UIColor.clear.cgColor
|
||||
|
||||
return glayer
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
//MARK: - Check CornerRadius
|
||||
|
||||
func cornerRadius(_ cornerRadius: CGFloat) -> CGFloat {
|
||||
let minSize = min(frame.size.width, frame.size.height)
|
||||
let minSizeRadius = (minSize / 2)
|
||||
let radius = ((cornerRadius < 0) || (minSizeRadius < cornerRadius) ? minSizeRadius : cornerRadius)
|
||||
return radius
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,23 +5,6 @@ import UIKit
|
||||
@IBDesignable
|
||||
class DesignButton: UIButton {
|
||||
|
||||
/*
|
||||
// @IBInspectable var cornerRadius: CGFloat = 0.0 {
|
||||
// didSet {
|
||||
// let minSize = min(frame.size.width, frame.size.height)
|
||||
// let radius = ((cornerRadius < 0) ? (minSize / 2) : cornerRadius)
|
||||
// self.layer.cornerRadius = radius
|
||||
// }
|
||||
// }
|
||||
|
||||
// UIGraphicsBeginImageContextWithOptions(glayer.frame.size, false, UIScreen.main.scale)
|
||||
// UIGraphicsBeginImageContext(glayer.frame.size)
|
||||
// glayer.render(in:UIGraphicsGetCurrentContext()!)
|
||||
// let image = UIGraphicsGetImageFromCurrentImageContext()
|
||||
// UIGraphicsEndImageContext()
|
||||
// setImage(image, for: .normal)
|
||||
*/
|
||||
|
||||
@IBInspectable var hideAnimation: Bool = true
|
||||
|
||||
@IBInspectable var fillColor: UIColor = .clear
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
//
|
||||
// Device.swift
|
||||
// Inferential Futures
|
||||
//
|
||||
// Created by Valentin on 22.10.2020.
|
||||
// Copyright © 2020 Valentin Titov. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
fileprivate let kIPad = "iPad"
|
||||
fileprivate let kIPhone = "iPhone"
|
||||
|
||||
enum DeviceType {
|
||||
case iPhoneSE, iPhoneMedium, iPhoneDefault, iPhoneMax, iPhoneX, iPhoneXMax, simulator, iPad97, iPad105, iPad129
|
||||
}
|
||||
|
||||
enum DeviceModel:String {
|
||||
case iPodTouch5, iPodTouch6,
|
||||
iPhone4, iPhone4s, iPhone5, iPhone5c, iPhone5s, iPhoneSE, iPhoneSE2ndGen,
|
||||
iPhone6, iPhone6s, iPhone7, iPhone8,
|
||||
iPhone6Plus, iPhone6sPlus, iPhone7Plus, iPhone8Plus, iPhoneX, iPhoneXS, iPhoneXSMax, iPhoneXR, iPhone11, iPhone11Pro, iPhone11ProMax,
|
||||
iPhone12Mini, iPhone12, iPhone12Pro, iPhone12ProMax,
|
||||
iPadMini, iPadMini2, iPadMini3, iPadMini4, iPadMini5,
|
||||
iPad2, iPad3, iPad4, iPadAir, iPadAir2, iPadPro, iPad5, iPadPro10, iPad2018,
|
||||
iPadPro12Inch, iPadPro12Inch2, iPadPro11,iPadPro12Inch3,
|
||||
appleTV, simulator, unknown
|
||||
}
|
||||
|
||||
open class Device {
|
||||
|
||||
// MARK: - Type
|
||||
|
||||
static public let isIpad = UIDevice.current.userInterfaceIdiom == .pad
|
||||
static public let isIphone = UIDevice.current.userInterfaceIdiom == .phone
|
||||
static public let isRetina = UIScreen.main.scale >= 2.0
|
||||
|
||||
class public var isBigIphone: Bool { get { (isIphone && ScreenSize.screenMax > 568.0) } }
|
||||
class public var isIphoneX: Bool { get { (isIphone && ScreenSize.screenMax > 736.0) } }
|
||||
class public var is12ProMax: Bool { get { (isIphone && ScreenSize.screenMax > 926.0) } }
|
||||
class public var SE: Bool { get { !isBigIphone } }
|
||||
|
||||
static public var isSimulator: Bool { return UIDevice.device == .simulator }
|
||||
}
|
||||
|
||||
extension UIDevice {
|
||||
|
||||
// MARK: - Device Model
|
||||
|
||||
static var modelName: DeviceModel {
|
||||
var systemInfo = utsname()
|
||||
uname(&systemInfo)
|
||||
let machineMirror = Mirror(reflecting: systemInfo.machine)
|
||||
let identifier = machineMirror.children.reduce(String.empty) { identifier, element in
|
||||
guard let value = element.value as? Int8 , value != 0 else { return identifier }
|
||||
return identifier + String(UnicodeScalar(UInt8(value)))
|
||||
}
|
||||
|
||||
switch identifier {
|
||||
case "iPod5,1": return .iPodTouch5
|
||||
case "iPod7,1": return .iPodTouch6
|
||||
case "iPhone3,1", "iPhone3,2", "iPhone3,3": return .iPhone4
|
||||
case "iPhone4,1": return .iPhone4s
|
||||
case "iPhone5,1", "iPhone5,2": return .iPhone5
|
||||
case "iPhone5,3", "iPhone5,4": return .iPhone5c
|
||||
case "iPhone6,1", "iPhone6,2": return .iPhone5s
|
||||
case "iPhone7,2": return .iPhone6
|
||||
case "iPhone7,1": return .iPhone6Plus
|
||||
case "iPhone8,1": return .iPhone6s
|
||||
case "iPhone8,2": return .iPhone6sPlus
|
||||
case "iPhone8,4": return .iPhoneSE
|
||||
case "iPhone9,1", "iPhone9,3": return .iPhone7
|
||||
case "iPhone9,2", "iPhone9,4": return .iPhone7Plus
|
||||
case "iPhone10,1", "iPhone10,3": return .iPhone8
|
||||
case "iPhone10,2", "iPhone10,4": return .iPhone8Plus
|
||||
case "iPhone10,5", "iPhone10,6": return .iPhoneX
|
||||
case "iPhone11,2": return .iPhoneXS
|
||||
case "iPhone11,4", "iPhone11,6": return .iPhoneXSMax
|
||||
case "iPhone11,8": return .iPhoneXR
|
||||
case "iPhone12,1": return .iPhone11
|
||||
case "iPhone12,3": return .iPhone11Pro
|
||||
case "iPhone12,5": return .iPhone11ProMax
|
||||
case "iPhone12,8": return .iPhoneSE2ndGen
|
||||
case "iPhone13,1": return .iPhone12Mini
|
||||
case "iPhone13,2": return .iPhone12
|
||||
case "iPhone13,3": return .iPhone12Pro
|
||||
case "iPhone13,4": return .iPhone12ProMax
|
||||
case "iPad2,1", "iPad2,2", "iPad2,3", "iPad2,4":return .iPad2
|
||||
case "iPad3,1", "iPad3,2", "iPad3,3": return .iPad3
|
||||
case "iPad3,4", "iPad3,5", "iPad3,6": return .iPad4
|
||||
case "iPad4,1", "iPad4,2", "iPad4,3": return .iPadAir
|
||||
case "iPad5,3", "iPad5,4": return .iPadAir2
|
||||
case "iPad2,5", "iPad2,6", "iPad2,7": return .iPadMini
|
||||
case "iPad4,4", "iPad4,5", "iPad4,6": return .iPadMini2
|
||||
case "iPad4,7", "iPad4,8", "iPad4,9": return .iPadMini3
|
||||
case "iPad5,1", "iPad5,2": return .iPadMini4
|
||||
case "iPad11,1", "iPad11,2": return .iPadMini5
|
||||
case "iPad6,3", "iPad6,4": return .iPadPro
|
||||
case "iPad6,7", "iPad6,8": return .iPadPro12Inch
|
||||
case "iPad6,11","iPad6,12": return .iPad5
|
||||
case "iPad7,1", "iPad7,2": return .iPadPro12Inch2
|
||||
case "iPad7,3", "iPad7,4": return .iPadPro10
|
||||
case "iPad7,5", "iPad7,6": return .iPad2018
|
||||
case "iPad8,1", "iPad8,2", "iPad8,3", "iPad8,4":return .iPadPro11
|
||||
case "iPad8,5", "iPad8,6", "iPad8,7", "iPad8,8":return .iPadPro12Inch3
|
||||
case "AppleTV1,1","AppleTV2,1","AppleTV3,1",
|
||||
"AppleTV3,2","AppleTV5,3","AppleTV6,2": return .appleTV
|
||||
case "i386", "x86_64", "arm64": return .simulator
|
||||
default: return .unknown
|
||||
}
|
||||
}
|
||||
|
||||
static var idiom: UIUserInterfaceIdiom {
|
||||
get { return UIDevice.current.userInterfaceIdiom }
|
||||
}
|
||||
|
||||
// MARK: - Device Type
|
||||
|
||||
static var device:DeviceType {
|
||||
switch UIDevice.current.userInterfaceIdiom {
|
||||
case .pad:
|
||||
let screenSize = UIScreen.main.bounds.size
|
||||
let height = max(screenSize.width, screenSize.height)
|
||||
switch height {
|
||||
case 1024: return .iPad97
|
||||
case 1112: return .iPad105
|
||||
case 1366: return .iPad129
|
||||
default: return .iPad97
|
||||
}
|
||||
case .phone:
|
||||
switch modelName {
|
||||
case .iPhone5,.iPhone5c,.iPhone5s,.iPhoneSE: return .iPhoneSE
|
||||
case .iPhone6, .iPhone7, .iPhone8, .iPhoneSE2ndGen, .iPhone12Mini: return .iPhoneMedium
|
||||
case .iPhone6Plus, .iPhone6sPlus, .iPhone7Plus, .iPhone8Plus: return .iPhoneMax
|
||||
case .iPhoneX, .iPhoneXS, .iPhone11Pro, .iPhone12Pro: return .iPhoneX
|
||||
case .iPhoneXR,.iPhoneXSMax, .iPhone11, .iPhone11ProMax, .iPhone12, .iPhone12ProMax: return .iPhoneXMax
|
||||
case .simulator : return .simulator
|
||||
default: return .iPhoneDefault
|
||||
}
|
||||
default: return .iPhoneDefault
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
//
|
||||
// Screen.swift
|
||||
// Realme
|
||||
//
|
||||
// Created by Valentin Titov on 04.01.2021.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
// Common screen constants
|
||||
var offset: CGFloat = 16
|
||||
|
||||
var lastNavBarHeight: CGFloat = 0
|
||||
|
||||
struct ScreenSize {
|
||||
static var size:CGSize { get { return UIScreen.main.bounds.size }}
|
||||
static var bounds:CGRect { get { return CGRect(origin: .zero, size: size) }}
|
||||
static var width:CGFloat { get { return size.width }}
|
||||
static var height:CGFloat { get { return size.height }}
|
||||
// static var aspectRatio: CGFloat { get { return size.aspectRatio }}
|
||||
static var screenMax:CGFloat { get { max(width, height) } }
|
||||
static var screenMin:CGFloat { get { min(width, height) } }
|
||||
|
||||
// MARK: - X Padding
|
||||
|
||||
static var isIphoneXTop: CGFloat { get { ( Device.isIphoneX ? 24.0 : 0.0) } }
|
||||
static var isIphoneXBottom: CGFloat { get { ( Device.isIphoneX ? 34.0 : 0.0) } }
|
||||
|
||||
}
|
||||
|
||||
extension UIViewController {
|
||||
|
||||
func getStatusBarHeight() -> CGFloat {
|
||||
var statusBarHeight: CGFloat = 0
|
||||
if #available(iOS 13.0, *) {
|
||||
let window = UIApplication.shared.windows.filter {$0.isKeyWindow}.first
|
||||
statusBarHeight = window?.windowScene?.statusBarManager?.statusBarFrame.height ?? 0
|
||||
} else {
|
||||
statusBarHeight = UIApplication.shared.statusBarFrame.height
|
||||
}
|
||||
return statusBarHeight
|
||||
}
|
||||
|
||||
var topbarHeight: CGFloat {
|
||||
|
||||
let statusHeight = getStatusBarHeight()
|
||||
let navHeight = self.navigationController?.navigationBar.frame.height ?? 0.0
|
||||
|
||||
let height: CGFloat = statusHeight + navHeight
|
||||
lastNavBarHeight = height
|
||||
|
||||
return height
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
//
|
||||
// UIView+Round.swift
|
||||
// PlusBank
|
||||
//
|
||||
// Created by Valentin Titov on 07.02.2021.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
//MARK: - Round
|
||||
extension UIView {
|
||||
|
||||
/**
|
||||
Rounds the given set of corners to the specified radius
|
||||
|
||||
- parameter corners: Corners to round
|
||||
- parameter radius: Radius to round to
|
||||
*/
|
||||
func roundRadius(corners: UIRectCorner,
|
||||
radius: CGFloat) {
|
||||
_ = _roundRadius(corners: corners, radius: radius)
|
||||
}
|
||||
|
||||
/**
|
||||
Rounds the given set of corners to the specified radius with a border
|
||||
- parameter corners: Corners to round
|
||||
- parameter radius: Radius to round to
|
||||
- parameter borderColor: The border color
|
||||
- parameter borderWidth: The border width
|
||||
*/
|
||||
func roundRadius(corners: UIRectCorner,
|
||||
radius: CGFloat,
|
||||
borderColor: UIColor,
|
||||
borderWidth: CGFloat) {
|
||||
let mask = _roundRadius(corners: corners, radius: radius)
|
||||
addBorder(mask: mask, borderColor: borderColor, borderWidth: borderWidth)
|
||||
}
|
||||
|
||||
/**
|
||||
Fully rounds an autolayout view (e.g. one with no known frame) with the given diameter and border
|
||||
|
||||
- parameter diameter: The view's diameter
|
||||
- parameter borderColor: The border color
|
||||
- parameter borderWidth: The border width
|
||||
*/
|
||||
func fullyRound(diameter: CGFloat,
|
||||
borderColor: UIColor,
|
||||
borderWidth: CGFloat) {
|
||||
layer.masksToBounds = true
|
||||
layer.cornerRadius = diameter / 2
|
||||
layer.borderWidth = borderWidth
|
||||
layer.borderColor = borderColor.cgColor;
|
||||
}
|
||||
|
||||
@discardableResult func _roundRadius(corners: UIRectCorner,
|
||||
radius: CGFloat) -> CAShapeLayer {
|
||||
let path = UIBezierPath(roundedRect: bounds,
|
||||
byRoundingCorners: corners,
|
||||
cornerRadii: CGSize(width: radius, height: radius))
|
||||
let mask = CAShapeLayer()
|
||||
mask.path = path.cgPath
|
||||
self.layer.mask = mask
|
||||
return mask
|
||||
}
|
||||
|
||||
func addBorder(mask: CAShapeLayer,
|
||||
borderColor: UIColor,
|
||||
borderWidth: CGFloat) {
|
||||
let borderLayer = CAShapeLayer()
|
||||
borderLayer.path = mask.path
|
||||
borderLayer.fillColor = UIColor.clear.cgColor
|
||||
borderLayer.strokeColor = borderColor.cgColor
|
||||
borderLayer.lineWidth = borderWidth
|
||||
borderLayer.frame = bounds
|
||||
layer.addSublayer(borderLayer)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// Alpha.swift
|
||||
// PlusBank
|
||||
//
|
||||
// Created by Valentin Titov on 05.02.2021.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
enum Alpha: CGFloat {
|
||||
case invisible = 0.0
|
||||
case halfTransluent = 0.5
|
||||
case visible = 1.0
|
||||
}
|
||||
|
||||
extension UIView {
|
||||
func setAlpha(_ alpha: Alpha) {
|
||||
self.alpha = alpha.rawValue
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// AutoresizingMask.swift
|
||||
// PlusBank
|
||||
//
|
||||
// Created by Рустам Мотыгуллин on 30.05.2021.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
extension UIView {
|
||||
|
||||
func maskAll() {
|
||||
self.autoresizingMask = [ .flexibleTopMargin,
|
||||
.flexibleBottomMargin,
|
||||
.flexibleLeftMargin,
|
||||
.flexibleRightMargin,
|
||||
.flexibleWidth,
|
||||
.flexibleHeight,]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
//
|
||||
// Blur.swift
|
||||
// PlusBank
|
||||
//
|
||||
// Created by Рустам Мотыгуллин on 21.05.2021.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Blurberry
|
||||
|
||||
protocol AppSceneProtocol {
|
||||
var window: UIWindow? { get set }
|
||||
}
|
||||
|
||||
private let overBlurTag: Int = 9036164
|
||||
|
||||
//MARK: - OverBlurView
|
||||
extension AppSceneProtocol {
|
||||
|
||||
public func appearOverBlurView() {
|
||||
|
||||
let effect = UIBlurEffect(style: .light)
|
||||
let view = UIVisualEffectView(effect: nil)
|
||||
view.blur.radius = 5.0
|
||||
view.blur.tintColor = .clear
|
||||
view.frame = window!.frame
|
||||
view.tag = overBlurTag
|
||||
|
||||
UIView.animate(withDuration: Speed.ms100.rawValue) {
|
||||
view.effect = effect
|
||||
}
|
||||
|
||||
self.window?.addSubview(view)
|
||||
}
|
||||
|
||||
public func dissappearOverBlurView() {
|
||||
|
||||
mainAsync(delay: Speed.ms100.rawValue) {
|
||||
|
||||
guard let view = self.window?.viewWithTag(overBlurTag) as? UIVisualEffectView else { return }
|
||||
|
||||
UIView.animate(withDuration: Speed.ms100.rawValue,
|
||||
animations: {
|
||||
view.effect = nil
|
||||
}, completion: { _ in
|
||||
view.removeFromSuperview()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
//
|
||||
// Bundle.swift
|
||||
// PlusBank
|
||||
//
|
||||
// Created by Valentin Titov on 02.03.2021.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension Bundle {
|
||||
static var appName: String {
|
||||
return Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") as? String ?? "KvantMobile"
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// BindableGestureRecognizer.swift
|
||||
// ButtonLayersClickStyle
|
||||
//
|
||||
// Created by Рустам Мотыгуллин on 09.03.2022.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
final class BindableGestureRecognizer: UITapGestureRecognizer {
|
||||
private var action: (UITapGestureRecognizer) -> Void
|
||||
|
||||
init(action: @escaping (UITapGestureRecognizer) -> Void) {
|
||||
self.action = action
|
||||
super.init(target: nil, action: nil)
|
||||
self.addTarget(self, action: #selector(execute(_:)))
|
||||
}
|
||||
|
||||
@objc private func execute(_ sender: UITapGestureRecognizer) {
|
||||
action(sender)
|
||||
}
|
||||
}
|
||||
|
||||
+253
@@ -0,0 +1,253 @@
|
||||
//
|
||||
// ButtonAnimationView.swift
|
||||
// ButtonLayersClickStyle
|
||||
//
|
||||
// Created by Рустам Мотыгуллин on 09.03.2022.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
extension CGFloat {
|
||||
static func random() -> CGFloat {
|
||||
return CGFloat(arc4random()) / CGFloat(UInt32.max)
|
||||
}
|
||||
}
|
||||
|
||||
extension UIColor {
|
||||
static func random() -> UIColor {
|
||||
return UIColor(
|
||||
red: .random(),
|
||||
green: .random(),
|
||||
blue: .random(),
|
||||
alpha: 1.0
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@IBDesignable
|
||||
class ButtonAnimationView: UIView {
|
||||
|
||||
@IBInspectable var cornerRadius: CGFloat = 0.0
|
||||
@IBInspectable var animationType: Int = 0
|
||||
var animationTypeValue: CGFloat?
|
||||
@IBInspectable var allSubviews: Bool = false
|
||||
@IBInspectable var startClick: Bool = false
|
||||
|
||||
|
||||
var addViews: [UIView]?
|
||||
|
||||
var setupDone = false
|
||||
|
||||
public var type: UIButton.TapType = .alpha(0.5)
|
||||
private var subviewsAnimation = false
|
||||
public var button: UIButton?
|
||||
// public var mainView: UIView?
|
||||
|
||||
|
||||
// override func draw(_ rect: CGRect) {
|
||||
// super.draw(rect)
|
||||
//
|
||||
// updateSubviews()
|
||||
// }
|
||||
|
||||
// MARK: - Initialize
|
||||
|
||||
required public init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
// updateSubviews()
|
||||
}
|
||||
|
||||
// public init() {
|
||||
// super.init(frame: .zero)
|
||||
// updateSubviews()
|
||||
// }
|
||||
|
||||
|
||||
|
||||
public init(
|
||||
frame: CGRect,
|
||||
radius: CGFloat = 0.0,
|
||||
animation: Int = 0,
|
||||
value: CGFloat? = nil,
|
||||
addViews: [UIView]? = nil,
|
||||
startClick: Bool = false
|
||||
) {
|
||||
super.init(frame: frame)
|
||||
self.cornerRadius = radius
|
||||
self.animationType = animation
|
||||
self.animationTypeValue = value
|
||||
self.addViews = addViews
|
||||
self.startClick = startClick
|
||||
// updateSubviews()
|
||||
}
|
||||
|
||||
|
||||
// MARK: - layout subViews Update
|
||||
|
||||
override func layoutSubviews() {
|
||||
updateSubviews()
|
||||
}
|
||||
|
||||
public func updateSubviews() {
|
||||
|
||||
if setupDone {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if button == nil {
|
||||
createButton()
|
||||
}
|
||||
// if animationType == 9 {
|
||||
// createMainViewCopy()
|
||||
// }
|
||||
|
||||
if animationType != 0 {
|
||||
|
||||
let type: UIButton.TapType = {
|
||||
switch animationType {
|
||||
case 1: return .alpha(animationTypeValue ?? 0.5)
|
||||
case 2: return .layerGray(animationTypeValue ?? 0.22)
|
||||
case 3: return .pulsate(new: false)
|
||||
case 4: return .pulsate(new: true)
|
||||
case 5: return .shake(new: false)
|
||||
case 6: return .shake(new: true)
|
||||
case 7: return .flash
|
||||
case 8: return .color(value: UIColor.random())
|
||||
case 9: return .androidClickable(dark: false)
|
||||
case 10: return .androidClickable(dark: true)
|
||||
default: return .alpha(0.5)
|
||||
}
|
||||
}()
|
||||
|
||||
setAnimation(type: type)
|
||||
|
||||
if startClick {
|
||||
startClick = false
|
||||
|
||||
let nviews = getViews()
|
||||
button?.onClick(views: nviews, radius: cornerRadius, type: type)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
setupDone = true
|
||||
}
|
||||
|
||||
func getType(value: CGFloat? = nil) -> UIButton.TapType {
|
||||
|
||||
let type: UIButton.TapType = {
|
||||
switch animationType {
|
||||
case 1: return .alpha(value ?? 0.5)
|
||||
case 2: return .layerGray(value ?? 0.22)
|
||||
case 3: return .pulsate(new: false)
|
||||
case 4: return .pulsate(new: true)
|
||||
case 5: return .shake(new: false)
|
||||
case 6: return .shake(new: true)
|
||||
case 7: return .flash
|
||||
case 8: return .color(value: UIColor.random())
|
||||
case 9: return .androidClickable(dark: false)
|
||||
case 10: return .androidClickable(dark: true)
|
||||
default: return .alpha(0.5)
|
||||
}
|
||||
}()
|
||||
return type
|
||||
}
|
||||
|
||||
public func onClick() {
|
||||
button?.onClick(views: getViews(), radius: cornerRadius, type: getType())
|
||||
}
|
||||
|
||||
// public func createMainViewCopy() {
|
||||
// let view = UIView(frame: bounds)
|
||||
// view.backgroundColor = .clear
|
||||
// view.layer.cornerRadius = cornerRadius
|
||||
// view.maskAll()
|
||||
// addSubview(view)
|
||||
// self.mainView = view
|
||||
// }
|
||||
|
||||
public func createButton() {
|
||||
var fr: CGRect = bounds
|
||||
if subviews.count != 0 {
|
||||
let mainView = subviews[0]
|
||||
fr = mainView.frame
|
||||
}
|
||||
|
||||
let btn = UIButton(type: .system)
|
||||
btn.frame = fr
|
||||
btn.backgroundColor = .clear
|
||||
btn.setTitle(nil, for: .normal)
|
||||
|
||||
// btn.contentVerticalAlignment = .center
|
||||
btn.layer.cornerRadius = cornerRadius
|
||||
btn.clipsToBounds = true
|
||||
// btn.maskAll()
|
||||
addSubview(btn)
|
||||
self.button = btn
|
||||
}
|
||||
|
||||
func getViews(views: [UIView]? = nil) -> [UIView] {
|
||||
|
||||
var nviews: [UIView] = []
|
||||
if let views = views {
|
||||
nviews = views
|
||||
} else {
|
||||
if allSubviews {
|
||||
nviews.append(self)
|
||||
subviews.forEach {
|
||||
nviews.append($0)
|
||||
}
|
||||
} else {
|
||||
|
||||
|
||||
|
||||
|
||||
if animationType == 8, let v = button {
|
||||
nviews.append(v)
|
||||
} else if ((animationType == 9) || (animationType == 10)), let v = button {
|
||||
nviews.append(v)
|
||||
} else if subviews.count != 0 {
|
||||
|
||||
if let addViews = addViews {
|
||||
|
||||
nviews = addViews
|
||||
|
||||
// if let tagsID = viewsTagsID {
|
||||
// let tags = tagsID.components(separatedBy: ",")
|
||||
// for str in tags {
|
||||
// let tagNumb = Int(str) ?? 0
|
||||
// for viww in subviews {
|
||||
// if viww.tag == tagNumb {
|
||||
// nviews.append(viww)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
} else {
|
||||
let mainView = subviews[0]
|
||||
nviews.append(mainView)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nviews
|
||||
}
|
||||
|
||||
public func setAnimation(type: UIButton.TapType, views: [UIView]? = nil) {
|
||||
self.type = type
|
||||
// updateSubviews()
|
||||
|
||||
layer.cornerRadius = cornerRadius
|
||||
|
||||
let nviews = getViews(views: views)
|
||||
|
||||
button?.tapHideAnimation(views: nviews, radius: cornerRadius, type: type, callback: { type in
|
||||
if type == .touchUpInside {
|
||||
|
||||
}
|
||||
})
|
||||
subviewsAnimation = true
|
||||
}
|
||||
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
//
|
||||
// PulseAnimation.swift
|
||||
// ButtonLayersClickStyle
|
||||
//
|
||||
// Created by Рустам Мотыгуллин on 09.03.2022.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class PulseAnimation: CALayer {
|
||||
|
||||
var animationGroup = CAAnimationGroup()
|
||||
var animationDuration: TimeInterval = 1.5
|
||||
var radius: CGFloat = 200
|
||||
var numebrOfPulse: Float = Float.infinity
|
||||
|
||||
override init(layer: Any) {
|
||||
super.init(layer: layer)
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
init(numberOfPulse: Float = Float.infinity, radius: CGFloat, postion: CGPoint){
|
||||
super.init()
|
||||
self.backgroundColor = UIColor.black.cgColor
|
||||
self.contentsScale = UIScreen.main.scale
|
||||
self.opacity = 0
|
||||
self.radius = radius
|
||||
self.numebrOfPulse = numberOfPulse
|
||||
self.position = postion
|
||||
|
||||
self.bounds = CGRect(x: 0, y: 0, width: radius*2, height: radius*2)
|
||||
self.cornerRadius = radius
|
||||
|
||||
DispatchQueue.global(qos: .default).async {
|
||||
self.setupAnimationGroup()
|
||||
DispatchQueue.main.async {
|
||||
self.add(self.animationGroup, forKey: "pulse")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func scaleAnimation() -> CABasicAnimation {
|
||||
let scaleAnimaton = CABasicAnimation(keyPath: "transform.scale.xy")
|
||||
scaleAnimaton.fromValue = NSNumber(value: 0)
|
||||
scaleAnimaton.toValue = NSNumber(value: 1)
|
||||
scaleAnimaton.duration = animationDuration
|
||||
return scaleAnimaton
|
||||
}
|
||||
|
||||
func createOpacityAnimation() -> CAKeyframeAnimation {
|
||||
let opacityAnimiation = CAKeyframeAnimation(keyPath: "opacity")
|
||||
opacityAnimiation.duration = animationDuration
|
||||
opacityAnimiation.values = [0.4,0.8,0]
|
||||
opacityAnimiation.keyTimes = [0,0.3,1]
|
||||
return opacityAnimiation
|
||||
}
|
||||
|
||||
func setupAnimationGroup() {
|
||||
self.animationGroup.duration = animationDuration
|
||||
self.animationGroup.repeatCount = numebrOfPulse
|
||||
let defaultCurve = CAMediaTimingFunction(name: CAMediaTimingFunctionName.default)
|
||||
self.animationGroup.timingFunction = defaultCurve
|
||||
self.animationGroup.animations = [scaleAnimation(),createOpacityAnimation()]
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+255
@@ -0,0 +1,255 @@
|
||||
//
|
||||
// UIButton+AllAnimations.swift
|
||||
// PlusBank
|
||||
//
|
||||
// Created by Рустам Мотыгуллин on 20.08.2021.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
// MARK: Animations
|
||||
|
||||
extension UIColor {
|
||||
var inverted: UIColor {
|
||||
var r: CGFloat = 0.0, g: CGFloat = 0.0, b: CGFloat = 0.0, a: CGFloat = 0.0
|
||||
self.getRed(&r, green: &g, blue: &b, alpha: &a)
|
||||
return UIColor(red: (1 - r), green: (1 - g), blue: (1 - b), alpha: a) // Assuming you want the same alpha value.
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - Button extension
|
||||
|
||||
extension UIButton {
|
||||
|
||||
// MARK: - Alpha
|
||||
|
||||
func updateChangeAlpha(with views: [UIView], visible: Alpha, value: CGFloat, duration: Speed?) {
|
||||
|
||||
var alpha: CGFloat = 1.0
|
||||
if visible != .visible {
|
||||
alpha = value
|
||||
}
|
||||
|
||||
if let duration = duration {
|
||||
UIView.animate(with: duration) {
|
||||
for view in views {
|
||||
view.alpha = alpha
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for view in views {
|
||||
view.alpha = alpha
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Layer Gray
|
||||
|
||||
func updateLayerGray(with views: [UIView], cornRadius: CGFloat? = nil, visible: Alpha, value: CGFloat, duration: Speed?) {
|
||||
|
||||
// let filterViews = views
|
||||
let filterViews = views.filter { !(($0 is UIButton) || ($0 is UILabel)) }
|
||||
|
||||
guard let mainView = filterViews.last else { return }
|
||||
let shTag = 31
|
||||
let spechialColor = UIColor(red: 10.0 / 255.0, green: 13.0 / 255.0, blue: 38.0 / 255.0, alpha: value)
|
||||
|
||||
if visible == .visible {
|
||||
|
||||
for v in mainView.subviews {
|
||||
|
||||
if v.tag == shTag {
|
||||
if let duration = duration {
|
||||
UIView.animate(
|
||||
with: duration,
|
||||
animations: {
|
||||
v.alpha = 0
|
||||
},
|
||||
completion: { fin in
|
||||
v.removeFromSuperview()
|
||||
}
|
||||
)
|
||||
} else {
|
||||
v.alpha = 0
|
||||
v.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if mainView.viewWithTag(shTag) == nil {
|
||||
|
||||
var radius = cornRadius ?? mainView.layer.cornerRadius
|
||||
if radius == 0 {
|
||||
for sv in mainView.subviews {
|
||||
if sv.layer.cornerRadius != 0 {
|
||||
radius = mainView.layer.cornerRadius
|
||||
}
|
||||
if let desFig = mainView as? DesignFigure {
|
||||
radius = desFig.cornerRadius
|
||||
if radius == -1 {
|
||||
let minSize = min(mainView.frame.width, mainView.bounds.height)
|
||||
radius = minSize / 2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if mainView.viewWithTag(shTag) != nil {
|
||||
return
|
||||
}
|
||||
|
||||
let shadowView = UIView(frame: mainView.bounds)
|
||||
shadowView.tag = shTag
|
||||
shadowView.backgroundColor = spechialColor
|
||||
shadowView.layer.cornerRadius = radius
|
||||
mainView.addSubview(shadowView)
|
||||
shadowView.alpha = ((duration == nil) ? 1 : 0)
|
||||
if let duration = duration {
|
||||
if duration == .zero {
|
||||
shadowView.alpha = 1
|
||||
} else {
|
||||
UIView.animate(with: duration) {
|
||||
shadowView.alpha = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - View extension
|
||||
|
||||
extension UIView {
|
||||
|
||||
// MARK: - Flash
|
||||
|
||||
func flash(duration: Speed = .ms200) {
|
||||
|
||||
let flash = CABasicAnimation(keyPath: "opacity")
|
||||
flash.duration = duration.rawValue
|
||||
flash.fromValue = 1
|
||||
flash.toValue = 0.1
|
||||
flash.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
|
||||
flash.autoreverses = true
|
||||
flash.repeatCount = 3
|
||||
|
||||
layer.add(flash, forKey: nil)
|
||||
}
|
||||
|
||||
|
||||
func inversionColor(duration: Speed = .ms300, cornRadius: CGFloat? = nil, value: UIColor) { // .s1
|
||||
|
||||
let v = UIView(frame: frame)
|
||||
v.backgroundColor = value.inverted
|
||||
v.layer.compositingFilter = "differenceBlendMode"
|
||||
addSubview(v)
|
||||
}
|
||||
|
||||
// MARK: - Change Color
|
||||
|
||||
func animationColor(duration: Speed = .ms300, cornRadius: CGFloat? = nil, value: UIColor) { // .s1
|
||||
|
||||
let color = CABasicAnimation(keyPath: "backgroundColor")
|
||||
let fromColor = backgroundColor ?? .clear
|
||||
color.fromValue = value.cgColor //UIColor.white.cgColor
|
||||
color.toValue = fromColor.cgColor
|
||||
color.duration = duration.rawValue
|
||||
color.beginTime = CACurrentMediaTime() + 0.1
|
||||
color.autoreverses = false
|
||||
|
||||
// if let r = cornRadius {
|
||||
// layer.cornerRadius = r
|
||||
// }
|
||||
|
||||
|
||||
layer.add(color, forKey: "animationColor")
|
||||
}
|
||||
|
||||
// MARK: - Pulsate
|
||||
|
||||
func pulsate(duration: Speed = .ms200) {
|
||||
|
||||
let pulse = CASpringAnimation(keyPath: "transform.scale")
|
||||
pulse.duration = duration.rawValue
|
||||
pulse.fromValue = 1.0
|
||||
pulse.toValue = 0.85
|
||||
pulse.autoreverses = true
|
||||
pulse.repeatCount = 2
|
||||
pulse.initialVelocity = 0.5
|
||||
|
||||
|
||||
layer.add(pulse, forKey: "pulse")
|
||||
}
|
||||
|
||||
// MARK: - Pulsate 2
|
||||
|
||||
func pulsateNew(visible: Alpha) {
|
||||
if visible == .visible {
|
||||
animate(self, transform: .identity)
|
||||
} else {
|
||||
animate(self, transform: CGAffineTransform.identity.scaledBy(x: 0.85, y: 0.85))
|
||||
}
|
||||
}
|
||||
|
||||
private func animate(_ view: UIView, transform: CGAffineTransform) {
|
||||
UIView.animate(withDuration: 0.4,
|
||||
delay: 0,
|
||||
usingSpringWithDamping: 0.5,
|
||||
initialSpringVelocity: 3,
|
||||
options: [.curveEaseInOut],
|
||||
animations: {
|
||||
view.transform = transform
|
||||
}, completion: nil)
|
||||
}
|
||||
|
||||
// MARK: - Shake
|
||||
|
||||
func shake() {
|
||||
let animation = CAKeyframeAnimation(keyPath: "transform.translation.x")
|
||||
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
|
||||
animation.duration = 0.6
|
||||
animation.values = [-20.0, 20.0, -20.0, 20.0, -10.0, 10.0, -5.0, 5.0, 0.0 ]
|
||||
layer.add(animation, forKey: "shake")
|
||||
}
|
||||
|
||||
// MARK: - Shake 2
|
||||
|
||||
func shakeNew(duration: Speed = .ms50) {
|
||||
|
||||
let shake = CABasicAnimation(keyPath: "position")
|
||||
shake.duration = duration.rawValue
|
||||
shake.repeatCount = 2
|
||||
shake.autoreverses = true
|
||||
|
||||
let fromPoint = CGPoint(x: center.x - 5, y: center.y)
|
||||
let fromValue = NSValue(cgPoint: fromPoint)
|
||||
|
||||
let toPoint = CGPoint(x: center.x + 5, y: center.y)
|
||||
let toValue = NSValue(cgPoint: toPoint)
|
||||
|
||||
shake.fromValue = fromValue
|
||||
shake.toValue = toValue
|
||||
|
||||
layer.add(shake, forKey: "position")
|
||||
}
|
||||
|
||||
// MARK: - Android Pulse
|
||||
|
||||
func androidPulseAnimation(duration: Speed = .ms1, dark: Bool, value: CGFloat? = nil, position: CGPoint) {
|
||||
|
||||
var color = dark ? #colorLiteral(red: 0, green: 0, blue: 0, alpha: 0.2786076018) : #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
|
||||
if let alpha = value {
|
||||
color = color.withAlphaComponent(alpha)
|
||||
}
|
||||
|
||||
let pulse = PulseAnimation(numberOfPulse: 1, radius: 200, postion: position)
|
||||
pulse.animationDuration = duration.rawValue
|
||||
pulse.backgroundColor = color.cgColor// #colorLiteral(red: 0.05282949957, green: 0.5737867104, blue: 1, alpha: 1)
|
||||
layer.insertSublayer(pulse, below: layer)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+206
@@ -0,0 +1,206 @@
|
||||
//
|
||||
// ViewAnimationSelect.swift
|
||||
// PlusBank
|
||||
//
|
||||
// Created by Рустам Мотыгуллин on 22.06.2021.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
extension UIButton {
|
||||
|
||||
// MARK: Tap Animation Type
|
||||
|
||||
static let setTag = 963
|
||||
|
||||
enum TapType {
|
||||
case alpha(_ value: CGFloat) // 0.5
|
||||
case layerGray(_ value: CGFloat = 0.1845) // 0.1845 add black shadow
|
||||
case pulsate(new: Bool = false)
|
||||
case shake(new: Bool = false)
|
||||
case flash
|
||||
case color(value: UIColor = .red)
|
||||
case androidClickable(dark: Bool = false)
|
||||
}
|
||||
|
||||
// MARK: Set
|
||||
|
||||
func tapHideAnimation(
|
||||
view: UIView?,
|
||||
type: TapType = .alpha(0.0),
|
||||
hideSpeed: Speed? = .ms100,
|
||||
callback: ((UIControl.Event) -> Void)? = nil
|
||||
) {
|
||||
guard let view = view else { return }
|
||||
tapHideAnimation(views: [view], type: type, callback: callback)
|
||||
}
|
||||
|
||||
func tapHideAnimation(
|
||||
views: [UIView],
|
||||
radius: CGFloat? = nil,
|
||||
type: TapType = .alpha(0.0),
|
||||
hideSpeed: Speed? = .ms100,
|
||||
callback: ((UIControl.Event) -> Void)? = nil
|
||||
) {
|
||||
|
||||
switch type {
|
||||
case .androidClickable(let dark): do {
|
||||
guard let mainView = views.last else { return }
|
||||
|
||||
var tapAdded: Bool = false
|
||||
gestureRecognizers?.forEach {
|
||||
if $0 is UITapGestureRecognizer {
|
||||
tapAdded = true
|
||||
}
|
||||
}
|
||||
if !tapAdded {
|
||||
let tap = BindableGestureRecognizer { [weak self] sender in
|
||||
|
||||
let touchPoint = sender.location(in: mainView)
|
||||
|
||||
guard let dur: Speed = self?.defaultDuration(type: .androidClickable(dark: dark)) else { return }
|
||||
self?.androidPulseAnimation(duration: dur, dark: dark, position: touchPoint)
|
||||
|
||||
}
|
||||
mainView.addGestureRecognizer(tap)
|
||||
}
|
||||
}
|
||||
default: break
|
||||
}
|
||||
|
||||
|
||||
// if tag != UIButton.setTag { return }
|
||||
// tag = UIButton.setTag
|
||||
|
||||
tap(for: .touchDown) { [weak self] in
|
||||
callback?(.touchDown)
|
||||
Logs.add(".touchDown")
|
||||
self?.hideAnimation(with: views, radius: radius, type: type, event: .touchDown, duration: hideSpeed)
|
||||
}
|
||||
tap(for: .touchUpInside) { [weak self] in
|
||||
callback?(.touchUpInside)
|
||||
Logs.add(".touchUpInside")
|
||||
self?.hideAnimation(with: views, radius: radius, visible: .visible, type: type, event: .touchUpInside)
|
||||
}
|
||||
tap(for: .touchUpOutside) { [weak self] in
|
||||
callback?(.touchUpOutside)
|
||||
Logs.add(".touchUpOutside")
|
||||
self?.hideAnimation(with: views, radius: radius, visible: .visible, type: type, event: .touchUpOutside)
|
||||
}
|
||||
tap(for: .touchCancel) { [weak self] in
|
||||
callback?(.touchCancel)
|
||||
Logs.add(".touchCancel")
|
||||
self?.hideAnimation(with: views, radius: radius, visible: .visible, type: type, event: .touchCancel)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - onClick
|
||||
|
||||
func onClick(
|
||||
views: [UIView],
|
||||
radius: CGFloat? = nil,
|
||||
type: TapType = .alpha(0.0),
|
||||
hideSpeed: Speed? = .ms100,
|
||||
callback: ((UIControl.Event) -> Void)? = nil
|
||||
) {
|
||||
main(delay: 0.1) { [weak self] in
|
||||
self?.hideAnimation(with: views, radius: radius, type: type, event: .touchDown, duration: hideSpeed)
|
||||
main(delay: 0.35) { [weak self] in
|
||||
self?.hideAnimation(with: views, radius: radius, visible: .visible, type: type, event: .touchUpInside)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@objc func handleTap(_ sender: UITapGestureRecognizer? = nil) {
|
||||
guard let touchPoint = sender?.location(in: superview) else { return }
|
||||
|
||||
let dur: Speed = defaultDuration(type: .androidClickable(dark: false))
|
||||
androidPulseAnimation(duration: dur, dark: false, position: touchPoint)
|
||||
}
|
||||
|
||||
// MARK: - De-Select
|
||||
|
||||
func deselected(_ views: [UIView]) {
|
||||
hideAnimation(
|
||||
with: views,
|
||||
visible: .visible
|
||||
)
|
||||
}
|
||||
|
||||
func defaultDuration(type: TapType) -> Speed {
|
||||
switch type {
|
||||
case .alpha(_), .layerGray(_): return .ms300
|
||||
case .pulsate(_), .flash: return .ms200
|
||||
case .shake(_): return .ms50
|
||||
case .color(_): return .ms300
|
||||
case .androidClickable(_): return .s1
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Animation Filter
|
||||
|
||||
func hideAnimation(
|
||||
with views: [UIView],
|
||||
radius: CGFloat? = nil,
|
||||
visible: Alpha = .invisible,
|
||||
type: TapType = .alpha(0.0),
|
||||
event: UIControl.Event = .touchUpInside,
|
||||
duration: Speed? = nil
|
||||
) {
|
||||
|
||||
let dur: Speed = defaultDuration(type: type)
|
||||
switch type {
|
||||
case .alpha(let value):
|
||||
updateChangeAlpha(with: views, visible: visible, value: value, duration: dur)
|
||||
case .layerGray(let value):
|
||||
if event == .touchDown {
|
||||
updateLayerGray(with: views, cornRadius: radius, visible: .invisible, value: value, duration: nil)
|
||||
} else {
|
||||
updateLayerGray(with: views, cornRadius: radius, visible: .visible, value: value, duration: dur)
|
||||
}
|
||||
default: break
|
||||
}
|
||||
|
||||
views.forEach {
|
||||
switch type {
|
||||
case .flash: $0.flash(duration: dur)
|
||||
case .color(let value):
|
||||
if event == .touchDown
|
||||
,$0 is UIButton
|
||||
// ,!(($0 is UIButton) || ($0 is UILabel))
|
||||
{
|
||||
$0.animationColor(duration: dur, cornRadius: radius, value: value)
|
||||
}
|
||||
case .pulsate(let new):
|
||||
if new { $0.pulsateNew(visible: visible) }
|
||||
else { if event == .touchDown { $0.pulsate(duration: dur) } }
|
||||
case .shake(let new):
|
||||
if new { $0.shake() }
|
||||
else { $0.shakeNew(duration: dur) }
|
||||
// case .androidClickable:
|
||||
// androidPulseAnimation(duration: dur)
|
||||
default: break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension UIControl.Event {
|
||||
|
||||
func strName() -> String {
|
||||
return enevtName(self)
|
||||
}
|
||||
|
||||
func enevtName(_ event: UIControl.Event) -> String {
|
||||
switch event {
|
||||
case .touchDown: return "touch.Down"
|
||||
case .touchCancel: return "touch.Cancel"
|
||||
case .touchUpOutside: return "touch.Up.Outside"
|
||||
case .touchUpInside: return "touch.Up.Inside"
|
||||
default: return ""
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
//
|
||||
// CALayer+Animate.swift
|
||||
// PlusBank
|
||||
//
|
||||
// Created by Valentin Titov on 04.02.2021.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
extension CALayer {
|
||||
|
||||
public func animate() -> CALayerAnimate {
|
||||
return CALayerAnimate(layer: self)
|
||||
}
|
||||
}
|
||||
|
||||
public class CALayerAnimate {
|
||||
|
||||
private var animations: [String: CAAnimation]
|
||||
private var duration: CFTimeInterval
|
||||
let layer: CALayer
|
||||
|
||||
init(layer: CALayer) {
|
||||
self.animations = [String: CAAnimation]()
|
||||
self.duration = 0.25 // second
|
||||
self.layer = layer
|
||||
}
|
||||
|
||||
public func shadowOpacity(shadowOpacity: Float) -> CALayerAnimate {
|
||||
let key = "shadowOpacity"
|
||||
let animation = CABasicAnimation(keyPath: key)
|
||||
animation.fromValue = layer.shadowOpacity
|
||||
animation.toValue = shadowOpacity
|
||||
animation.isRemovedOnCompletion = false
|
||||
animation.fillMode = .forwards
|
||||
animations[key] = animation
|
||||
return self
|
||||
}
|
||||
|
||||
public func shadowRadius(shadowRadius: CGFloat) -> CALayerAnimate {
|
||||
let key = "shadowRadius"
|
||||
let animation = CABasicAnimation(keyPath: key)
|
||||
animation.fromValue = layer.shadowRadius
|
||||
animation.toValue = shadowRadius
|
||||
animation.isRemovedOnCompletion = false
|
||||
animation.fillMode = CAMediaTimingFillMode.forwards
|
||||
animations[key] = animation
|
||||
return self
|
||||
}
|
||||
|
||||
public func shadowOffsetX(shadowOffsetX: CGFloat) -> CALayerAnimate {
|
||||
let key = "shadowOffset"
|
||||
|
||||
var toValue: CGSize
|
||||
if let currentAnimation = animations[key] as? CABasicAnimation, let currentValue = currentAnimation.toValue {
|
||||
toValue = (currentValue as AnyObject).cgSizeValue
|
||||
} else {
|
||||
toValue = .zero
|
||||
}
|
||||
toValue.width = shadowOffsetX
|
||||
|
||||
let animation = CABasicAnimation(keyPath: key)
|
||||
animation.fromValue = NSValue(cgSize: layer.shadowOffset)
|
||||
animation.toValue = NSValue(cgSize: toValue)
|
||||
animation.isRemovedOnCompletion = false
|
||||
animation.fillMode = CAMediaTimingFillMode.forwards
|
||||
animations[key] = animation
|
||||
return self
|
||||
}
|
||||
|
||||
public func shadowOffsetY(shadowOffsetY: CGFloat) -> CALayerAnimate {
|
||||
let key = "shadowOffset"
|
||||
|
||||
var toValue: CGSize
|
||||
if let currentAnimation = animations[key] as? CABasicAnimation, let currentValue = currentAnimation.toValue {
|
||||
toValue = (currentValue as AnyObject).cgSizeValue
|
||||
} else {
|
||||
toValue = .zero
|
||||
}
|
||||
toValue.height = shadowOffsetY
|
||||
|
||||
let animation = CABasicAnimation(keyPath: key)
|
||||
animation.fromValue = NSValue(cgSize: layer.shadowOffset)
|
||||
animation.toValue = NSValue(cgSize: toValue)
|
||||
animation.isRemovedOnCompletion = false
|
||||
animation.fillMode = CAMediaTimingFillMode.forwards
|
||||
animations[key] = animation
|
||||
return self
|
||||
}
|
||||
|
||||
public func duration(duration: CFTimeInterval) -> CALayerAnimate {
|
||||
self.duration = duration
|
||||
return self
|
||||
}
|
||||
|
||||
public func start() {
|
||||
for (key, animation) in animations {
|
||||
animation.duration = duration
|
||||
layer.removeAnimation(forKey: key)
|
||||
layer.add(animation, forKey: key)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// CallPhone.swift
|
||||
// PlusBank
|
||||
//
|
||||
// Created by Рустам Мотыгуллин on 24.05.2021.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
class CallPhone {
|
||||
|
||||
class func call(phoneNumber:String) {
|
||||
let cleanPhoneNumber = phoneNumber.components(separatedBy: CharacterSet.decimalDigits.inverted).joined(separator: "")
|
||||
let urlString:String = "tel://\(cleanPhoneNumber)"
|
||||
if let phoneCallURL = URL(string: urlString) {
|
||||
if (UIApplication.shared.canOpenURL(phoneCallURL)) {
|
||||
UIApplication.shared.open(phoneCallURL, options: [:], completionHandler: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// Cell.swift
|
||||
// FigmaConvertXib
|
||||
//
|
||||
// Created by Рустам Мотыгуллин on 08.08.2020.
|
||||
// Copyright © 2020 mrusta. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
extension UITableViewCell {
|
||||
|
||||
func separator(hide: Bool) {
|
||||
separatorInset = UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: hide ? CGFloat(Double.greatestFiniteMagnitude) : 0.0)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// Colors.swift
|
||||
// Dieta
|
||||
//
|
||||
// Created by Рустам Мотыгуллин on 23.01.2022.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
|
||||
extension UIColor {
|
||||
|
||||
// 16진수로 컬러값 세팅
|
||||
convenience init(hex: Int, alpha: CGFloat = 1.0) {
|
||||
let newRed = CGFloat((hex >> 16) & 0xff) / 255
|
||||
let newGreen = CGFloat((hex >> 08) & 0xff) / 255
|
||||
let newBlue = CGFloat((hex >> 00) & 0xff) / 255
|
||||
|
||||
self.init(red:newRed, green:newGreen, blue:newBlue, alpha:alpha)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
//
|
||||
// Date.swift
|
||||
// CarLoan
|
||||
//
|
||||
// Created by Valentin Titov on 28/11/2019.
|
||||
// Copyright © 2019 Valentin Titov. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
enum DateFormatType: String {
|
||||
case shortSlash = "dd/MM/yyyy"
|
||||
case shortDot = "dd.MM.yyyy"
|
||||
case shortHyphen = "yyyy-MM-dd"
|
||||
case utc = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
|
||||
case dayMonth = "d MMMM"
|
||||
}
|
||||
|
||||
extension Date {
|
||||
func formatted(by type: DateFormatType) -> String {
|
||||
return toString(with: type.rawValue)
|
||||
}
|
||||
|
||||
func toString(with dateFormat: String, ru: Bool = false) -> String {
|
||||
let formatter = DateFormatter()
|
||||
if ru { formatter.locale = Locale(identifier: "ru_RU") }
|
||||
formatter.dateFormat = dateFormat
|
||||
return formatter.string(from: self)
|
||||
}
|
||||
}
|
||||
|
||||
extension String {
|
||||
func localDateAsString(with dateFormat: DateFormatType = .shortHyphen) -> String {
|
||||
if let date = localDate(dateFormat: dateFormat) {
|
||||
return date.toString(with: dateFormat.rawValue)
|
||||
}
|
||||
return .empty
|
||||
}
|
||||
|
||||
func localDate(dateFormat: DateFormatType = .shortHyphen) -> Date? {
|
||||
let dateFormatter = DateFormatter()
|
||||
dateFormatter.dateFormat = dateFormat.rawValue
|
||||
dateFormatter.timeZone = TimeZone.init(abbreviation: "UTC")
|
||||
let date = dateFormatter.date(from: self)
|
||||
return date
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
//
|
||||
// Device.swift
|
||||
// Inferential Futures
|
||||
//
|
||||
// Created by Valentin on 22.10.2020.
|
||||
// Copyright © 2020 Valentin Titov. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
fileprivate let kIPad = "iPad"
|
||||
fileprivate let kIPhone = "iPhone"
|
||||
|
||||
enum DeviceType {
|
||||
case iPhoneSE, iPhoneMedium, iPhoneDefault, iPhoneMax, iPhoneX, iPhoneXMax, simulator, iPad97, iPad105, iPad129
|
||||
}
|
||||
|
||||
enum DeviceModel:String {
|
||||
case iPodTouch5, iPodTouch6,
|
||||
iPhone4, iPhone4s, iPhone5, iPhone5c, iPhone5s, iPhoneSE, iPhoneSE2ndGen,
|
||||
iPhone6, iPhone6s, iPhone7, iPhone8,
|
||||
iPhone6Plus, iPhone6sPlus, iPhone7Plus, iPhone8Plus, iPhoneX, iPhoneXS, iPhoneXSMax, iPhoneXR, iPhone11, iPhone11Pro, iPhone11ProMax,
|
||||
iPhone12Mini, iPhone12, iPhone12Pro, iPhone12ProMax,
|
||||
iPadMini, iPadMini2, iPadMini3, iPadMini4, iPadMini5,
|
||||
iPad2, iPad3, iPad4, iPadAir, iPadAir2, iPadPro, iPad5, iPadPro10, iPad2018,
|
||||
iPadPro12Inch, iPadPro12Inch2, iPadPro11,iPadPro12Inch3,
|
||||
appleTV, simulator, unknown
|
||||
}
|
||||
|
||||
open class Device {
|
||||
|
||||
// MARK: - Type
|
||||
|
||||
static public let isIpad = UIDevice.current.userInterfaceIdiom == .pad
|
||||
static public let isIphone = UIDevice.current.userInterfaceIdiom == .phone
|
||||
static public let isRetina = UIScreen.main.scale >= 2.0
|
||||
|
||||
class public var isBigIphone: Bool { get { (isIphone && ScreenSize.screenMax > 568.0) } }
|
||||
class public var isIphoneX: Bool { get { (isIphone && ScreenSize.screenMax > 736.0) } }
|
||||
class public var is12ProMax: Bool { get { (isIphone && ScreenSize.screenMax > 926.0) } }
|
||||
class public var SE: Bool { get { !isBigIphone } }
|
||||
|
||||
static public var isSimulator: Bool { return UIDevice.device == .simulator }
|
||||
}
|
||||
|
||||
extension UIDevice {
|
||||
|
||||
// MARK: - Device Model
|
||||
|
||||
static var modelName: DeviceModel {
|
||||
var systemInfo = utsname()
|
||||
uname(&systemInfo)
|
||||
let machineMirror = Mirror(reflecting: systemInfo.machine)
|
||||
let identifier = machineMirror.children.reduce(String.empty) { identifier, element in
|
||||
guard let value = element.value as? Int8 , value != 0 else { return identifier }
|
||||
return identifier + String(UnicodeScalar(UInt8(value)))
|
||||
}
|
||||
|
||||
switch identifier {
|
||||
case "iPod5,1": return .iPodTouch5
|
||||
case "iPod7,1": return .iPodTouch6
|
||||
case "iPhone3,1", "iPhone3,2", "iPhone3,3": return .iPhone4
|
||||
case "iPhone4,1": return .iPhone4s
|
||||
case "iPhone5,1", "iPhone5,2": return .iPhone5
|
||||
case "iPhone5,3", "iPhone5,4": return .iPhone5c
|
||||
case "iPhone6,1", "iPhone6,2": return .iPhone5s
|
||||
case "iPhone7,2": return .iPhone6
|
||||
case "iPhone7,1": return .iPhone6Plus
|
||||
case "iPhone8,1": return .iPhone6s
|
||||
case "iPhone8,2": return .iPhone6sPlus
|
||||
case "iPhone8,4": return .iPhoneSE
|
||||
case "iPhone9,1", "iPhone9,3": return .iPhone7
|
||||
case "iPhone9,2", "iPhone9,4": return .iPhone7Plus
|
||||
case "iPhone10,1", "iPhone10,3": return .iPhone8
|
||||
case "iPhone10,2", "iPhone10,4": return .iPhone8Plus
|
||||
case "iPhone10,5", "iPhone10,6": return .iPhoneX
|
||||
case "iPhone11,2": return .iPhoneXS
|
||||
case "iPhone11,4", "iPhone11,6": return .iPhoneXSMax
|
||||
case "iPhone11,8": return .iPhoneXR
|
||||
case "iPhone12,1": return .iPhone11
|
||||
case "iPhone12,3": return .iPhone11Pro
|
||||
case "iPhone12,5": return .iPhone11ProMax
|
||||
case "iPhone12,8": return .iPhoneSE2ndGen
|
||||
case "iPhone13,1": return .iPhone12Mini
|
||||
case "iPhone13,2": return .iPhone12
|
||||
case "iPhone13,3": return .iPhone12Pro
|
||||
case "iPhone13,4": return .iPhone12ProMax
|
||||
case "iPad2,1", "iPad2,2", "iPad2,3", "iPad2,4":return .iPad2
|
||||
case "iPad3,1", "iPad3,2", "iPad3,3": return .iPad3
|
||||
case "iPad3,4", "iPad3,5", "iPad3,6": return .iPad4
|
||||
case "iPad4,1", "iPad4,2", "iPad4,3": return .iPadAir
|
||||
case "iPad5,3", "iPad5,4": return .iPadAir2
|
||||
case "iPad2,5", "iPad2,6", "iPad2,7": return .iPadMini
|
||||
case "iPad4,4", "iPad4,5", "iPad4,6": return .iPadMini2
|
||||
case "iPad4,7", "iPad4,8", "iPad4,9": return .iPadMini3
|
||||
case "iPad5,1", "iPad5,2": return .iPadMini4
|
||||
case "iPad11,1", "iPad11,2": return .iPadMini5
|
||||
case "iPad6,3", "iPad6,4": return .iPadPro
|
||||
case "iPad6,7", "iPad6,8": return .iPadPro12Inch
|
||||
case "iPad6,11","iPad6,12": return .iPad5
|
||||
case "iPad7,1", "iPad7,2": return .iPadPro12Inch2
|
||||
case "iPad7,3", "iPad7,4": return .iPadPro10
|
||||
case "iPad7,5", "iPad7,6": return .iPad2018
|
||||
case "iPad8,1", "iPad8,2", "iPad8,3", "iPad8,4":return .iPadPro11
|
||||
case "iPad8,5", "iPad8,6", "iPad8,7", "iPad8,8":return .iPadPro12Inch3
|
||||
case "AppleTV1,1","AppleTV2,1","AppleTV3,1",
|
||||
"AppleTV3,2","AppleTV5,3","AppleTV6,2": return .appleTV
|
||||
case "i386", "x86_64", "arm64": return .simulator
|
||||
default: return .unknown
|
||||
}
|
||||
}
|
||||
|
||||
static var idiom: UIUserInterfaceIdiom {
|
||||
get { return UIDevice.current.userInterfaceIdiom }
|
||||
}
|
||||
|
||||
// MARK: - Device Type
|
||||
|
||||
static var device:DeviceType {
|
||||
switch UIDevice.current.userInterfaceIdiom {
|
||||
case .pad:
|
||||
let screenSize = UIScreen.main.bounds.size
|
||||
let height = max(screenSize.width, screenSize.height)
|
||||
switch height {
|
||||
case 1024: return .iPad97
|
||||
case 1112: return .iPad105
|
||||
case 1366: return .iPad129
|
||||
default: return .iPad97
|
||||
}
|
||||
case .phone:
|
||||
switch modelName {
|
||||
case .iPhone5,.iPhone5c,.iPhone5s,.iPhoneSE: return .iPhoneSE
|
||||
case .iPhone6, .iPhone7, .iPhone8, .iPhoneSE2ndGen, .iPhone12Mini: return .iPhoneMedium
|
||||
case .iPhone6Plus, .iPhone6sPlus, .iPhone7Plus, .iPhone8Plus: return .iPhoneMax
|
||||
case .iPhoneX, .iPhoneXS, .iPhone11Pro, .iPhone12Pro: return .iPhoneX
|
||||
case .iPhoneXR,.iPhoneXSMax, .iPhone11, .iPhone11ProMax, .iPhone12, .iPhone12ProMax: return .iPhoneXMax
|
||||
case .simulator : return .simulator
|
||||
default: return .iPhoneDefault
|
||||
}
|
||||
default: return .iPhoneDefault
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
//
|
||||
// Screen.swift
|
||||
// Realme
|
||||
//
|
||||
// Created by Valentin Titov on 04.01.2021.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
// Common screen constants
|
||||
var offset: CGFloat = 16
|
||||
|
||||
var lastNavBarHeight: CGFloat = 0
|
||||
|
||||
struct ScreenSize {
|
||||
static var size:CGSize { get { return UIScreen.main.bounds.size }}
|
||||
static var bounds:CGRect { get { return CGRect(origin: .zero, size: size) }}
|
||||
static var width:CGFloat { get { return size.width }}
|
||||
static var height:CGFloat { get { return size.height }}
|
||||
static var aspectRatio: CGFloat { get { return size.aspectRatio }}
|
||||
static var screenMax:CGFloat { get { max(width, height) } }
|
||||
static var screenMin:CGFloat { get { min(width, height) } }
|
||||
|
||||
// MARK: - X Padding
|
||||
|
||||
static var isIphoneXTop: CGFloat { get { ( Device.isIphoneX ? 24.0 : 0.0) } }
|
||||
static var isIphoneXBottom: CGFloat { get { ( Device.isIphoneX ? 34.0 : 0.0) } }
|
||||
|
||||
}
|
||||
|
||||
extension UIViewController {
|
||||
|
||||
func getStatusBarHeight() -> CGFloat {
|
||||
var statusBarHeight: CGFloat = 0
|
||||
if #available(iOS 13.0, *) {
|
||||
let window = UIApplication.shared.windows.filter {$0.isKeyWindow}.first
|
||||
statusBarHeight = window?.windowScene?.statusBarManager?.statusBarFrame.height ?? 0
|
||||
} else {
|
||||
statusBarHeight = UIApplication.shared.statusBarFrame.height
|
||||
}
|
||||
return statusBarHeight
|
||||
}
|
||||
|
||||
var topbarHeight: CGFloat {
|
||||
|
||||
let statusHeight = getStatusBarHeight()
|
||||
let navHeight = self.navigationController?.navigationBar.frame.height ?? 0.0
|
||||
|
||||
let height: CGFloat = statusHeight + navHeight
|
||||
lastNavBarHeight = height
|
||||
|
||||
return height
|
||||
}
|
||||
}
|
||||
|
||||
+248
@@ -0,0 +1,248 @@
|
||||
//
|
||||
// UIView+Round.swift
|
||||
// PlusBank
|
||||
//
|
||||
// Created by Valentin Titov on 07.02.2021.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
//MARK: - Round
|
||||
extension UIView {
|
||||
|
||||
func set(x:CGFloat) {
|
||||
var frame:CGRect = self.frame
|
||||
frame.origin.x = x
|
||||
self.frame = frame
|
||||
}
|
||||
|
||||
func set(y:CGFloat) {
|
||||
var frame:CGRect = self.frame
|
||||
frame.origin.y = y
|
||||
self.frame = frame
|
||||
}
|
||||
|
||||
func set(width:CGFloat) {
|
||||
var frame:CGRect = self.frame
|
||||
frame.size.width = width
|
||||
self.frame = frame
|
||||
}
|
||||
|
||||
func set(height:CGFloat) {
|
||||
var frame:CGRect = self.frame
|
||||
frame.size.height = height
|
||||
self.frame = frame
|
||||
}
|
||||
}
|
||||
|
||||
extension CALayer {
|
||||
|
||||
func set(x:CGFloat) {
|
||||
var frame:CGRect = self.frame
|
||||
frame.origin.x = x
|
||||
self.frame = frame
|
||||
}
|
||||
|
||||
func set(y:CGFloat) {
|
||||
var frame:CGRect = self.frame
|
||||
frame.origin.y = y
|
||||
self.frame = frame
|
||||
}
|
||||
|
||||
func set(width:CGFloat) {
|
||||
var frame:CGRect = self.frame
|
||||
frame.size.width = width
|
||||
self.frame = frame
|
||||
}
|
||||
|
||||
func set(height:CGFloat) {
|
||||
var frame:CGRect = self.frame
|
||||
frame.size.height = height
|
||||
self.frame = frame
|
||||
}
|
||||
}
|
||||
|
||||
public extension UIView {
|
||||
|
||||
// MARK: - Basic Properties
|
||||
|
||||
/// X Axis value of UIView.
|
||||
var x: CGFloat {
|
||||
set { self.frame = CGRect(x: _pixelIntegral(newValue),
|
||||
y: self.y,
|
||||
width: self.width,
|
||||
height: self.height)
|
||||
}
|
||||
get { return self.frame.origin.x }
|
||||
}
|
||||
|
||||
/// Y Axis value of UIView.
|
||||
var y: CGFloat {
|
||||
set { self.frame = CGRect(x: self.x,
|
||||
y: _pixelIntegral(newValue),
|
||||
width: self.width,
|
||||
height: self.height)
|
||||
}
|
||||
get { return self.frame.origin.y }
|
||||
}
|
||||
|
||||
/// Width of view.
|
||||
var width: CGFloat {
|
||||
set { self.frame = CGRect(x: self.x,
|
||||
y: self.y,
|
||||
width: _pixelIntegral(newValue),
|
||||
height: self.height)
|
||||
}
|
||||
get { return self.frame.size.width }
|
||||
}
|
||||
|
||||
/// Height of view.
|
||||
var height: CGFloat {
|
||||
set { self.frame = CGRect(x: self.x,
|
||||
y: self.y,
|
||||
width: self.width,
|
||||
height: _pixelIntegral(newValue))
|
||||
}
|
||||
get { return self.frame.size.height }
|
||||
}
|
||||
|
||||
// MARK: - Origin and Size
|
||||
|
||||
/// View's Origin point.
|
||||
var origin: CGPoint {
|
||||
set { self.frame = CGRect(x: _pixelIntegral(newValue.x),
|
||||
y: _pixelIntegral(newValue.y),
|
||||
width: self.width,
|
||||
height: self.height)
|
||||
}
|
||||
get { return self.frame.origin }
|
||||
}
|
||||
|
||||
/// View's size.
|
||||
var size: CGSize {
|
||||
set { self.frame = CGRect(x: self.x,
|
||||
y: self.y,
|
||||
width: _pixelIntegral(newValue.width),
|
||||
height: _pixelIntegral(newValue.height))
|
||||
}
|
||||
get { return self.frame.size }
|
||||
}
|
||||
|
||||
// MARK: - Extra Properties
|
||||
|
||||
/// View's right side (x + width).
|
||||
var right: CGFloat {
|
||||
set { self.x = newValue - self.width }
|
||||
get { return self.x + self.width }
|
||||
}
|
||||
|
||||
/// View's bottom (y + height).
|
||||
var bottom: CGFloat {
|
||||
set { self.y = newValue - self.height }
|
||||
get { return self.y + self.height }
|
||||
}
|
||||
|
||||
/// View's top (y).
|
||||
var top: CGFloat {
|
||||
set { self.y = newValue }
|
||||
get { return self.y }
|
||||
}
|
||||
|
||||
/// View's left side (x).
|
||||
var left: CGFloat {
|
||||
set { self.x = newValue }
|
||||
get { return self.x }
|
||||
}
|
||||
|
||||
/// View's center X value (center.x).
|
||||
var centerX: CGFloat {
|
||||
set { self.center = CGPoint(x: newValue, y: self.centerY) }
|
||||
get { return self.center.x }
|
||||
}
|
||||
|
||||
/// View's center Y value (center.y).
|
||||
var centerY: CGFloat {
|
||||
set { self.center = CGPoint(x: self.centerX, y: newValue) }
|
||||
get { return self.center.y }
|
||||
}
|
||||
|
||||
/// Last subview on X Axis.
|
||||
var lastSubviewOnX: UIView? {
|
||||
return self.subviews.reduce(UIView(frame: .zero)) {
|
||||
return $1.x > $0.x ? $1 : $0
|
||||
}
|
||||
}
|
||||
|
||||
/// Last subview on Y Axis.
|
||||
var lastSubviewOnY: UIView? {
|
||||
return self.subviews.reduce(UIView(frame: .zero)) {
|
||||
return $1.y > $0.y ? $1 : $0
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Bounds Methods
|
||||
|
||||
/// X value of bounds (bounds.origin.x).
|
||||
var boundsX: CGFloat {
|
||||
set { self.bounds = CGRect(x: _pixelIntegral(newValue),
|
||||
y: self.boundsY,
|
||||
width: self.boundsWidth,
|
||||
height: self.boundsHeight)
|
||||
}
|
||||
get { return self.bounds.origin.x }
|
||||
}
|
||||
|
||||
/// Y value of bounds (bounds.origin.y).
|
||||
var boundsY: CGFloat {
|
||||
set { self.frame = CGRect(x: self.boundsX,
|
||||
y: _pixelIntegral(newValue),
|
||||
width: self.boundsWidth,
|
||||
height: self.boundsHeight)
|
||||
}
|
||||
get { return self.bounds.origin.y }
|
||||
}
|
||||
|
||||
/// Width of bounds (bounds.size.width).
|
||||
var boundsWidth: CGFloat {
|
||||
set { self.frame = CGRect(x: self.boundsX,
|
||||
y: self.boundsY,
|
||||
width: _pixelIntegral(newValue),
|
||||
height: self.boundsHeight)
|
||||
}
|
||||
get { return self.bounds.size.width }
|
||||
}
|
||||
|
||||
/// Height of bounds (bounds.size.height).
|
||||
var boundsHeight: CGFloat {
|
||||
set { self.frame = CGRect(x: self.boundsX,
|
||||
y: self.boundsY,
|
||||
width: self.boundsWidth,
|
||||
height: _pixelIntegral(newValue))
|
||||
}
|
||||
get { return self.bounds.size.height }
|
||||
}
|
||||
|
||||
// MARK: - Useful Methods
|
||||
|
||||
/// Center view to it's parent view.
|
||||
// func centerToParent() {
|
||||
// guard let superview = self.superview else { return }
|
||||
//
|
||||
// switch UIApplication.shared.statusBarOrientation {
|
||||
// case .landscapeLeft, .landscapeRight:
|
||||
// self.origin = CGPoint(x: (superview.height / 2) - (self.width / 2),
|
||||
// y: (superview.width / 2) - (self.height / 2))
|
||||
// case .portrait, .portraitUpsideDown:
|
||||
// self.origin = CGPoint(x: (superview.width / 2) - (self.width / 2),
|
||||
// y: (superview.height / 2) - (self.height / 2))
|
||||
// case .unknown:
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
|
||||
// MARK: - Private Methods
|
||||
fileprivate func _pixelIntegral(_ pointValue: CGFloat) -> CGFloat {
|
||||
let scale = UIScreen.main.scale
|
||||
return (round(pointValue * scale) / scale)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
//
|
||||
// UIView+Round.swift
|
||||
// PlusBank
|
||||
//
|
||||
// Created by Valentin Titov on 07.02.2021.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
//MARK: - Round
|
||||
extension UIView {
|
||||
|
||||
/**
|
||||
Rounds the given set of corners to the specified radius
|
||||
|
||||
- parameter corners: Corners to round
|
||||
- parameter radius: Radius to round to
|
||||
*/
|
||||
func roundRadius(corners: UIRectCorner,
|
||||
radius: CGFloat) {
|
||||
_ = _roundRadius(corners: corners, radius: radius)
|
||||
}
|
||||
|
||||
/**
|
||||
Rounds the given set of corners to the specified radius with a border
|
||||
- parameter corners: Corners to round
|
||||
- parameter radius: Radius to round to
|
||||
- parameter borderColor: The border color
|
||||
- parameter borderWidth: The border width
|
||||
*/
|
||||
func roundRadius(corners: UIRectCorner,
|
||||
radius: CGFloat,
|
||||
borderColor: UIColor,
|
||||
borderWidth: CGFloat) {
|
||||
let mask = _roundRadius(corners: corners, radius: radius)
|
||||
addBorder(mask: mask, borderColor: borderColor, borderWidth: borderWidth)
|
||||
}
|
||||
|
||||
/**
|
||||
Fully rounds an autolayout view (e.g. one with no known frame) with the given diameter and border
|
||||
|
||||
- parameter diameter: The view's diameter
|
||||
- parameter borderColor: The border color
|
||||
- parameter borderWidth: The border width
|
||||
*/
|
||||
func fullyRound(diameter: CGFloat,
|
||||
borderColor: UIColor,
|
||||
borderWidth: CGFloat) {
|
||||
layer.masksToBounds = true
|
||||
layer.cornerRadius = diameter / 2
|
||||
layer.borderWidth = borderWidth
|
||||
layer.borderColor = borderColor.cgColor;
|
||||
}
|
||||
|
||||
@discardableResult func _roundRadius(corners: UIRectCorner,
|
||||
radius: CGFloat) -> CAShapeLayer {
|
||||
let path = UIBezierPath(roundedRect: bounds,
|
||||
byRoundingCorners: corners,
|
||||
cornerRadii: CGSize(width: radius, height: radius))
|
||||
let mask = CAShapeLayer()
|
||||
mask.path = path.cgPath
|
||||
self.layer.mask = mask
|
||||
return mask
|
||||
}
|
||||
|
||||
func addBorder(mask: CAShapeLayer,
|
||||
borderColor: UIColor,
|
||||
borderWidth: CGFloat) {
|
||||
let borderLayer = CAShapeLayer()
|
||||
borderLayer.path = mask.path
|
||||
borderLayer.fillColor = UIColor.clear.cgColor
|
||||
borderLayer.strokeColor = borderColor.cgColor
|
||||
borderLayer.lineWidth = borderWidth
|
||||
borderLayer.frame = bounds
|
||||
layer.addSublayer(borderLayer)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
|
||||
|
||||
import Foundation
|
||||
|
||||
public func background(work: @escaping () -> Void) {
|
||||
DispatchQueue.global(qos: .userInitiated).async {
|
||||
work()
|
||||
}
|
||||
}
|
||||
|
||||
public func main(work: @escaping () -> Void) {
|
||||
DispatchQueue.main.async {
|
||||
work()
|
||||
}
|
||||
}
|
||||
|
||||
public func mainAsync(delay: Double, work: @escaping () -> Void) {
|
||||
main(delay: delay, work: work)
|
||||
}
|
||||
|
||||
public func main(delay: Double, work: @escaping () -> Void) {
|
||||
let deadline = DispatchTime.now() + delay
|
||||
DispatchQueue.main.asyncAfter(deadline: deadline) {
|
||||
work()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// TextField.swift
|
||||
// PlusBank
|
||||
//
|
||||
// Created by Рустам Мотыгуллин on 17.02.2021.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
extension UITextField {
|
||||
@IBInspectable var placeHolderColor: UIColor? {
|
||||
get {
|
||||
return self.placeHolderColor
|
||||
}
|
||||
set {
|
||||
self.attributedPlaceholder = NSAttributedString(string:self.placeholder != nil ? self.placeholder! : "", attributes:[NSAttributedString.Key.foregroundColor: newValue!])
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
//
|
||||
// Jailbreak.swift
|
||||
// PlusBank
|
||||
//
|
||||
// Created by Рустам Мотыгуллин on 20.05.2021.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
class JailbreakCheck {
|
||||
|
||||
public static func jailbroken() -> Bool {
|
||||
guard let cydiaUrlScheme = URL(string: "cydia://package/com.example.package") else { return isJailbroken() }
|
||||
return UIApplication.shared.canOpenURL(cydiaUrlScheme) || isJailbroken()
|
||||
}
|
||||
|
||||
static func isJailbroken() -> Bool {
|
||||
|
||||
if Device.isSimulator {
|
||||
return false
|
||||
}
|
||||
|
||||
let fileManager = FileManager.default
|
||||
if fileManager.fileExists(atPath: "/Applications/Cydia.app") ||
|
||||
fileManager.fileExists(atPath: "/Library/MobileSubstrate/MobileSubstrate.dylib") ||
|
||||
fileManager.fileExists(atPath: "/bin/bash") ||
|
||||
fileManager.fileExists(atPath: "/usr/sbin/sshd") ||
|
||||
fileManager.fileExists(atPath: "/etc/apt") ||
|
||||
fileManager.fileExists(atPath: "/usr/bin/ssh") {
|
||||
return true
|
||||
}
|
||||
|
||||
if canOpen("/Applications/Cydia.app") ||
|
||||
canOpen("/Library/MobileSubstrate/MobileSubstrate.dylib") ||
|
||||
canOpen("/bin/bash") ||
|
||||
canOpen("/usr/sbin/sshd") ||
|
||||
canOpen("/etc/apt") ||
|
||||
canOpen("/usr/bin/ssh") {
|
||||
return true
|
||||
}
|
||||
|
||||
let path = "/private/" + UUID().uuidString
|
||||
do {
|
||||
try "anyString".write(toFile: path, atomically: true, encoding: .utf8)
|
||||
try fileManager.removeItem(atPath: path)
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
static func canOpen(_ path: String) -> Bool {
|
||||
let file = fopen(path, "r")
|
||||
guard file != nil else { return false }
|
||||
fclose(file)
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
//
|
||||
// LayoutConstraint.swift
|
||||
// PlusBank
|
||||
//
|
||||
// Created by Рустам Мотыгуллин on 30.05.2021.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
|
||||
extension NSLayoutConstraint {
|
||||
/**
|
||||
Change multiplier constraint
|
||||
|
||||
- parameter multiplier: CGFloat
|
||||
- returns: NSLayoutConstraint
|
||||
*/
|
||||
func multiplier(_ value: CGFloat) -> NSLayoutConstraint {
|
||||
|
||||
guard let firstItem = firstItem else { return self }
|
||||
|
||||
NSLayoutConstraint.deactivate([self])
|
||||
|
||||
let newConstraint = NSLayoutConstraint(
|
||||
item: firstItem,
|
||||
attribute: firstAttribute,
|
||||
relatedBy: relation,
|
||||
toItem: secondItem,
|
||||
attribute: secondAttribute,
|
||||
multiplier: value,
|
||||
constant: constant
|
||||
)
|
||||
|
||||
newConstraint.priority = priority
|
||||
newConstraint.shouldBeArchived = self.shouldBeArchived
|
||||
newConstraint.identifier = self.identifier
|
||||
|
||||
NSLayoutConstraint.activate([newConstraint])
|
||||
return newConstraint
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
//
|
||||
// NSData+JsonRu.swift
|
||||
// PlusBank
|
||||
//
|
||||
// Created by Рустам Мотыгуллин on 08.06.2021.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
// MARK: - Convert Json RU
|
||||
|
||||
func convertJsonRU(to data: Data, encoding: String.Encoding = .utf8) -> String {
|
||||
|
||||
guard let temp: String = String(data: data as Data, encoding: encoding) else { return "" }
|
||||
|
||||
let arr = Array(temp)
|
||||
|
||||
var tab = 0
|
||||
var findLeft = false
|
||||
var findRight = false
|
||||
var findNote = false
|
||||
// var array = false
|
||||
|
||||
var result: [Character] = []
|
||||
let count = arr.count
|
||||
let countMinus = count-1
|
||||
for i in 0..<count {
|
||||
|
||||
let charr = arr[i]
|
||||
var nextChar: Character = " "
|
||||
let y = (i + 1)
|
||||
if y < count {
|
||||
nextChar = arr[y]
|
||||
}
|
||||
|
||||
if (charr != "\"") {
|
||||
|
||||
if ((charr == "{") && (i == 0)) ||
|
||||
((charr == "{") && (nextChar == "\"")) ||
|
||||
((charr == "[") && (nextChar == "\"")) ||
|
||||
((charr == "[") && (nextChar == "{" )) ||
|
||||
((charr == "[") && (nextChar == "]" )) {
|
||||
tab += 1
|
||||
findLeft = true
|
||||
result.append(charr)
|
||||
if (charr == "[") {
|
||||
// array = true
|
||||
result += (" " + "🔢 " + "A" + "r" + "r" + "a" + "y")
|
||||
}
|
||||
}
|
||||
else if ((charr == "}") && (i == countMinus)) ||
|
||||
((charr == "}") && (nextChar == ",")) ||
|
||||
((charr == "}") && (nextChar == "}")) ||
|
||||
((charr == "}") && (nextChar == "]")) ||
|
||||
((charr == "]") && (nextChar == ",")) ||
|
||||
((charr == "]") && (nextChar == "}")) {
|
||||
if 0 < tab {
|
||||
tab -= 1
|
||||
}
|
||||
findRight = true
|
||||
if (charr == "]") {
|
||||
// array = false
|
||||
}
|
||||
}
|
||||
else if (charr == ",") {
|
||||
findNote = true
|
||||
result.append(charr)
|
||||
} else if (charr == ":") {
|
||||
result.append(" ")
|
||||
result.append(charr)
|
||||
result.append(" ")
|
||||
//result.append("👉")
|
||||
// result.append("〰️")
|
||||
// result.append("〰️")
|
||||
// result.append("➰")
|
||||
} else {
|
||||
result.append(charr)
|
||||
}
|
||||
|
||||
if findLeft || findRight || findNote {
|
||||
|
||||
let tabb = " "
|
||||
var t = ""
|
||||
for _ in 0..<tab {
|
||||
t += tabb
|
||||
}
|
||||
|
||||
result.append("🔴")
|
||||
for _ in 0..<t.count {
|
||||
result.append(" ")
|
||||
}
|
||||
// result.append("|")
|
||||
|
||||
findLeft = false
|
||||
findNote = false
|
||||
|
||||
if findRight {
|
||||
result.append(charr)
|
||||
findRight = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var str = String(result)
|
||||
str = str.replacingOccurrences(of: "🔴", with: "\n | ")
|
||||
str = str.replacingOccurrences(of: "false", with: " ❌")
|
||||
str = str.replacingOccurrences(of: "true", with: " ✅")
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// Bool.swift
|
||||
// PlusBank
|
||||
//
|
||||
// Created by Valentin Titov on 03.03.2021.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
extension Bool {
|
||||
static func from(string: String) -> Bool {
|
||||
if string == "true" { return true }
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
extension Bool: Stringable {
|
||||
var stringValue: String {
|
||||
if self { return "true" }
|
||||
return "false"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
//
|
||||
// Decimal.swift
|
||||
// PlusBank
|
||||
//
|
||||
// Created by Valentin Titov on 03.03.2021.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension Decimal {
|
||||
var intValue: Int {
|
||||
return (self as NSDecimalNumber).intValue
|
||||
}
|
||||
|
||||
var doubleValue: Double {
|
||||
return (self as NSDecimalNumber).doubleValue
|
||||
}
|
||||
|
||||
func formatWith(currency: CurrencyType) -> String {
|
||||
currencyFormatted(currency)
|
||||
}
|
||||
}
|
||||
|
||||
extension Decimal {
|
||||
|
||||
func currencyFormatted(_ currency: CurrencyType) -> String {
|
||||
let value = self * 100.0
|
||||
let floatValue = (value as NSDecimalNumber).floatValue
|
||||
let intValue = Int(floatValue)
|
||||
var stringValue: String = String(intValue)
|
||||
while stringValue.count < 3 {
|
||||
stringValue.insert("0", at: stringValue.startIndex)
|
||||
}
|
||||
var result: String = currency.symbol
|
||||
result.insert(" ", at: result.startIndex)
|
||||
var cents: String = String(stringValue.removeLast())
|
||||
cents.insert(stringValue.removeLast(), at: cents.startIndex)
|
||||
result = cents + result
|
||||
result.insert(",", at: result.startIndex)
|
||||
let characters: [Character] = Array(stringValue) as [Character]
|
||||
var j = 1
|
||||
for i in (0..<characters.count).reversed() {
|
||||
let character = characters[i]
|
||||
result.insert(character, at: result.startIndex)
|
||||
if j%3 == 0, i != 0 {result.insert(" ", at: result.startIndex)}
|
||||
j += 1
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
extension String {
|
||||
var decimalValue: Decimal? {
|
||||
let formattedStr =
|
||||
replacingOccurrences(of: " ", with: "")
|
||||
.replacingOccurrences(of: ",", with: ".")
|
||||
return Decimal(string: formattedStr)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// Double.swift
|
||||
// PlusBank
|
||||
//
|
||||
// Created by Valentin Titov on 03.03.2021.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension Double {
|
||||
func decimalValue() -> Decimal? {
|
||||
return Decimal(self)
|
||||
}
|
||||
|
||||
func currencyFormatted(_ currency: CurrencyType) -> String {
|
||||
let intValue = Int(self * 100.0)
|
||||
var stringValue: String = String(intValue)
|
||||
while stringValue.count < 3 {
|
||||
stringValue.insert("0", at: stringValue.startIndex)
|
||||
}
|
||||
var result: String = currency.symbol
|
||||
result.insert(" ", at: result.startIndex)
|
||||
var cents: String = String(stringValue.removeLast())
|
||||
cents.insert(stringValue.removeLast(), at: cents.startIndex)
|
||||
result = cents + result
|
||||
result.insert(",", at: result.startIndex)
|
||||
let characters: [Character] = Array(stringValue) as [Character]
|
||||
var j = 1
|
||||
for i in (0..<characters.count).reversed() {
|
||||
let character = characters[i]
|
||||
result.insert(character, at: result.startIndex)
|
||||
if j%3 == 0, i != 0 {result.insert(" ", at: result.startIndex)}
|
||||
j += 1
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
//
|
||||
// Int.swift
|
||||
// PlusBank
|
||||
//
|
||||
// Created by Valentin Titov on 03.03.2021.
|
||||
//
|
||||
|
||||
import CoreGraphics
|
||||
|
||||
extension Int {
|
||||
static var zero: Int { get {return 0 }}
|
||||
mutating func next() { self += 1 }
|
||||
mutating func increase() { self += 1 }
|
||||
mutating func previous() { self -= 1 }
|
||||
mutating func decrease() { self -= 1 }
|
||||
|
||||
func percentToCGFloat() -> CGFloat {
|
||||
return CGFloat(self)/CGFloat(100.0)
|
||||
}
|
||||
}
|
||||
|
||||
extension UInt8 {
|
||||
public init(_ boolean: BooleanLiteralType) {
|
||||
self = boolean ? 1 : 0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
//
|
||||
// Number.swift
|
||||
// PlusBank
|
||||
//
|
||||
// Created by Valentin Titov on 03.02.2021.
|
||||
//
|
||||
|
||||
import CoreGraphics
|
||||
|
||||
extension CGFloat {
|
||||
static var zero: CGFloat { get {return 0.0 }}
|
||||
var half:CGFloat { get { return self/2 } }
|
||||
var double:CGFloat { get { return self*2 } }
|
||||
var toPercent: CGFloat { get { return self*100.0 } }
|
||||
var syncSize: CGFloat { get { return self * sizeAspect.rawValue }}
|
||||
var syncFont: CGFloat { get { return self * fontAspect.rawValue }}
|
||||
|
||||
//Pi Constants
|
||||
static let π = CGFloat(Double.pi)
|
||||
static let π2 = CGFloat(2 * Double.pi)
|
||||
static let π_2 = CGFloat(Double.pi/2)
|
||||
|
||||
var radians: CGFloat {
|
||||
return (self*CGFloat.pi)/180.0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// ScrollView.swift
|
||||
// PlusBank
|
||||
//
|
||||
// Created by Рустам Мотыгуллин on 07.04.2021.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
extension UIScrollView {
|
||||
|
||||
public func scrollingBottom(animated: Bool = true) {
|
||||
let deadline = DispatchTime.now() + 0.5
|
||||
DispatchQueue.main.asyncAfter(deadline: deadline) {
|
||||
let bottomOffset = CGPoint(x: 0, y: self.contentSize.height - self.bounds.height + self.contentInset.bottom)
|
||||
self.setContentOffset(bottomOffset, animated: animated)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension UITextView {
|
||||
|
||||
func scrollTextToBottom() {
|
||||
if text.count != 0 {
|
||||
let a = (text.count - 1)
|
||||
let bottom: NSRange = NSRange(location: a, length: 1) // a..<1
|
||||
scrollRangeToVisible(bottom)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// SendMail.swift
|
||||
// PlusBank
|
||||
//
|
||||
// Created by Рустам Мотыгуллин on 08.06.2021.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import MessageUI
|
||||
import SwiftLog
|
||||
|
||||
extension UIViewController {
|
||||
|
||||
func sendEmailLogs() {
|
||||
|
||||
if MFMailComposeViewController.canSendMail() {
|
||||
let composeVC = MFMailComposeViewController()
|
||||
composeVC.mailComposeDelegate = self
|
||||
|
||||
// Configure the fields of the interface.
|
||||
composeVC.setToRecipients(["rustamburger@gmail.com"])
|
||||
composeVC.setSubject("Send Email Logs")
|
||||
composeVC.setMessageBody("", isHTML: false)
|
||||
|
||||
var attachmentData: NSData?
|
||||
|
||||
let path = Log.logger.currentPath
|
||||
if FileManager.default.fileExists(atPath: path) {
|
||||
if let data = NSData(contentsOfFile: path) {
|
||||
attachmentData = data
|
||||
}
|
||||
}
|
||||
|
||||
guard let attData = attachmentData as Data? else { return }
|
||||
composeVC.addAttachmentData(attData, mimeType: "text/plain", fileName: "crash.log")
|
||||
self.present(composeVC, animated: true, completion: nil)
|
||||
|
||||
} else {
|
||||
// Tell user about not able to send email directly.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension UIViewController: MFMailComposeViewControllerDelegate {
|
||||
public func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
|
||||
controller.dismiss(animated: true)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// Sequances.swift
|
||||
// PB Dev
|
||||
//
|
||||
// Created by Valentin on 04.03.2020.
|
||||
// Copyright © 2020 Plus Bank. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension Dictionary {
|
||||
mutating func merge(dict: [Key: Value]){
|
||||
for (k, v) in dict {
|
||||
updateValue(v, forKey: k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Sequence where Element: Numeric {
|
||||
/// Returns the sum of all elements in the collection
|
||||
func sum() -> Element { return reduce(0, +) }
|
||||
}
|
||||
@@ -0,0 +1,340 @@
|
||||
//
|
||||
// String.swift
|
||||
// PlusBank
|
||||
//
|
||||
// Created by Valentin Titov on 02.02.2021.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
protocol Stringable {
|
||||
var stringValue: String { get }
|
||||
}
|
||||
|
||||
protocol OptionalStringable {
|
||||
var stringValue: String? { get }
|
||||
}
|
||||
|
||||
//MARK: - Constants
|
||||
extension String {
|
||||
static let empty: String = ""
|
||||
static let whitespace: String = " "
|
||||
static let slash: String = "/"
|
||||
static let newLine: String = "\n"
|
||||
static let endLine: String = "\r\n"
|
||||
static let colon: String = ":"
|
||||
static let semicolon: String = ";"
|
||||
static let equal: String = "="
|
||||
static let and: String = "&"
|
||||
}
|
||||
|
||||
extension String {
|
||||
func substringInRange(_ range: NSRange) -> String {
|
||||
let range =
|
||||
index(startIndex, offsetBy: range.location)
|
||||
..<
|
||||
index(startIndex, offsetBy: range.location + range.length)
|
||||
return String(self[range])
|
||||
}
|
||||
|
||||
func replaceStringInRange(
|
||||
_ range: NSRange,
|
||||
with string: String
|
||||
) -> String {
|
||||
return (self as NSString)
|
||||
.replacingCharacters(in: range, with: string)
|
||||
.trimmingCharacters(in: .whitespaces) as String
|
||||
}
|
||||
}
|
||||
|
||||
extension String {
|
||||
func index(from: Int) -> Index {
|
||||
return self.index(startIndex, offsetBy: from)
|
||||
}
|
||||
|
||||
func substring(from: Int) -> String {
|
||||
let fromIndex = index(from: from)
|
||||
return String(self[fromIndex...])
|
||||
}
|
||||
|
||||
func substring(to: Int) -> String {
|
||||
let toIndex = index(from: to)
|
||||
return String(self[..<toIndex])
|
||||
}
|
||||
|
||||
func substring(with r: Range<Int>) -> String {
|
||||
let startIndex = index(from: r.lowerBound)
|
||||
let endIndex = index(from: r.upperBound)
|
||||
return String(self[startIndex..<endIndex])
|
||||
}
|
||||
}
|
||||
|
||||
extension String {
|
||||
subscript (bounds: CountableClosedRange<Int>) -> String {
|
||||
let start = index(startIndex, offsetBy: bounds.lowerBound)
|
||||
let end = index(startIndex, offsetBy: bounds.upperBound)
|
||||
return String(self[start...end])
|
||||
}
|
||||
|
||||
subscript (bounds: CountableRange<Int>) -> String {
|
||||
let start = index(startIndex, offsetBy: bounds.lowerBound)
|
||||
let end = index(startIndex, offsetBy: bounds.upperBound)
|
||||
return String(self[start..<end])
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - Letters formatting
|
||||
extension String {
|
||||
var firstCapitalized: String {
|
||||
prefix(1).capitalized + dropFirst()
|
||||
}
|
||||
|
||||
var firstLowercased: String {
|
||||
prefix(1).lowercased() + dropFirst()
|
||||
}
|
||||
|
||||
var positiveStringNumber: String {
|
||||
return "+ \(self)"
|
||||
}
|
||||
|
||||
var negativeStringNumber: String {
|
||||
return "- \(self)"
|
||||
}
|
||||
|
||||
var titleCase: String {
|
||||
return self
|
||||
.replacingOccurrences(of: "([A-Z])",
|
||||
with: " $1",
|
||||
options: .regularExpression,
|
||||
range: range(of: self))
|
||||
.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
.capitalized
|
||||
}
|
||||
}
|
||||
|
||||
extension String {
|
||||
var normalizePhone: String {
|
||||
let decimalPhone = self.decimalString() as String
|
||||
let formatted: String
|
||||
if let symb = decimalPhone.first, (symb == "7" || symb == "8") {
|
||||
formatted = String(decimalPhone.dropFirst())
|
||||
} else {
|
||||
formatted = decimalPhone
|
||||
}
|
||||
return formatted
|
||||
}
|
||||
func decimalString() -> NSString {
|
||||
return components(separatedBy: NSCharacterSet.decimalDigits.inverted).joined() as NSString
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
extension String {
|
||||
|
||||
func addHighlight(texts: [String],
|
||||
color: UIColor = .systemBlue) -> NSMutableAttributedString {
|
||||
|
||||
let attr = NSMutableAttributedString(string: self, attributes: nil)
|
||||
|
||||
for text in texts {
|
||||
let range = (attr.string as NSString).range(of: text)
|
||||
attr.setAttributes([.foregroundColor: color], range: range)
|
||||
}
|
||||
|
||||
return attr
|
||||
}
|
||||
|
||||
func addUnderline() -> NSMutableAttributedString {
|
||||
|
||||
let textRange = NSRange(location: 0, length: self.count)
|
||||
let attr = NSMutableAttributedString(string: self)
|
||||
attr.addAttribute(.underlineStyle,
|
||||
value: NSUnderlineStyle.single.rawValue,
|
||||
range: textRange)
|
||||
|
||||
return attr
|
||||
}
|
||||
|
||||
func remove(currency: CurrencyType) -> String {
|
||||
let value = self
|
||||
.replacingOccurrences(of: currency.symbol, with: "")
|
||||
.replacingOccurrences(of: " ", with: "")
|
||||
return value
|
||||
}
|
||||
|
||||
func formattingRemoveSpaces() -> String {
|
||||
return removeSpaces().findReplace(find: " ", replace: "", all: true)
|
||||
}
|
||||
|
||||
func currencyInputFormatting() -> String {
|
||||
|
||||
func formatting(text: String, digits: Int) -> String {
|
||||
let formatting =
|
||||
text.currencyInputFullFormatting(digits: digits, spaces: false)
|
||||
.remove(currency: .rub)
|
||||
.removeSpaces()
|
||||
return formatting
|
||||
}
|
||||
|
||||
let removeSpace = self.formattingRemoveSpaces()
|
||||
|
||||
let needle: Character = ","
|
||||
if let idx = removeSpace.firstIndex(of: needle) {
|
||||
let pos = removeSpace.distance(from: removeSpace.startIndex, to: idx)
|
||||
|
||||
|
||||
let result1 = removeSpace.components(separatedBy: ",")
|
||||
|
||||
if result1.count > 2 {
|
||||
|
||||
let result = String(removeSpace.dropLast())
|
||||
return result
|
||||
|
||||
} else if result1.count > 1 {
|
||||
|
||||
let components = result1[1]
|
||||
print("components \(components.count) ")
|
||||
|
||||
if components.count != 0 {
|
||||
|
||||
var digits = 0
|
||||
let digitsMax = 2
|
||||
if components.count > digitsMax {
|
||||
let newComponents = String(components.prefix(2))
|
||||
let text = result1[0] + newComponents
|
||||
|
||||
return formatting(text: text, digits: digitsMax)
|
||||
}
|
||||
else if digitsMax >= components.count {
|
||||
digits = components.count
|
||||
}
|
||||
|
||||
let text = result1[0] + components
|
||||
return formatting(text: text, digits: digits)
|
||||
} else {
|
||||
return removeSpace // formatting(text: removeSpace, digits: 0)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
print("Found \(needle) at position \(pos) -- \(result1.count) ")
|
||||
|
||||
return removeSpace // formatting(text: removeSpace, digits: 0)
|
||||
}
|
||||
else {
|
||||
print("Not found")
|
||||
return removeSpace
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func currencyInputFullFormatting(digits: Int, spaces: Bool = true) -> String {
|
||||
|
||||
var number: NSNumber!
|
||||
let formatter = NumberFormatter()
|
||||
formatter.numberStyle = .currencyAccounting
|
||||
formatter.currencySymbol = "₽"
|
||||
formatter.maximumFractionDigits = digits
|
||||
formatter.minimumFractionDigits = digits
|
||||
|
||||
var amountWithPrefix = self
|
||||
|
||||
// remove from String: "$", ".", ","
|
||||
let regex = try! NSRegularExpression(pattern: "[^0-9]", options: .caseInsensitive)
|
||||
amountWithPrefix = regex.stringByReplacingMatches(in: amountWithPrefix, options: NSRegularExpression.MatchingOptions(rawValue: 0), range: NSMakeRange(0, self.count), withTemplate: "")
|
||||
|
||||
var value: Double = 100
|
||||
if digits == 1 {
|
||||
value = 10
|
||||
} else if digits == 0 {
|
||||
value = 1
|
||||
}
|
||||
|
||||
let double = (amountWithPrefix as NSString).doubleValue
|
||||
number = NSNumber(value: (double / value))
|
||||
|
||||
// if first number is 0 or all numbers were deleted
|
||||
guard number != 0 as NSNumber else {
|
||||
return ""
|
||||
}
|
||||
|
||||
let result = formatter.string(from: number)!
|
||||
if !spaces {
|
||||
return result.formattingRemoveSpaces()
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
extension String {
|
||||
|
||||
func find(_ find: String) -> Bool {
|
||||
range(of: find) != nil
|
||||
}
|
||||
|
||||
func indexInt(of char: Character, last: Bool = false) -> (index: Index, number: Int)? {
|
||||
if last {
|
||||
guard let index = lastIndex(of: char) else { return nil }
|
||||
return ( index, index.utf16Offset(in: self) )
|
||||
} else {
|
||||
guard let index = firstIndex(of: char) else { return nil }
|
||||
return ( index, index.utf16Offset(in: self) )
|
||||
}
|
||||
}
|
||||
|
||||
func findReplace(find: String, replace: String, all: Bool = false) -> String {
|
||||
if all {
|
||||
return replacingOccurrences(of: find, with: replace, options: .literal, range: nil)
|
||||
} else {
|
||||
return replacingOccurrences(of: find, with: replace)
|
||||
}
|
||||
}
|
||||
|
||||
func removeSpaces() -> String {
|
||||
trimmingCharacters(in: .whitespaces)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension String {
|
||||
func removingEmoji() -> String {
|
||||
return String(self.filter { !$0.isEmoji() })
|
||||
}
|
||||
func removeEmoji() -> String {
|
||||
return self.components(separatedBy: CharacterSet.symbols).joined()
|
||||
}
|
||||
}
|
||||
|
||||
extension Character {
|
||||
fileprivate func isEmoji() -> Bool {
|
||||
return
|
||||
Character(UnicodeScalar(UInt32(0x1d000))!) <= self && self <= Character(UnicodeScalar(UInt32(0x1f77f))!) ||
|
||||
Character(UnicodeScalar(UInt32(0x2100))!) <= self && self <= Character(UnicodeScalar(UInt32(0x26ff))!) ||
|
||||
|
||||
Character(UnicodeScalar(UInt32(0x1f600))!) <= self && self <= Character(UnicodeScalar(UInt32(0x1f64f))!) ||
|
||||
Character(UnicodeScalar(UInt32(0x1f300))!) <= self && self <= Character(UnicodeScalar(UInt32(0x1f5ff))!) ||
|
||||
Character(UnicodeScalar(UInt32(0x1f680))!) <= self && self <= Character(UnicodeScalar(UInt32(0x1f5ff))!) ||
|
||||
Character(UnicodeScalar(UInt32(0x2600))!) <= self && self <= Character(UnicodeScalar(UInt32(0x2600))!) ||
|
||||
Character(UnicodeScalar(UInt32(0x2700))!) <= self && self <= Character(UnicodeScalar(UInt32(0x27bf))!) ||
|
||||
Character(UnicodeScalar(UInt32(0xfe00))!) <= self && self <= Character(UnicodeScalar(UInt32(0xfe0f))!)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Name Of Class
|
||||
|
||||
extension NSObject {
|
||||
class var nameClss: String{
|
||||
return NSStringFromClass(self).components(separatedBy: ".").last!
|
||||
}
|
||||
var nameClss: String{
|
||||
return NSStringFromClass(type(of: self)).components(separatedBy: ".").last!
|
||||
}
|
||||
var nameObj: String{
|
||||
return String(describing: type(of: Self.self))
|
||||
}
|
||||
func fontSize(se: Bool = false) -> UIFont {
|
||||
return UIFont.systemFont(ofSize: se && Device.SE ? 16 : 18)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
//
|
||||
// TimeInterval.swift
|
||||
// PlusBank
|
||||
//
|
||||
// Created by Valentin Titov on 02.02.2021.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
typealias Interval = TimeInterval
|
||||
|
||||
enum Speed: Interval {
|
||||
case zero = 0.0
|
||||
case ms1 = 0.001
|
||||
case ms50 = 0.05
|
||||
case ms100 = 0.1
|
||||
case ms200 = 0.2
|
||||
case ms300 = 0.3
|
||||
case ms350 = 0.35
|
||||
case ms400 = 0.4
|
||||
case ms450 = 0.45
|
||||
case ms500 = 0.5
|
||||
case s1 = 1.0
|
||||
case s2 = 2.0
|
||||
case s3 = 3.0
|
||||
case s4 = 4.0
|
||||
case s5 = 5.0
|
||||
}
|
||||
|
||||
enum Delay: Interval {
|
||||
case ms1 = 0.001
|
||||
case ms100 = 0.1
|
||||
case ms200 = 0.2
|
||||
case ms300 = 0.3
|
||||
case ms400 = 0.4
|
||||
case ms500 = 0.5
|
||||
case s1 = 1.0
|
||||
case s2 = 2.0
|
||||
case s3 = 3.0
|
||||
case s4 = 4.0
|
||||
case s5 = 5.0
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
//
|
||||
// UIView+AddShadow.swift
|
||||
// PlusBank
|
||||
//
|
||||
// Created by Рустам Мотыгуллин on 08.05.2021.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
extension UIView {
|
||||
|
||||
func addShadowFigma(blur: CGFloat, offset: CGSize, color: UIColor, bColor: UIColor = UIColor.background, radius: CGFloat, alpha: Float = 1.0) {
|
||||
|
||||
let layer = self.layer
|
||||
layer.frame = bounds
|
||||
layer.backgroundColor = bColor.cgColor
|
||||
layer.shadowOpacity = alpha
|
||||
layer.shadowColor = color.cgColor
|
||||
layer.shadowOffset = offset
|
||||
layer.shadowRadius = (blur / 2)
|
||||
layer.cornerRadius = radius
|
||||
|
||||
layer.shouldRasterize = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// UIView+Animation.swift
|
||||
// PlusBank
|
||||
//
|
||||
// Created by Valentin Titov on 28.02.2021.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
extension UIView {
|
||||
func shakeAnimation(with duration: TimeInterval = 0.03,
|
||||
repeates: Float = 3.0,
|
||||
delta: CGFloat = 5.0) {
|
||||
let animationKey: String = "position"
|
||||
let animation = CABasicAnimation(keyPath: animationKey)
|
||||
animation.duration = duration
|
||||
animation.repeatCount = repeates
|
||||
animation.autoreverses = true
|
||||
animation.fromValue = NSValue(cgPoint: CGPoint(x: center.x - delta, y: center.y))
|
||||
animation.toValue = NSValue(cgPoint: CGPoint(x: center.x + delta, y: center.y))
|
||||
layer.add(animation,
|
||||
forKey: animationKey)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
//
|
||||
// URL.swift
|
||||
// PlusBank
|
||||
//
|
||||
// Created by Valentin Titov on 19.04.2021.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension URL {
|
||||
subscript(queryParam:String) -> String? {
|
||||
guard let url = URLComponents(string: self.absoluteString) else { return nil }
|
||||
return url.queryItems?.first(where: { $0.name == queryParam })?.value
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
//
|
||||
// Window+Get.swift
|
||||
// PlusBank
|
||||
//
|
||||
// Created by Рустам Мотыгуллин on 25.05.2021.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
extension UIWindow {
|
||||
|
||||
static var current: UIWindow? {
|
||||
if #available(iOS 13.0, *) {
|
||||
return UIApplication.shared.windows.filter {$0.isKeyWindow}.first
|
||||
} else {
|
||||
return UIApplication.shared.keyWindow
|
||||
}
|
||||
}
|
||||
|
||||
static var root: UIViewController? {
|
||||
UIWindow.current?.rootViewController
|
||||
}
|
||||
|
||||
static var nav: UINavigationController? {
|
||||
if let nav = root as? UINavigationController {
|
||||
return nav
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
static var top: UIViewController? {
|
||||
if let nav = UIWindow.nav {
|
||||
if let vc = nav.topViewController {
|
||||
return vc
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
-31
@@ -1,31 +0,0 @@
|
||||
//
|
||||
// StoryboardController.swift
|
||||
// PatternsSwift
|
||||
//
|
||||
// Created by mrustaa on 20/04/2020.
|
||||
// Copyright © 2020 mrustaa. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
@IBDesignable
|
||||
class StoryboardController: UIViewController {
|
||||
|
||||
class func instantiate() -> UIViewController {
|
||||
return fromStoryboardController()
|
||||
}
|
||||
|
||||
class func fromStoryboardController() -> UIViewController {
|
||||
let className = String(describing: self)
|
||||
|
||||
let storyboard = UIStoryboard.init(name: className, bundle: nil)
|
||||
|
||||
// if let initialViewController = storyboard.instantiateInitialViewController() {
|
||||
// return initialViewController
|
||||
// } else {
|
||||
// fatalError("Can't initialize view controller \(self)")
|
||||
// }
|
||||
|
||||
return storyboard.instantiateViewController(withIdentifier: className)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
//
|
||||
// StoryboardController.swift
|
||||
// PatternsSwift
|
||||
//
|
||||
// Created by mrustaa on 20/04/2020.
|
||||
// Copyright © 2020 mrustaa. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import ContainerControllerSwift
|
||||
|
||||
@IBDesignable
|
||||
class StoryboardController: UIViewController {
|
||||
|
||||
var navBarHide: Bool = false
|
||||
|
||||
class func instantiate() -> UIViewController {
|
||||
return fromStoryboardController()
|
||||
}
|
||||
|
||||
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
if navBarHide {
|
||||
navigationController?.navigationBar.isTranslucent = false
|
||||
setNeedsStatusBarAppearanceUpdate()
|
||||
navBar(hide: true)
|
||||
}
|
||||
}
|
||||
|
||||
override func viewWillDisappear(_ animated: Bool) {
|
||||
super.viewWillDisappear(animated)
|
||||
|
||||
if navBarHide {
|
||||
navBar(hide: false)
|
||||
}
|
||||
}
|
||||
|
||||
func navBar(hide: Bool) {
|
||||
self.navigationController?.setNavigationBarHidden(hide, animated: true)
|
||||
}
|
||||
|
||||
class func fromStoryboardController() -> UIViewController {
|
||||
let className = String(describing: self)
|
||||
|
||||
let storyboard = UIStoryboard.init(name: className, bundle: nil)
|
||||
|
||||
// if let initialViewController = storyboard.instantiateInitialViewController() {
|
||||
// return initialViewController
|
||||
// } else {
|
||||
// fatalError("Can't initialize view controller \(self)")
|
||||
// }
|
||||
|
||||
return storyboard.instantiateViewController(withIdentifier: className)
|
||||
}
|
||||
}
|
||||
|
||||
extension UIViewController {
|
||||
|
||||
func back() {
|
||||
self.navigationController?.popViewController(animated: true)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension StoryboardController: ContainerControllerDelegate {
|
||||
|
||||
|
||||
func addContainer(position: ContainerPosition, radius: CGFloat, items: [TableAdapterItem]? = nil, delegate: ContainerControllerDelegate? = nil, addShadow: Bool = true, addBackShadow: Bool = false, header: UIView? = nil, footer: UIView? = nil, back: UIView? = nil) -> (ContainerController, TableAdapterView) {
|
||||
|
||||
let layoutC = ContainerLayout()
|
||||
layoutC.positions = position //
|
||||
layoutC.insets = .init(right: 0, left: 0)
|
||||
let container = ContainerController(addTo: self, layout: layoutC)
|
||||
container.view.cornerRadius = radius
|
||||
container.view.addShadow()
|
||||
container.view.tag = 13
|
||||
if let delegate = delegate {
|
||||
container.delegate = delegate
|
||||
} else {
|
||||
container.delegate = self
|
||||
}
|
||||
|
||||
if addShadow {
|
||||
// let shadowColor = UIColor.black.withAlphaComponent(0.1).cgColor
|
||||
|
||||
container.view.layer.shadowOpacity = Float(0.20)
|
||||
container.view.layer.shadowOffset = .init(width: 0, height: 13)
|
||||
container.view.layer.shadowRadius = 30.0
|
||||
container.view.layer.shadowColor = UIColor.black.cgColor
|
||||
|
||||
|
||||
}
|
||||
|
||||
container.set(backgroundShadowShow: addBackShadow)
|
||||
|
||||
container.view.backgroundColor = .white
|
||||
|
||||
|
||||
// container.view.backgroundColor = color
|
||||
|
||||
if let headerV = header {
|
||||
container.add(headerView: headerV)
|
||||
}
|
||||
|
||||
if let footerV = footer {
|
||||
container.add(footerView: footerV)
|
||||
}
|
||||
|
||||
if let back = back {
|
||||
container.backView?.addSubview(back)
|
||||
}
|
||||
|
||||
|
||||
let table = TableAdapterView(frame: CGRect(x: 0, y: 0, width: ContainerDevice.width, height: 0), style: .plain)
|
||||
table.indicatorStyle = .default
|
||||
// container.add(scrollView: addCollectionView())
|
||||
|
||||
if let items = items {
|
||||
table.set(items: items )
|
||||
|
||||
container.add(scrollView: table)
|
||||
}
|
||||
|
||||
container.move(type: .hide, animation: false)
|
||||
|
||||
// main(delay: 0.05) {
|
||||
// container.move(type: .top
|
||||
// )
|
||||
// }
|
||||
|
||||
return (container, table)
|
||||
}
|
||||
|
||||
|
||||
func containerControllerMove(_ containerController: ContainerController, position: CGFloat, type: ContainerMoveType, animation: Bool) {
|
||||
if animation {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func containerControllerHandlePan(_ containerController: ContainerController, pan: UIPanGestureRecognizer) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// Cell.swift
|
||||
// FigmaConvertXib
|
||||
//
|
||||
// Created by Рустам Мотыгуллин on 08.08.2020.
|
||||
// Copyright © 2020 mrusta. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
extension UITableViewCell {
|
||||
|
||||
func separator(hide: Bool) {
|
||||
separatorInset = UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: hide ? CGFloat(Double.greatestFiniteMagnitude) : 0.0)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
extension UIColor {
|
||||
var redValue: CGFloat{ return CIColor(color: self).red }
|
||||
var greenValue: CGFloat{ return CIColor(color: self).green }
|
||||
var blueValue: CGFloat{ return CIColor(color: self).blue }
|
||||
var alphaValue: CGFloat{ return CIColor(color: self).alpha }
|
||||
}
|
||||
|
||||
class Colors {
|
||||
|
||||
class func rgba( _ red: CGFloat, _ green: CGFloat, _ blue: CGFloat, _ alpha: CGFloat) -> UIColor {
|
||||
return UIColor(red: red / 255, green: green / 255, blue: blue / 255, alpha: alpha)
|
||||
}
|
||||
|
||||
class func rgb( _ red: CGFloat, _ green: CGFloat, _ blue: CGFloat) -> UIColor {
|
||||
return rgba(red, green, blue, 1)
|
||||
}
|
||||
|
||||
class func grayLevel(_ gray: CGFloat) -> UIColor {
|
||||
return rgb(gray * 255, gray * 255, gray * 255)
|
||||
}
|
||||
|
||||
class func blackAlpha(_ alpha: CGFloat) -> UIColor {
|
||||
return rgba(0, 0, 0, alpha)
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
static public let lightGray = blackAlpha(0.1)
|
||||
static public let slightlyDark = blackAlpha(0.3)
|
||||
static public let halfBlack = blackAlpha(0.5)
|
||||
|
||||
static public let black = grayLevel(0) // 0 %
|
||||
static public let gray = grayLevel(127) // 49 %
|
||||
static public let lightInactiveGray = grayLevel(178) // 69 %
|
||||
static public let silver = grayLevel(229) // 89 %
|
||||
static public let inactiveGray = grayLevel(246) // 96 %
|
||||
static public let white = grayLevel(255) // 100 %
|
||||
|
||||
static public let transparentGray = rgba(225, 225, 225, 0.3)
|
||||
static public let lightOrange = rgba(255, 105, 0, 0.1)
|
||||
|
||||
static public let red = rgb(255, 59, 48)
|
||||
static public let blue = rgb(44, 174, 233)
|
||||
static public let yellow = rgb(254, 219, 6)
|
||||
static public let orange = rgb(255, 105, 0)
|
||||
static public let purple = rgb(128, 0, 128)
|
||||
static public let gold = rgb(226, 201, 127)
|
||||
static public let beige = rgb(245, 245, 220)
|
||||
static public let brand = rgb(255, 105, 0)
|
||||
static public let approveGreen = rgb(49, 183, 0)
|
||||
static public let darkBlue = rgb(74, 144, 226)
|
||||
static public let semidarkBlue = rgb(0, 107, 202)
|
||||
static public let darkGray = rgb(142, 142, 147)
|
||||
static public let lightYellow = rgb(254, 229, 6)
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
|
||||
|
||||
import Foundation
|
||||
|
||||
public func background(work: @escaping () -> Void) {
|
||||
DispatchQueue.global(qos: .userInitiated).async {
|
||||
work()
|
||||
}
|
||||
}
|
||||
|
||||
public func main(work: @escaping () -> Void) {
|
||||
DispatchQueue.main.async {
|
||||
work()
|
||||
}
|
||||
}
|
||||
|
||||
public func main(delay: Double, work: @escaping () -> Void) {
|
||||
let deadline = DispatchTime.now() + delay
|
||||
DispatchQueue.main.asyncAfter(deadline: deadline) {
|
||||
work()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
//
|
||||
// Font.swift
|
||||
// ContainerControllerSwift_Example
|
||||
//
|
||||
// Created by Рустам Мотыгуллин on 08.08.2024.
|
||||
// Copyright © 2024 CocoaPods. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
extension UIFont {
|
||||
class func mediumSystemFont(ofSize pointSize: CGFloat) -> UIFont {
|
||||
return self.systemFont(ofSize: pointSize, weight: .medium)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
//
|
||||
// Label.swift
|
||||
// PlusBank
|
||||
//
|
||||
// Created by Рустам Мотыгуллин on 25.02.2021.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
extension UILabel {
|
||||
|
||||
func addUnderline(size: Int = 1) {
|
||||
|
||||
let attributeString: NSMutableAttributedString = NSMutableAttributedString(string: self.text ?? "")
|
||||
attributeString.addAttribute(.underlineStyle, value: size, range: NSMakeRange(0, attributeString.length))
|
||||
|
||||
self.attributedText = attributeString
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func sizeToFitLabel(text: String?, font: UIFont? = nil, lines: Int = 99, padding: CGFloat = 0.0) -> CGSize {
|
||||
|
||||
let label = UILabel(frame: .init(x: 0, y: 0, width: 60, height: 15))
|
||||
label.font = font
|
||||
label.numberOfLines = lines
|
||||
label.text = text ?? ""
|
||||
label.sizeToFit()
|
||||
return .init(width: label.frame.size.width + (padding * 2), height: label.frame.size.height)
|
||||
|
||||
}
|
||||
@@ -0,0 +1,340 @@
|
||||
//
|
||||
// String.swift
|
||||
// PlusBank
|
||||
//
|
||||
// Created by Valentin Titov on 02.02.2021.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
protocol Stringable {
|
||||
var stringValue: String { get }
|
||||
}
|
||||
|
||||
protocol OptionalStringable {
|
||||
var stringValue: String? { get }
|
||||
}
|
||||
|
||||
//MARK: - Constants
|
||||
extension String {
|
||||
static let empty: String = ""
|
||||
static let whitespace: String = " "
|
||||
static let slash: String = "/"
|
||||
static let newLine: String = "\n"
|
||||
static let endLine: String = "\r\n"
|
||||
static let colon: String = ":"
|
||||
static let semicolon: String = ";"
|
||||
static let equal: String = "="
|
||||
static let and: String = "&"
|
||||
}
|
||||
|
||||
extension String {
|
||||
func substringInRange(_ range: NSRange) -> String {
|
||||
let range =
|
||||
index(startIndex, offsetBy: range.location)
|
||||
..<
|
||||
index(startIndex, offsetBy: range.location + range.length)
|
||||
return String(self[range])
|
||||
}
|
||||
|
||||
func replaceStringInRange(
|
||||
_ range: NSRange,
|
||||
with string: String
|
||||
) -> String {
|
||||
return (self as NSString)
|
||||
.replacingCharacters(in: range, with: string)
|
||||
.trimmingCharacters(in: .whitespaces) as String
|
||||
}
|
||||
}
|
||||
|
||||
extension String {
|
||||
func index(from: Int) -> Index {
|
||||
return self.index(startIndex, offsetBy: from)
|
||||
}
|
||||
|
||||
func substring(from: Int) -> String {
|
||||
let fromIndex = index(from: from)
|
||||
return String(self[fromIndex...])
|
||||
}
|
||||
|
||||
func substring(to: Int) -> String {
|
||||
let toIndex = index(from: to)
|
||||
return String(self[..<toIndex])
|
||||
}
|
||||
|
||||
func substring(with r: Range<Int>) -> String {
|
||||
let startIndex = index(from: r.lowerBound)
|
||||
let endIndex = index(from: r.upperBound)
|
||||
return String(self[startIndex..<endIndex])
|
||||
}
|
||||
}
|
||||
|
||||
extension String {
|
||||
subscript (bounds: CountableClosedRange<Int>) -> String {
|
||||
let start = index(startIndex, offsetBy: bounds.lowerBound)
|
||||
let end = index(startIndex, offsetBy: bounds.upperBound)
|
||||
return String(self[start...end])
|
||||
}
|
||||
|
||||
subscript (bounds: CountableRange<Int>) -> String {
|
||||
let start = index(startIndex, offsetBy: bounds.lowerBound)
|
||||
let end = index(startIndex, offsetBy: bounds.upperBound)
|
||||
return String(self[start..<end])
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - Letters formatting
|
||||
extension String {
|
||||
var firstCapitalized: String {
|
||||
prefix(1).capitalized + dropFirst()
|
||||
}
|
||||
|
||||
var firstLowercased: String {
|
||||
prefix(1).lowercased() + dropFirst()
|
||||
}
|
||||
|
||||
var positiveStringNumber: String {
|
||||
return "+ \(self)"
|
||||
}
|
||||
|
||||
var negativeStringNumber: String {
|
||||
return "- \(self)"
|
||||
}
|
||||
|
||||
var titleCase: String {
|
||||
return self
|
||||
.replacingOccurrences(of: "([A-Z])",
|
||||
with: " $1",
|
||||
options: .regularExpression,
|
||||
range: range(of: self))
|
||||
.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
.capitalized
|
||||
}
|
||||
}
|
||||
|
||||
extension String {
|
||||
var normalizePhone: String {
|
||||
let decimalPhone = self.decimalString() as String
|
||||
let formatted: String
|
||||
if let symb = decimalPhone.first, (symb == "7" || symb == "8") {
|
||||
formatted = String(decimalPhone.dropFirst())
|
||||
} else {
|
||||
formatted = decimalPhone
|
||||
}
|
||||
return formatted
|
||||
}
|
||||
func decimalString() -> NSString {
|
||||
return components(separatedBy: NSCharacterSet.decimalDigits.inverted).joined() as NSString
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
extension String {
|
||||
|
||||
func addHighlight(texts: [String],
|
||||
color: UIColor = .systemBlue) -> NSMutableAttributedString {
|
||||
|
||||
let attr = NSMutableAttributedString(string: self, attributes: nil)
|
||||
|
||||
for text in texts {
|
||||
let range = (attr.string as NSString).range(of: text)
|
||||
attr.setAttributes([.foregroundColor: color], range: range)
|
||||
}
|
||||
|
||||
return attr
|
||||
}
|
||||
|
||||
func addUnderline() -> NSMutableAttributedString {
|
||||
|
||||
let textRange = NSRange(location: 0, length: self.count)
|
||||
let attr = NSMutableAttributedString(string: self)
|
||||
attr.addAttribute(.underlineStyle,
|
||||
value: NSUnderlineStyle.single.rawValue,
|
||||
range: textRange)
|
||||
|
||||
return attr
|
||||
}
|
||||
|
||||
// func remove(currency: CurrencyType) -> String {
|
||||
// let value = self
|
||||
// .replacingOccurrences(of: currency.symbol, with: "")
|
||||
// .replacingOccurrences(of: " ", with: "")
|
||||
// return value
|
||||
// }
|
||||
//
|
||||
// func formattingRemoveSpaces() -> String {
|
||||
// return removeSpaces().findReplace(find: " ", replace: "", all: true)
|
||||
// }
|
||||
//
|
||||
// func currencyInputFormatting() -> String {
|
||||
//
|
||||
// func formatting(text: String, digits: Int) -> String {
|
||||
// let formatting =
|
||||
// text.currencyInputFullFormatting(digits: digits, spaces: false)
|
||||
// .remove(currency: .rub)
|
||||
// .removeSpaces()
|
||||
// return formatting
|
||||
// }
|
||||
//
|
||||
// let removeSpace = self.formattingRemoveSpaces()
|
||||
//
|
||||
// let needle: Character = ","
|
||||
// if let idx = removeSpace.firstIndex(of: needle) {
|
||||
// let pos = removeSpace.distance(from: removeSpace.startIndex, to: idx)
|
||||
//
|
||||
//
|
||||
// let result1 = removeSpace.components(separatedBy: ",")
|
||||
//
|
||||
// if result1.count > 2 {
|
||||
//
|
||||
// let result = String(removeSpace.dropLast())
|
||||
// return result
|
||||
//
|
||||
// } else if result1.count > 1 {
|
||||
//
|
||||
// let components = result1[1]
|
||||
// print("components \(components.count) ")
|
||||
//
|
||||
// if components.count != 0 {
|
||||
//
|
||||
// var digits = 0
|
||||
// let digitsMax = 2
|
||||
// if components.count > digitsMax {
|
||||
// let newComponents = String(components.prefix(2))
|
||||
// let text = result1[0] + newComponents
|
||||
//
|
||||
// return formatting(text: text, digits: digitsMax)
|
||||
// }
|
||||
// else if digitsMax >= components.count {
|
||||
// digits = components.count
|
||||
// }
|
||||
//
|
||||
// let text = result1[0] + components
|
||||
// return formatting(text: text, digits: digits)
|
||||
// } else {
|
||||
// return removeSpace // formatting(text: removeSpace, digits: 0)
|
||||
// }
|
||||
//
|
||||
//
|
||||
// }
|
||||
//
|
||||
// print("Found \(needle) at position \(pos) -- \(result1.count) ")
|
||||
//
|
||||
// return removeSpace // formatting(text: removeSpace, digits: 0)
|
||||
// }
|
||||
// else {
|
||||
// print("Not found")
|
||||
// return removeSpace
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
// func currencyInputFullFormatting(digits: Int, spaces: Bool = true) -> String {
|
||||
//
|
||||
// var number: NSNumber!
|
||||
// let formatter = NumberFormatter()
|
||||
// formatter.numberStyle = .currencyAccounting
|
||||
// formatter.currencySymbol = "₽"
|
||||
// formatter.maximumFractionDigits = digits
|
||||
// formatter.minimumFractionDigits = digits
|
||||
//
|
||||
// var amountWithPrefix = self
|
||||
//
|
||||
// // remove from String: "$", ".", ","
|
||||
// let regex = try! NSRegularExpression(pattern: "[^0-9]", options: .caseInsensitive)
|
||||
// amountWithPrefix = regex.stringByReplacingMatches(in: amountWithPrefix, options: NSRegularExpression.MatchingOptions(rawValue: 0), range: NSMakeRange(0, self.count), withTemplate: "")
|
||||
//
|
||||
// var value: Double = 100
|
||||
// if digits == 1 {
|
||||
// value = 10
|
||||
// } else if digits == 0 {
|
||||
// value = 1
|
||||
// }
|
||||
//
|
||||
// let double = (amountWithPrefix as NSString).doubleValue
|
||||
// number = NSNumber(value: (double / value))
|
||||
//
|
||||
// // if first number is 0 or all numbers were deleted
|
||||
// guard number != 0 as NSNumber else {
|
||||
// return ""
|
||||
// }
|
||||
//
|
||||
// let result = formatter.string(from: number)!
|
||||
// if !spaces {
|
||||
// return result.formattingRemoveSpaces()
|
||||
// }
|
||||
// return result
|
||||
// }
|
||||
}
|
||||
|
||||
extension String {
|
||||
|
||||
func find(_ find: String) -> Bool {
|
||||
range(of: find) != nil
|
||||
}
|
||||
|
||||
func indexInt(of char: Character, last: Bool = false) -> (index: Index, number: Int)? {
|
||||
if last {
|
||||
guard let index = lastIndex(of: char) else { return nil }
|
||||
return ( index, index.utf16Offset(in: self) )
|
||||
} else {
|
||||
guard let index = firstIndex(of: char) else { return nil }
|
||||
return ( index, index.utf16Offset(in: self) )
|
||||
}
|
||||
}
|
||||
|
||||
func findReplace(find: String, replace: String, all: Bool = false) -> String {
|
||||
if all {
|
||||
return replacingOccurrences(of: find, with: replace, options: .literal, range: nil)
|
||||
} else {
|
||||
return replacingOccurrences(of: find, with: replace)
|
||||
}
|
||||
}
|
||||
|
||||
func removeSpaces() -> String {
|
||||
trimmingCharacters(in: .whitespaces)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension String {
|
||||
func removingEmoji() -> String {
|
||||
return String(self.filter { !$0.isEmoji() })
|
||||
}
|
||||
func removeEmoji() -> String {
|
||||
return self.components(separatedBy: CharacterSet.symbols).joined()
|
||||
}
|
||||
}
|
||||
|
||||
extension Character {
|
||||
fileprivate func isEmoji() -> Bool {
|
||||
return
|
||||
Character(UnicodeScalar(UInt32(0x1d000))!) <= self && self <= Character(UnicodeScalar(UInt32(0x1f77f))!) ||
|
||||
Character(UnicodeScalar(UInt32(0x2100))!) <= self && self <= Character(UnicodeScalar(UInt32(0x26ff))!) ||
|
||||
|
||||
Character(UnicodeScalar(UInt32(0x1f600))!) <= self && self <= Character(UnicodeScalar(UInt32(0x1f64f))!) ||
|
||||
Character(UnicodeScalar(UInt32(0x1f300))!) <= self && self <= Character(UnicodeScalar(UInt32(0x1f5ff))!) ||
|
||||
Character(UnicodeScalar(UInt32(0x1f680))!) <= self && self <= Character(UnicodeScalar(UInt32(0x1f5ff))!) ||
|
||||
Character(UnicodeScalar(UInt32(0x2600))!) <= self && self <= Character(UnicodeScalar(UInt32(0x2600))!) ||
|
||||
Character(UnicodeScalar(UInt32(0x2700))!) <= self && self <= Character(UnicodeScalar(UInt32(0x27bf))!) ||
|
||||
Character(UnicodeScalar(UInt32(0xfe00))!) <= self && self <= Character(UnicodeScalar(UInt32(0xfe0f))!)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Name Of Class
|
||||
|
||||
extension NSObject {
|
||||
class var nameClss: String{
|
||||
return NSStringFromClass(self).components(separatedBy: ".").last!
|
||||
}
|
||||
var nameClss: String{
|
||||
return NSStringFromClass(type(of: self)).components(separatedBy: ".").last!
|
||||
}
|
||||
var nameObj: String{
|
||||
return String(describing: type(of: Self.self))
|
||||
}
|
||||
func fontSize(se: Bool = false) -> UIFont {
|
||||
return UIFont.systemFont(ofSize: se && Device.SE ? 16 : 18)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
//
|
||||
// UIView+AutoLayout.swift
|
||||
// ContainerControllerSwift_Example
|
||||
//
|
||||
// Created by Рустам Мотыгуллин on 12.08.2024.
|
||||
// Copyright © 2024 CocoaPods. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
//MARK: - Round
|
||||
extension UIView {
|
||||
|
||||
|
||||
|
||||
func addConstraintsZerro(view: UIView?, superView: UIView?, padding: CGFloat) {
|
||||
|
||||
|
||||
// let padding: CGFloat = 0
|
||||
|
||||
if let mainCardView = superView, let view3 = view {
|
||||
|
||||
|
||||
view3.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
|
||||
let bottomConstraint = NSLayoutConstraint(
|
||||
item: mainCardView,
|
||||
attribute: NSLayoutConstraint.Attribute.bottom,
|
||||
relatedBy: NSLayoutConstraint.Relation.equal,
|
||||
toItem: view3,
|
||||
attribute: NSLayoutConstraint.Attribute.bottom,
|
||||
multiplier: 1,
|
||||
constant: (padding * -1)
|
||||
)
|
||||
|
||||
let trailingConstraint = NSLayoutConstraint(
|
||||
item: mainCardView,
|
||||
attribute: NSLayoutConstraint.Attribute.trailing,
|
||||
relatedBy: NSLayoutConstraint.Relation.equal,
|
||||
toItem: view3,
|
||||
attribute: NSLayoutConstraint.Attribute.trailing,
|
||||
multiplier: 1,
|
||||
constant: (padding * -1)
|
||||
)
|
||||
|
||||
let topConstraint = NSLayoutConstraint(
|
||||
item: mainCardView,
|
||||
attribute: NSLayoutConstraint.Attribute.top,
|
||||
relatedBy: NSLayoutConstraint.Relation.equal,
|
||||
toItem: view3,
|
||||
attribute: NSLayoutConstraint.Attribute.top,
|
||||
multiplier: 1,
|
||||
constant: padding
|
||||
)
|
||||
|
||||
let leadingConstraint = NSLayoutConstraint(
|
||||
item: mainCardView,
|
||||
attribute: NSLayoutConstraint.Attribute.leading,
|
||||
relatedBy: NSLayoutConstraint.Relation.equal,
|
||||
toItem: view3,
|
||||
attribute: NSLayoutConstraint.Attribute.leading,
|
||||
multiplier: 1,
|
||||
constant: padding
|
||||
)
|
||||
|
||||
addConstraints([bottomConstraint, trailingConstraint, topConstraint, leadingConstraint])
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -7,6 +7,34 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
//MARK: - Round
|
||||
extension UIView {
|
||||
|
||||
func set(x:CGFloat) {
|
||||
var frame:CGRect = self.frame
|
||||
frame.origin.x = x
|
||||
self.frame = frame
|
||||
}
|
||||
|
||||
func set(y:CGFloat) {
|
||||
var frame:CGRect = self.frame
|
||||
frame.origin.y = y
|
||||
self.frame = frame
|
||||
}
|
||||
|
||||
func set(width:CGFloat) {
|
||||
var frame:CGRect = self.frame
|
||||
frame.size.width = width
|
||||
self.frame = frame
|
||||
}
|
||||
|
||||
func set(height:CGFloat) {
|
||||
var frame:CGRect = self.frame
|
||||
frame.size.height = height
|
||||
self.frame = frame
|
||||
}
|
||||
}
|
||||
|
||||
public extension UIView {
|
||||
|
||||
// MARK: - Basic Properties
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"size" : "20x20",
|
||||
"idiom": "iphone",
|
||||
"filename" : "appIcon-20@2x.png",
|
||||
"scale": "2x"
|
||||
},
|
||||
{
|
||||
"size" : "20x20",
|
||||
"idiom": "iphone",
|
||||
"filename" : "appIcon-20@3x.png",
|
||||
"scale": "3x"
|
||||
},
|
||||
{
|
||||
"size" : "20x20",
|
||||
"idiom": "ipad",
|
||||
"filename" : "appIcon-20.png",
|
||||
"scale": "1x"
|
||||
},
|
||||
{
|
||||
"size" : "20x20",
|
||||
"idiom": "ipad",
|
||||
"filename" : "appIcon-20@2x.png",
|
||||
"scale": "2x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "appIcon-29@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "appIcon-29@3x.png",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"size" : "40x40",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "appIcon-40@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "40x40",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "appIcon-40@3x.png",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"size" : "60x60",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "appIcon-60@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "60x60",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "appIcon-60@3x.png",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "appIcon-29.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "appIcon-29@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "40x40",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "appIcon-40.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "40x40",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "appIcon-40@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "76x76",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "appIcon-76.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "76x76",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "appIcon-76@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "83.5x83.5",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "appIcon-83.5@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "1024x1024",
|
||||
"idiom" : "ios-marketing",
|
||||
"filename" : "appIcon-1024.png",
|
||||
"scale" : "1x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 404 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.8 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.9 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.4 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user