Compare commits

..

81 Commits

Author SHA1 Message Date
Rustam Moty 4738558207 Update README.md Add Desing 2024-09-19 05:30:33 +03:00
mrustaa ba36c06666 Merge commit '409f526cf64aa02d90b8efc8d2b0325d4def49bf' into ui_examples 2024-09-19 05:10:28 +03:00
mrustaa 41cc7c725a update ui_examples 2024-09-19 05:09:10 +03:00
Rustam Moty 409f526cf6 Update README.md 2024-09-19 05:08:04 +03:00
Rustam Moty b2b30b147f Update README.md 2024-09-04 18:10:35 +03:00
Rustam Moty a6b09007b2 Update README.md 2024-09-04 18:05:41 +03:00
Rustam Moty 7b001c292f Update README.md 2024-09-04 18:05:04 +03:00
Rustam Moty 5498067ffd Update README.md 2024-09-04 18:01:46 +03:00
Rustam Moty dce0be5a86 Update README.md 2024-09-04 17:59:41 +03:00
mrustaa 0e952505a9 Refactoring 2024-09-04 15:40:55 +03:00
mrustaa 43fca160be Refactoring 2024-09-04 13:37:11 +03:00
mrustaa 3453526d51 added screens button back 2024-09-04 12:02:07 +03:00
Rustam Moty 237bd57aca Update README.md 2024-09-04 10:30:09 +03:00
mrustaa 5ae4a97450 Refactoring. 2024-09-04 09:41:54 +03:00
mrustaa e9a9f8add1 Refactoring Musics / Images - fix / refactoring / resource compression / memory optimization 2024-09-04 09:20:12 +03:00
mrustaa 615b955965 Fix 2024-09-04 08:31:02 +03:00
mrustaa 943963d8c7 Add + PlayMusic fix localizable 2024-09-04 05:34:20 +03:00
mrustaa f0c7d0abb9 Add + PlayMusic fix update 2024-09-04 05:27:39 +03:00
mrustaa 46bb7d49f7 Add + PlayMusic Resources.Music - Examples superfluous 2024-09-04 04:36:28 +03:00
mrustaa 50d52c70a4 Add + All Screens 2024-09-04 04:26:04 +03:00
mrustaa 85d52cd837 Add + Update screen : PlayMusic 2024-07-31 15:59:53 +03:00
mrustaa 18c90f0c63 Add new 6 screens (BuyStock, CustomCard, Crypto, MapParking, Sport, Taxi)
1 BuyStock
2 CustomCard
3 Crypto
4 MapParking
5 Sport
6 Taxi
2024-07-31 04:54:11 +03:00
mrustaa 15bfb95e8a Update (UI Design + ContainerController) 2024-07-16 09:35:30 +03:00
mrustaa e87a0a40b6 Add - 5 Screen examples (UI Design from dribbble + ContainerController) 2024-07-16 09:33:41 +03:00
mrustaa 341ed54032 Pod spec update. s.version = '1.1.7' 2024-04-18 03:14:22 +03:00
mrustaa bdf75322b8 Merge branch 'supportNavBarTranslucent' 2024-01-12 07:01:00 +03:00
mrustaa 0534cba5df update support isTranslucent 2023-12-24 14:23:18 +03:00
mrustaa 831b2f144f Added support for the change isTranslucent NavigationBar 2023-12-24 00:43:06 +03:00
mrustaa 8bff1e5c24 UI edits/corrections - for the new xcode version 2023-12-23 17:00:38 +03:00
Rustam 4f1ca8157a Update README.md 2022-06-04 17:08:07 +03:00
mrustaa 8d6589004b Update 2021-11-16 15:12:13 +03:00
mrustaa 3993d4d003 Return examples 2021-11-16 15:08:55 +03:00
mrustaa f2e8d4411f Update podspec only Source 2021-11-16 15:03:18 +03:00
mrustaa 3c2848ffd1 Update podspec 2021-11-16 14:35:31 +03:00
mrustaa f162d50891 Merge branch 'fixRotation' 2021-11-06 14:07:58 +03:00
rmotygullin 9f34fcb70c Merge branch 'fix_rotation' 2021-11-06 12:37:56 +03:00
mrustaa fed617321f FixRotation: (Issues During Rotation) Fix Completed Testing 2021-11-05 14:02:00 +03:00
rmotygullin 139082d366 FixRotation: (Issues During Rotation) Fix Completed 2021-11-05 14:01:00 +03:00
mrustaa ca43421a8f FixRotation: Create extension UIDeviceOrientation + Add property Boolean .isRotateAllowed, .face 2021-11-04 14:02:00 +03:00
rmotygullin 08975badd3 FixRotation: Create extension UIDeviceOrientation + Add property Boolean .isRotateAllowed, .face 2021-11-04 14:01:00 +03:00
mrustaa 72759913ae FixRotation: (Issues During Rotation) is isPortrait remove orientation .portraitUpsideDown 2021-11-03 14:02:00 +03:00
rmotygullin 410f53dc1c FixRotation: (Issues During Rotation) is isPortrait remove orientation .portraitUpsideDown 2021-11-03 14:01:00 +03:00
mrustaa 7916101d7b Merge branch 'dark_mode' 2020-09-21 12:21:11 +03:00
mrustaa 17313e2bd3 Merge branch 'fix_readme' 2020-09-21 12:18:14 +03:00
mrustaa fc09d21272 add app-icon 2020-09-21 12:10:27 +03:00
Ernesto Rivera 1b85dfa196 Dark mode support 2020-09-16 15:39:17 -04:00
Ernesto Rivera 830bceb3cb Update README.md
Fix sample code
2020-09-16 09:48:01 -04:00
Ernesto Rivera 8b467a2b59 Add @available(iOS 13.0, *) to classes and protocols for improved compatibility 2020-09-16 09:13:01 -04:00
Ernesto Rivera 7656a3f287 Remove unnecessary iOS 13 version check 2020-09-16 09:12:38 -04:00
Rustam aa2f484b6b Update README.md 2020-09-12 03:53:52 +03:00
mrustaa fd21f9adb7 add open proj 2020-06-25 16:45:42 +03:00
mrustaa ffb3e42939 fix 2020-06-24 13:27:05 +03:00
mrustaa a59bacaf89 update podspec 2020-06-22 15:29:00 +03:00
mrustaa ab373c569a fix talbe-adapter 2020-06-22 15:20:00 +03:00
mrustaa 29c7b63352 add package 2020-06-22 13:58:52 +03:00
Rustam 45a2479733 Update README.md 2020-06-20 17:37:18 +03:00
Rustam 36ec1c3d12 Update README.md 2020-06-12 03:01:47 +03:00
Rustam 59832f0e4b Update README.md 2020-06-12 02:47:06 +03:00
Rustam f77b041bce Update README.md 2020-06-12 02:45:00 +03:00
Rustam 07ddae83a0 Update README.md 2020-06-12 02:28:06 +03:00
Rustam 7ae64d9350 Update README.md 2020-06-12 02:27:13 +03:00
Rustam 51b9c84daa Update README.md 2020-06-12 02:26:10 +03:00
Rustam 211dcf48a6 Update README.md 2020-06-12 02:24:32 +03:00
Rustam e3ae3d440e Update README.md 2020-06-12 02:16:04 +03:00
mrustaa 87b208dc1b update split class 2020-06-11 16:06:12 +03:00
mrustaa 9449d4d9d6 update 2020-06-11 02:44:10 +03:00
mrustaa 5249c14314 fix remove status-bar hidden 2020-06-11 02:16:30 +03:00
Rustam f95ba4f1e4 Update README.md 2020-06-11 01:41:02 +03:00
mrustaa ad9dfd7f5a fix rotated 2020-06-11 01:14:43 +03:00
mrustaa 6d7e1a010b Merge commit '262727193c7a6366c9d53674228eb31db8df3467' 2020-06-11 00:43:56 +03:00
Rustam 262727193c Update README.md 2020-06-10 21:54:29 +03:00
mrustaa a17e142cfa update 2020-06-10 21:34:48 +03:00
mrustaa 99591d784f fix 2020-06-10 21:26:11 +03:00
mrustaa c4ff71566c add screen preview image 2020-06-10 21:24:27 +03:00
mrustaa 1418eeda0f update podspec version 2020-06-10 20:33:37 +03:00
mrustaa 8aeab6b534 update podspec 2020-06-10 20:33:05 +03:00
mrustaa 45ef8d369f update 2020-06-10 20:23:31 +03:00
mrustaa 8e4786465a localizable update 2020-06-10 19:58:31 +03:00
mrustaa 353468f82a podspec update version 2020-06-10 19:16:27 +03:00
mrustaa f2c2dec7e9 hide status-bar-hidden, add gif presentation readme 2020-06-10 19:15:09 +03:00
mrustaa f71316aded update 1.0.8 2020-06-10 15:02:28 +03:00
705 changed files with 40949 additions and 1243 deletions
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>
+3 -4
View File
@@ -8,7 +8,7 @@
Pod::Spec.new do |s|
s.name = 'ContainerControllerSwift'
s.version = '1.0.7'
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,9 +33,8 @@ 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 = 'ContainerControllerSwift/ContainerTable/*.{swift}'
s.source_files = 'ContainerControllerSwift/ContainerCollection/*.{swift}'
s.source_files = 'Sources/**/*.{swift}'
s.framework = "UIKit"
# s.ios.framework = 'UIKit'
# s.resource_bundles = {
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 @@
//
// TableAdapterCellData.swift
// PatternsSwift
//
// Created by Рустам Мотыгуллин on 17/04/2020.
// Copyright © 2020 mrusta. All rights reserved.
//
import UIKit
open class TableAdapterCellData: NSObject {
// public let cellIdentifier: String
//
// public init(cellIdentifier: String? = UUID().uuidString) {
// self.cellIdentifier = cellIdentifier ?? UUID().uuidString
// }
open func cellHeight() -> CGFloat {
return UITableView.automaticDimension
}
open func canEditing() -> Bool {
return false
}
}
@@ -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 &#1048;&#1058;&#1057;&#1086;&#1092;&#1090;. 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
@@ -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>
@@ -36,23 +38,34 @@
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="132" y="137.18140929535232"/>
<point key="canvasLocation" x="132" y="136.69950738916256"/>
</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.23529411764705882" green="0.23529411764705882" blue="0.2627450980392157" 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
}
}
@@ -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
}
}
@@ -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
}
}
@@ -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
}
}
@@ -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
}
}
@@ -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.
![GIF Demo](https://github.com/unboxme/Blurberry/blob/master/Resources/demo.gif)
[[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>
@@ -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
@@ -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 { }
@@ -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")
}
}
}
@@ -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()
}
}
}
@@ -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()]
}
}
@@ -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)
}
}
}
@@ -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,324 @@
//
// DesignTest.swift
// FigmaConvertXib
//
// Created by Рустам Мотыгуллин on 28.07.2020.
// Copyright © 2020 mrusta. All rights reserved.
//
import UIKit
@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
}
}
}
@@ -0,0 +1,71 @@
//
// DesignnGradientView.swift
// ContainerControllerSwift_Example
//
// Created by Рустам Мотыгуллин on 04.09.2024.
// Copyright © 2024 CocoaPods. All rights reserved.
//
import UIKit
@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() }
}
@@ -0,0 +1,674 @@
//
// DesignnGraphView.swift
// ContainerControllerSwift_Example
//
// Created by Рустам Мотыгуллин on 04.09.2024.
// Copyright © 2024 CocoaPods. All rights reserved.
//
import UIKit
@IBDesignable
class DesignnGraphView: UIView {
@IBInspectable var gr2Color1: UIColor?
@IBInspectable var gr2Color2: UIColor?
@IBInspectable var gr2Color3: UIColor?
@IBInspectable var gr2StartPoint: CGPoint = .zero
@IBInspectable var gr2EndPoint: CGPoint = .zero
@IBInspectable var gr2BlendMode: 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
@IBInspectable var grBlendMode: Int = 0
@IBInspectable var fillColor: UIColor?
@IBInspectable var padding: CGFloat = 0
@IBInspectable var cornerRadius: CGFloat = 0.0
@IBInspectable var brColor: UIColor = .clear
@IBInspectable var brWidth: CGFloat = 0.0
@IBInspectable var addPoints: Bool = true
@IBInspectable var arrStr: String = ""
var data: [CGFloat] {
get {
let result = arrStr.components(separatedBy: ",")
let newResult = result.map {
CGFloat(Int($0) ?? 0)
}
return newResult
}
set {
setNeedsDisplay()
}
}
override func layoutSubviews() { setup() }
func setup() {
self.layer.backgroundColor = UIColor.clear.cgColor
self.backgroundColor = UIColor.clear
self.layer.cornerRadius = cornerRadius
// self.cornerRadius = cornerRadius
self.clipsToBounds = true
let bezier = rectanglee(rect: bounds, radius: cornerRadius)
addFill(bezier: bezier, fillColor: fillColor)
// guard let context = UIGraphicsGetCurrentContext() else { return }
let curve = quadCurvedPath()
let path = curve.0
let points = curve.1
addGradient(
bezier: path,
grColor1: gr2Color1,
grColor2: gr2Color2,
grColor3: gr2Color3,
grStartPoint: gr2StartPoint,
grEndPoint: gr2EndPoint,
grBlendMode: gr2BlendMode
)
// context.saveGState()
// path.addClip()
// context.drawLinearGradient(gradient,
// start: grStartPointR,
// end: grEndPointR,
// options: options)
// brColor.setStroke()
// path.lineWidth = brWidth
// path.stroke()
gradientCurve(points: points)
// addInfoLabels()
// addGradient(bezier: bezier)
// if let foundView = viewWithTag(55) {
// foundView.removeFromSuperview()
// }
//
//
// let d = DesignnGradientView(frame: bounds)
// d.backgroundColor = .clear
// d.tag = 44
// d.fillColor = .clear
// d.brWidth = brWidth
// d.brColor = brColor
// d.grColor1 = grColor1
// d.grColor2 = grColor2
// d.grColor3 = grColor3
// d.grStartPoint = grStartPoint
// d.grEndPoint = grEndPoint
// d.layoutSubviews()
//
//
// let g = DesignnGraphOnlyView(frame: bounds)
// g.backgroundColor = .clear
// g.tag = 44
// g.fillColor = .clear
// g.brWidth = brWidth
// g.brColor = brColor
// g.arrStr = arrStr
// g.layoutSubviews()
// let path = quadCurvedPath()
// brColor.setStroke()
// path.lineWidth = brWidth
// path.stroke()
// 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) }
//
// let gradient = CAGradientLayer()
// gradient.frame = path.bounds
// gradient.colors = colors
//
// let shapeMask = CAShapeLayer()
// shapeMask.path = path.cgPath
//
// gradient.mask = shapeMask
// self.layer.addSublayer(gradient)
// let newPath = convert(path, from: self)
// UIColor.orange.setFill()
// newPath.fill()
}
func gradientCurve(points: [CGPoint]) {
// Gradient for the chart colors
let gradient = CAGradientLayer()
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) }
gradient.colors = colors
gradient.startPoint = grEndPoint
gradient.endPoint = grStartPoint
gradient.frame = bounds
layer.addSublayer(gradient)
// Random points
let graph = CAShapeLayer()
let path = CGMutablePath()
// var y: CGFloat = 150
// let points: [CGPoint] = stride(from: CGFloat.zero, to: 300, by: 2).map {
// let change = CGFloat.random(in: -20...20)
// var newY = y + change
// newY = max(10, newY)
// newY = min(newY, 300)
// y = newY
// return CGPoint(x: $0, y: y)
// }
path.addLines(between: points)
graph.path = path
graph.fillColor = nil
graph.strokeColor = UIColor.black.cgColor
graph.lineWidth = brWidth
graph.lineJoin = .round
graph.lineCap = .round
graph.frame = bounds
// Only show the gradient where the line is
gradient.mask = graph
}
func convert(_ path: UIBezierPath, from view: UIView) -> UIBezierPath {
path.apply(view.transform)
// we now have a path in view's superview's coordinates. Let's adjust by the origin.
let viewTransform = CGAffineTransform(translationX: view.frame.origin.x, y: view.frame.origin.y)
path.apply(viewTransform)
// now the path is adjusted by view's origin. Next we need to subtract self's origin
let selfTranslationTransform = CGAffineTransform(translationX: -self.frame.origin.x, y: -self.frame.origin.y)
path.apply(selfTranslationTransform)
// now adjust by self's transform
path.apply(self.transform.inverted())
return path
}
override func draw(_ rect: CGRect) {
setup()
// self.layer.cornerRadius = cornerRadius
// self.layer.backgroundColor = fillColor?.cgColor
// self.clipsToBounds = true
//
//
// let bezier = rectangle(rect: bounds, radius: cornerRadius)
//
//// addFill(bezier: bezier)
////
//// addGradient(bezier: bezier)
//
//
// let path = quadCurvedPath()
//
//
// 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) }
//
// let gradient = CAGradientLayer()
// gradient.frame = path.bounds
// gradient.colors = colors
//
// let shapeMask = CAShapeLayer()
// shapeMask.path = path.cgPath
//
// gradient.mask = shapeMask
// self.layer.addSublayer(gradient)
// brColor.setStroke()
// path.lineWidth = brWidth
// path.stroke()
}
func getText(_ text: String, size: CGFloat = 12, position: CGPoint) -> CATextLayer {
let textlayer = CATextLayer()
textlayer.frame = CGRect(x: position.x, y: position.y, width: 30, height: 18)
textlayer.fontSize = size
textlayer.alignmentMode = .center
textlayer.string = text
textlayer.isWrapped = true
textlayer.truncationMode = .end
textlayer.backgroundColor = UIColor.clear.cgColor
textlayer.foregroundColor = UIColor.black.cgColor
return textlayer
}
func getRect() -> CGSize {
.init(width: bounds.width - (padding * 2), height: bounds.height - (padding * 2))
}
func quadCurvedPath() -> (UIBezierPath, [CGPoint]) {
var arrPoints: [CGPoint] = []
let path = UIBezierPath()
let step = (getRect().width) / CGFloat(data.count - 1)
var p1 = CGPoint(x: 0 + padding, y: coordYFor(index: 0) + padding)
arrPoints.append(p1)
path.move(to: p1)
if addPoints { drawPoint(point: p1, color: UIColor.red, radius: 3) }
if (data.count == 2) {
let pp = CGPoint(x: step + padding, y: coordYFor(index: 1) + padding)
arrPoints.append(pp)
path.addLine(to: pp)
return (path, arrPoints)
}
var oldControlP: CGPoint?
for i in 1..<data.count {
let p2 = CGPoint(x: (step * CGFloat(i)) + padding, y: coordYFor(index: i) + padding)
if addPoints { drawPoint(point: p2, color: UIColor.red, radius: 3) }
var p3: CGPoint?
if i < data.count - 1 {
p3 = CGPoint(x: (step * CGFloat(i + 1)) + padding, y: (coordYFor(index: i + 1)) + padding)
}
let newControlP = controlPointForPoints(p1: p1, p2: p2, next: p3)
arrPoints.append(p2)
path.addCurve(to: p2, controlPoint1: oldControlP ?? p1, controlPoint2: newControlP ?? p2)
p1 = p2
oldControlP = antipodalFor(point: newControlP, center: p2)
}
path.addLine(to: .init(x: getRect().width, y: getRect().height))
path.addLine(to: .init(x: 0, y: getRect().height))
if arrPoints.count > 0 {
path.addLine(to: arrPoints[0])
}
// path.close()
// arrPoints.reverse()
//
// var old: CGPoint = oldControlP ?? p1
// arrPoints.forEach {
// let new: CGPoint = .init(x: $0.x + 0.5, y: $0.y + 0.5)
// path.addCurve(to: new, controlPoint1: old, controlPoint2: new)
// old = new
// }
return (path, arrPoints);
}
/// located on the opposite side from the center point
func antipodalFor(point: CGPoint?, center: CGPoint?) -> CGPoint? {
guard let p1 = point, let center = center else {
return nil
}
let newX = 2 * center.x - p1.x
let diffY = abs(p1.y - center.y)
let newY = center.y + diffY * (p1.y < center.y ? 1 : -1)
return CGPoint(x: newX, y: newY)
}
/// halfway of two points
func midPointForPoints(p1: CGPoint, p2: CGPoint) -> CGPoint {
return CGPoint(x: (p1.x + p2.x) / 2, y: (p1.y + p2.y) / 2);
}
/// Find controlPoint2 for addCurve
/// - Parameters:
/// - p1: first point of curve
/// - p2: second point of curve whose control point we are looking for
/// - next: predicted next point which will use antipodal control point for finded
func controlPointForPoints(p1: CGPoint, p2: CGPoint, next p3: CGPoint?) -> CGPoint? {
guard let p3 = p3 else {
return nil
}
let leftMidPoint = midPointForPoints(p1: p1, p2: p2)
let rightMidPoint = midPointForPoints(p1: p2, p2: p3)
var controlPoint = midPointForPoints(p1: leftMidPoint, p2: antipodalFor(point: rightMidPoint, center: p2)!)
if p1.y.between(a: p2.y, b: controlPoint.y) {
controlPoint.y = p1.y
} else if p2.y.between(a: p1.y, b: controlPoint.y) {
controlPoint.y = p2.y
}
let imaginContol = antipodalFor(point: controlPoint, center: p2)!
if p2.y.between(a: p3.y, b: imaginContol.y) {
controlPoint.y = p2.y
}
if p3.y.between(a: p2.y, b: imaginContol.y) {
let diffY = abs(p2.y - p3.y)
controlPoint.y = p2.y + diffY * (p3.y < p2.y ? 1 : -1)
}
// make lines easier
controlPoint.x += (p2.x - p1.x) * 0.1
return controlPoint
}
func coordYFor(index: Int) -> CGFloat {
return getRect().height - getRect().height * data[index] / (data.max() ?? 0)
}
func drawPoint(point: CGPoint, color: UIColor, radius: CGFloat) {
let ovalPath = UIBezierPath(ovalIn: CGRect(x: point.x - radius, y: point.y - radius, width: radius * 2, height: radius * 2))
color.setFill()
ovalPath.fill()
}
}
extension CGFloat {
func between(a: CGFloat, b: CGFloat) -> Bool {
return self >= Swift.min(a, b) && self <= Swift.max(a, b)
}
}
extension UIView {
func rectanglee(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
}
func drawLinearGradient(inside path:UIBezierPath, start:CGPoint, end:CGPoint, colors:[UIColor])
{
guard let ctx = UIGraphicsGetCurrentContext() else { return }
ctx.saveGState()
defer { ctx.restoreGState() } // clean up graphics state changes when the method returns
path.addClip() // use the path as the clipping region
let cgColors = colors.map({ $0.cgColor })
guard let gradient = CGGradient(colorsSpace: nil, colors: cgColors as CFArray, locations: nil)
else { return }
ctx.drawLinearGradient(gradient, start: start, end: end, options: [])
}
}
extension DesignnGraphView {
//MARK: - Gradient
private func addGradient(
bezier: UIBezierPath,
grColor1: UIColor?,
grColor2: UIColor?,
grColor3: UIColor?,
grStartPoint: CGPoint,
grEndPoint: CGPoint,
grBlendMode: Int = 0
) {
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 grDrawsOptions: Bool = true
let grPointPercent: Bool = true
let bMode = CGBlendMode(rawValue: CGBlendMode.RawValue(grBlendMode)) ?? CGBlendMode.sourceAtop
// bMode = CGBlendMode.screen
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)
}
}
}
extension DesignnGraphView {
func addInfoLabels() {
let mmaax = Int(data.max() ?? 0)
self.layer.addSublayer(getText("0" ,size: 13, position: .init(x: 0, y: bounds.height - 18 )))
self.layer.addSublayer(getText("\(mmaax)" ,size: 13, position: .init(x: 0, y: 0 )))
self.layer.addSublayer(getText("\(data.count)" ,size: 13, position: .init(x: bounds.width - 30, y: bounds.height - 18 )))
var start = false
var half = false
var end = false
for i in 0..<mmaax {
let y = (( CGFloat(i) / CGFloat(mmaax) ) * (bounds.height - 18))
let percent = Int((y / bounds.height) * 100)
var str = "\(100 - percent )%"
str = "--"
if percent >= 25 && !start {
start = true
self.layer.addSublayer(getText( str ,size: 10, position: .init(x: 0 , y: y - 9 )))
self.layer.addSublayer(getText( str ,size: 10, position: .init(x: bounds.width - 30 , y: y - 9 )))
}
if percent >= 50 && !half {
half = true
str = "=--"
self.layer.addSublayer(getText(str,size: 10, position: .init(x: 0 , y: y - 9 )))
str = "--="
self.layer.addSublayer(getText( str ,size: 10, position: .init(x: bounds.width - 30 , y: y - 9 )))
}
if percent >= 75 && !end {
end = true
self.layer.addSublayer(getText(str,size: 10, position: .init(x:0, y:y - 9)))
self.layer.addSublayer(getText( str ,size: 10, position: .init(x: bounds.width - 30 , y: y - 9 )))
}
}
start = false
half = false
end = false
for i in 0..<data.count {
let x = (( CGFloat(i) / CGFloat(data.count) ) * (bounds.width - 30))
let percent = Int((x / bounds.width) * 100)
var str = "\(percent)%"
str = "!"
if percent >= 25 && !start {
start = true
self.layer.addSublayer(getText(str,size: 10, position: .init(x: x - 15 , y: bounds.height - 18 )))
self.layer.addSublayer(getText(str,size: 10, position: .init(x: x - 15 , y: 5 )))
}
if percent >= 50 && !half {
half = true
str = ".|."
self.layer.addSublayer(getText(str,size: 10, position: .init(x: x - 15 , y: bounds.height - 18 )))
str = "'|'"
self.layer.addSublayer(getText(str,size: 10, position: .init(x: x - 15 , y: 5 )))
}
if percent >= 75 && !end {
end = true
self.layer.addSublayer(getText(str,size: 10, position: .init(x: x - 15, y: bounds.height - 18 )))
self.layer.addSublayer(getText(str,size: 10, position: .init(x: x - 15 , y: 5 )))
}
}
}
}
extension UIView {
func addFill(bezier: UIBezierPath, fillColor: UIColor?) {
guard let fillColor = fillColor else { return }
guard let context = UIGraphicsGetCurrentContext() else { return }
bezier.close()
context.saveGState()
fillColor.setFill()
bezier.fill()
context.restoreGState()
}
}
@@ -0,0 +1,193 @@
//
// DesignnGraphViewLegacy.swift
// ContainerControllerSwift_Example
//
// Created by Рустам Мотыгуллин on 04.09.2024.
// Copyright © 2024 CocoaPods. All rights reserved.
//
import UIKit
@IBDesignable
class DesignnGraphViewOld: UIView {
@IBInspectable var maxValue: Int = 100
@IBInspectable var minValue: Int = 0
@IBInspectable var padding: Int = 10
@IBInspectable var arrStr: String = ""
var arrData: [Int] = [10,5,50,70,80,40,35,55,60,62,20,40,70,60,50,90,95]
@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 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()
}
override func draw(_ rect: CGRect) {
super.draw(rect)
// layer.backgroundColor = fillColor?.cgColor
let context = UIGraphicsGetCurrentContext()!
let oval = UIBezierPath(ovalIn: rect)
addFill(bezier: oval)
UIColor.green.setStroke()
oval.lineWidth = 2 * 2
oval.stroke()
//// Color Declarations
let gradientColor = UIColor(red: 0.039, green: 0.839, blue: 0.361, alpha: 0.140)
let gradientColor2 = UIColor(red: 0.039, green: 0.841, blue: 0.362, alpha: 0.000)
let gradientColor3 = UIColor.green
let color = UIColor.red
//// Gradient Declarations
// let gradient = CGGradient(colorsSpace: nil, colors: [ gradientColor.cgColor, gradientColor2.cgColor] as CFArray, locations: [0, 1])!
//// Rectangle Drawing
// let rectanglePath = UIBezierPath(rect: CGRect(x: 0, y: 0, width: 70, height: 70))
// color.setFill()
// rectanglePath.fill()
//// Bezier Drawing
let bezierPath = UIBezierPath ()
UIColor.green.setStroke()
bezierPath.lineWidth = 2 * 2
// let result = arrStr.components(separatedBy: ",")
//
// let newResult = result.map {
// Int($0) ?? 0
// }
arrData.enumerated().forEach { index, item in
let widht = ( (rect.width) - ( CGFloat(padding) * 2))
let height = ( (rect.height) - (CGFloat(padding) * 2))
let y = ((( CGFloat(index) / CGFloat(arrData.count) ) * widht) + CGFloat(padding))
let maxHeight = CGFloat(arrData.max() ?? 0)
let x = ((( CGFloat(item) / maxHeight) * height) + CGFloat(padding))
bezierPath.move(to: CGPoint(x: x, y: y))
}
// bezierPath.move(to: CGPoint(x: 4, y: 34.64))
// bezierPath.addLine(to: CGPoint(x: 8.4, y: 34.64))
// bezierPath.addLine(to: CGPoint(x: 11.04, y: 32.04))
// bezierPath.addLine(to: CGPoint(x: 15.44, y: 32.04))
// bezierPath.addLine(to: CGPoint(x: 18.53, y: 29.01))
// bezierPath.addLine(to: CGPoint(x: 22.49, y: 29.01))
// bezierPath.addLine(to: CGPoint(x: 26.01, y: 34.64))
// bezierPath.addLine(to: CGPoint(x: 29.53, y: 37.89))
// bezierPath.addLine(to: CGPoint(x: 32.61, y: 37.89))
// bezierPath.addLine(to: CGPoint(x: 34.81, y: 34.64))
// bezierPath.addLine(to: CGPoint(x: 38.55, y: 34.64))
// bezierPath.addLine(to: CGPoint(x: 42.95, y: 25.55))
// bezierPath.addLine(to: CGPoint(x: 53.08, y: 25.55))
// bezierPath.addLine(to: CGPoint(x: 55.5, y: 21))
// bezierPath.addLine(to: CGPoint(x: 61, y: 21))
// bezierPath.addLine(to: CGPoint(x: 61, y: 42))
// bezierPath.addLine(to: CGPoint(x: 4, y: 42))
// bezierPath.addLine(to: CGPoint(x: 4, y: 34.64))
bezierPath.stroke()
// bezierPath.stroke()
context.restoreGState()
// context.restoreGState()
// bezierPath.close()
// context.saveGState()
// context.restoreGState()
// bezierPath.close ()
// context.saveGState ()
// bezierPath.addClip()
// context.drawLinearGradient(gradient, start: CGPoint(x: 362.5, y: 84), end: CGPoint(x: 362.5, y: 105), options: [])
// context.restoreGState()
}
func getPoints() {
var arr: [CGPoint] = []
print(" getPoints width: \(self.width) height: \(self.height)")
arrData.enumerated().forEach { index, item in
let widht = ( (self.width) - ( CGFloat(padding) * 2))
let height = ( (self.height) - (CGFloat(padding) * 2))
let y = ((( CGFloat(index) / CGFloat(arrData.count) ) * widht) + CGFloat(padding))
let maxHeight = CGFloat(arrData.max() ?? 0)
let x = ((( CGFloat(item) / maxHeight) * height) + CGFloat(padding))
print(" getPoints ( width: \(widht) height: \(height) maxHeight \(maxHeight)) index: \(index), x: \(x) y: \(y)")
arr.append(CGPoint(x: x, y: y))
// bezierPath.move(to: CGPoint(x: x, y: y))
}
}
// 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() }
}
@@ -0,0 +1,49 @@
//
// DesignnImageView.swift
// ContainerControllerSwift_Example
//
// Created by Рустам Мотыгуллин on 04.09.2024.
// Copyright © 2024 CocoaPods. 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() }
}
@@ -0,0 +1,56 @@
//
// DesignnShadowView.swift
// ContainerControllerSwift_Example
//
// Created by Рустам Мотыгуллин on 04.09.2024.
// Copyright © 2024 CocoaPods. All rights reserved.
//
import UIKit
@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() }
}
@@ -0,0 +1,47 @@
//
// DesignnView.swift
// ContainerControllerSwift_Example
//
// Created by Рустам Мотыгуллин on 04.09.2024.
// Copyright © 2024 CocoaPods. All rights reserved.
//
import UIKit
@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() }
}
@@ -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"
}
}
@@ -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)
}
}
@@ -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
}
}
@@ -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()]
}
}
@@ -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)
}
}
@@ -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
}
}
@@ -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
}
}
@@ -1,31 +0,0 @@
//
// StoryboardController.swift
// PatternsSwift
//
// Created by Рустам Мотыгуллин on 20/04/2020.
// Copyright © 2020 mrusta. 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) {
}
}
@@ -2,8 +2,8 @@
// XibView.swift
// PatternsSwift
//
// Created by Рустам Мотыгуллин on 19/04/2020.
// Copyright © 2020 mrusta. All rights reserved.
// Created by mrustaa on 19/04/2020.
// Copyright © 2020 mrustaa. All rights reserved.
//
import UIKit
@@ -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])
}
}
}

Some files were not shown because too many files have changed in this diff Show More