From 35eefb0052d21ac91db5f9f3499e1d11c1a1cacb Mon Sep 17 00:00:00 2001 From: Ivan Vorobei Date: Tue, 10 May 2022 18:46:11 +0300 Subject: [PATCH 01/16] Clean code. --- .github/FUNDING.yml | 1 + .github/ISSUE_TEMPLATE/bug_report.md | 10 +- .github/ISSUE_TEMPLATE/feature_request.md | 6 +- .github/ISSUE_TEMPLATE/question.md | 11 - .github/PULL_REQUEST_TEMPLATE.md | 5 - Assets/Readme/Elements/NativeAvatarView.svg | 13 - .../Elements/NativeLargeActionButton.svg | 13 - .../Elements/NativePlayPauseCompactButton.svg | 9 - .../Elements/NativeSmallActionButton.svg | 20 - Assets/Readme/spm-install-preview.png | Bin 44092 -> 0 bytes CONTRIBUTING.md | 5 +- .../NativeUIKit.xcodeproj/project.pbxproj | 807 ------------------ .../contents.xcworkspacedata | 7 - .../xcshareddata/IDEWorkspaceChecks.plist | 8 - .../xcshareddata/swiftpm/Package.resolved | 34 - .../UserInterfaceState.xcuserstate | Bin 233476 -> 0 bytes .../xcschemes/iOS Example.xcscheme | 78 -- .../xcschemes/watchOS Example.xcscheme | 105 --- .../xcdebugger/Breakpoints_v2.xcbkptlist | 6 - .../xcschemes/xcschememanagement.plist | 37 - Example App/iOS Example/App/AppDelegate.swift | 36 - .../AppIcon.appiconset/Contents.json | 98 --- .../App/Assets.xcassets/Contents.json | 6 - .../App/Base.lproj/LaunchScreen.storyboard | 25 - Example App/iOS Example/Info.plist | 45 - .../iOS Example/Scenes/RootController.swift | 29 - Example App/tvOS Example/AppDelegate.swift | 5 - .../Content.imageset/Contents.json | 11 - .../Back.imagestacklayer/Contents.json | 6 - .../Contents.json | 17 - .../Content.imageset/Contents.json | 11 - .../Front.imagestacklayer/Contents.json | 6 - .../Content.imageset/Contents.json | 11 - .../Middle.imagestacklayer/Contents.json | 6 - .../Content.imageset/Contents.json | 16 - .../Back.imagestacklayer/Contents.json | 6 - .../App Icon.imagestack/Contents.json | 17 - .../Content.imageset/Contents.json | 16 - .../Front.imagestacklayer/Contents.json | 6 - .../Content.imageset/Contents.json | 16 - .../Middle.imagestacklayer/Contents.json | 6 - .../Contents.json | 32 - .../Contents.json | 24 - .../Top Shelf Image.imageset/Contents.json | 24 - .../Assets.xcassets/Contents.json | 6 - Example App/tvOS Example/Info.plist | 34 - .../tvOS Example/LaunchScreen.storyboard | 29 - .../Circular.imageset/Contents.json | 28 - .../Contents.json | 53 -- .../Extra Large.imageset/Contents.json | 28 - .../Graphic Bezel.imageset/Contents.json | 18 - .../Graphic Circular.imageset/Contents.json | 18 - .../Graphic Corner.imageset/Contents.json | 18 - .../Contents.json | 28 - .../Contents.json | 18 - .../Modular.imageset/Contents.json | 28 - .../Utilitarian.imageset/Contents.json | 28 - .../Assets.xcassets/Contents.json | 6 - .../ExtensionDelegate.swift | 3 - .../watchOS Example Extension/Info.plist | 36 - .../AccentColor.colorset/Contents.json | 11 - .../AppIcon.appiconset/Contents.json | 81 -- .../Assets.xcassets/Contents.json | 6 - .../Base.lproj/Interface.storyboard | 15 - Example App/watchOS Example/Info.plist | 31 - LICENSE | 2 +- .../NativeUIKit/Bars/NativeBorderedView.swift | 114 --- .../Bars/NativeMimicrateBarView.swift | 78 -- .../NativeMimicrateNavigationBarView.swift | 38 - .../NativeLargeActionToolBarView.swift | 100 --- .../NativeLargeSmallActionToolBarView.swift | 63 -- ...NativeSkipableLargeActionToolBarView.swift | 63 -- .../ToolBar/NativeAppleAuthToolBarView.swift | 75 -- ...NativeMimicrateNavigationToolBarView.swift | 55 -- .../NativeSmallActionToolBarView.swift | 54 -- .../NativeLargeHeaderCollectionView.swift | 54 -- ...tionHeaderFooterProvider+LargeHeader.swift | 38 - .../NativePromoCollectionViewCell.swift | 59 -- .../NativeOnbiardingActionButton.swift | 151 ---- .../NativeOnboardingActionsController.swift | 85 -- .../NativeOnboardingFeatureView.swift | 132 --- .../NativeOnboardingFeaturesController.swift | 102 --- .../NativeOnboardingController.swift | 86 -- .../Profile/NativeAvatarHeaderView.swift | 79 -- .../Profile/NativeProfileController.swift | 51 -- .../Profile/NativeProfileHeaderView.swift | 144 ---- .../NativeNavigationController.swift | 80 -- .../Scroll/NativeHeaderController.swift | 57 -- ...NativeHeaderFullWidthImageController.swift | 60 -- .../NativeHeaderTextFieldController.swift | 59 -- ...rTableController+HeaderContainerView.swift | 71 -- .../Header/NativeHeaderTableController.swift | 77 -- .../Buttons/NativeLargeActionButton.swift | 118 --- .../Buttons/NativeMediumActionButton.swift | 89 -- .../Buttons/NativeSmallActionButton.swift | 89 -- .../Controls/NativeLargeTextField.swift | 80 -- .../NativePlayPauseCompactButton.swift | 67 -- .../NativeUIKit/Labels/NativeFooterView.swift | 69 -- .../Labels/NativeModalHeaderView.swift | 131 --- Sources/NativeUIKit/NativeAppearance.swift | 34 - Sources/NativeUIKit/NativeLayout.swift | 58 -- .../Table/Button/CellProvider+Button.swift | 44 - .../Button/NativeDiffableLeftButton.swift | 59 -- .../NativeLeftButtonTableViewCell.swift | 47 - .../Table/Empty/CellProvider+Empty.swift | 40 - .../Table/Empty/NativeEmptyRowItem.swift | 37 - .../Empty/NativeEmptyTableViewCell.swift | 113 --- .../CellProvider+LargeHeader.swift | 38 - .../LargeHeader/NativeLargeHeaderItem.swift | 42 - .../NativeLargeHeaderViewExtension.swift | 37 - .../NativeUIKit/Views/NativeAvatarView.swift | 255 ------ .../Views/NativeLargeHeaderView.swift | 97 --- .../Views/NativePlaceholderView.swift | 157 ---- .../NativeUIKit/Views/NativePromoView.swift | 153 ---- Sources/UIKitExtension/FillFile.swift | 1 + 115 files changed, 6 insertions(+), 6049 deletions(-) create mode 100644 .github/FUNDING.yml delete mode 100644 .github/ISSUE_TEMPLATE/question.md delete mode 100644 Assets/Readme/Elements/NativeAvatarView.svg delete mode 100644 Assets/Readme/Elements/NativeLargeActionButton.svg delete mode 100644 Assets/Readme/Elements/NativePlayPauseCompactButton.svg delete mode 100644 Assets/Readme/Elements/NativeSmallActionButton.svg delete mode 100644 Assets/Readme/spm-install-preview.png delete mode 100644 Example App/NativeUIKit.xcodeproj/project.pbxproj delete mode 100644 Example App/NativeUIKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata delete mode 100644 Example App/NativeUIKit.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist delete mode 100644 Example App/NativeUIKit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved delete mode 100644 Example App/NativeUIKit.xcodeproj/project.xcworkspace/xcuserdata/ivanvorobei.xcuserdatad/UserInterfaceState.xcuserstate delete mode 100644 Example App/NativeUIKit.xcodeproj/xcshareddata/xcschemes/iOS Example.xcscheme delete mode 100644 Example App/NativeUIKit.xcodeproj/xcshareddata/xcschemes/watchOS Example.xcscheme delete mode 100644 Example App/NativeUIKit.xcodeproj/xcuserdata/ivanvorobei.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist delete mode 100644 Example App/NativeUIKit.xcodeproj/xcuserdata/ivanvorobei.xcuserdatad/xcschemes/xcschememanagement.plist delete mode 100644 Example App/iOS Example/App/AppDelegate.swift delete mode 100644 Example App/iOS Example/App/Assets.xcassets/AppIcon.appiconset/Contents.json delete mode 100644 Example App/iOS Example/App/Assets.xcassets/Contents.json delete mode 100644 Example App/iOS Example/App/Base.lproj/LaunchScreen.storyboard delete mode 100644 Example App/iOS Example/Info.plist delete mode 100644 Example App/iOS Example/Scenes/RootController.swift delete mode 100644 Example App/tvOS Example/AppDelegate.swift delete mode 100644 Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json delete mode 100644 Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json delete mode 100644 Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json delete mode 100644 Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json delete mode 100644 Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json delete mode 100644 Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json delete mode 100644 Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Contents.json delete mode 100644 Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json delete mode 100644 Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json delete mode 100644 Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json delete mode 100644 Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json delete mode 100644 Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json delete mode 100644 Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json delete mode 100644 Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Contents.json delete mode 100644 Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json delete mode 100644 Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json delete mode 100644 Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json delete mode 100644 Example App/tvOS Example/Assets.xcassets/Contents.json delete mode 100644 Example App/tvOS Example/Info.plist delete mode 100644 Example App/tvOS Example/LaunchScreen.storyboard delete mode 100644 Example App/watchOS Example Extension/Assets.xcassets/Complication.complicationset/Circular.imageset/Contents.json delete mode 100644 Example App/watchOS Example Extension/Assets.xcassets/Complication.complicationset/Contents.json delete mode 100644 Example App/watchOS Example Extension/Assets.xcassets/Complication.complicationset/Extra Large.imageset/Contents.json delete mode 100644 Example App/watchOS Example Extension/Assets.xcassets/Complication.complicationset/Graphic Bezel.imageset/Contents.json delete mode 100644 Example App/watchOS Example Extension/Assets.xcassets/Complication.complicationset/Graphic Circular.imageset/Contents.json delete mode 100644 Example App/watchOS Example Extension/Assets.xcassets/Complication.complicationset/Graphic Corner.imageset/Contents.json delete mode 100644 Example App/watchOS Example Extension/Assets.xcassets/Complication.complicationset/Graphic Extra Large.imageset/Contents.json delete mode 100644 Example App/watchOS Example Extension/Assets.xcassets/Complication.complicationset/Graphic Large Rectangular.imageset/Contents.json delete mode 100644 Example App/watchOS Example Extension/Assets.xcassets/Complication.complicationset/Modular.imageset/Contents.json delete mode 100644 Example App/watchOS Example Extension/Assets.xcassets/Complication.complicationset/Utilitarian.imageset/Contents.json delete mode 100644 Example App/watchOS Example Extension/Assets.xcassets/Contents.json delete mode 100644 Example App/watchOS Example Extension/ExtensionDelegate.swift delete mode 100644 Example App/watchOS Example Extension/Info.plist delete mode 100644 Example App/watchOS Example/Assets.xcassets/AccentColor.colorset/Contents.json delete mode 100644 Example App/watchOS Example/Assets.xcassets/AppIcon.appiconset/Contents.json delete mode 100644 Example App/watchOS Example/Assets.xcassets/Contents.json delete mode 100644 Example App/watchOS Example/Base.lproj/Interface.storyboard delete mode 100644 Example App/watchOS Example/Info.plist delete mode 100644 Sources/NativeUIKit/Bars/NativeBorderedView.swift delete mode 100644 Sources/NativeUIKit/Bars/NativeMimicrateBarView.swift delete mode 100644 Sources/NativeUIKit/Bars/NativeMimicrateNavigationBarView.swift delete mode 100644 Sources/NativeUIKit/Bars/ToolBar/LargeAction/NativeLargeActionToolBarView.swift delete mode 100644 Sources/NativeUIKit/Bars/ToolBar/LargeAction/NativeLargeSmallActionToolBarView.swift delete mode 100644 Sources/NativeUIKit/Bars/ToolBar/LargeAction/NativeSkipableLargeActionToolBarView.swift delete mode 100644 Sources/NativeUIKit/Bars/ToolBar/NativeAppleAuthToolBarView.swift delete mode 100644 Sources/NativeUIKit/Bars/ToolBar/NativeMimicrateNavigationToolBarView.swift delete mode 100644 Sources/NativeUIKit/Bars/ToolBar/NativeSmallActionToolBarView.swift delete mode 100644 Sources/NativeUIKit/Collection/LargeHeader/NativeLargeHeaderCollectionView.swift delete mode 100644 Sources/NativeUIKit/Collection/LargeHeader/SPDiffableCollectionHeaderFooterProvider+LargeHeader.swift delete mode 100644 Sources/NativeUIKit/Collection/NativePromoCollectionViewCell.swift delete mode 100644 Sources/NativeUIKit/Controllers/Complex/Onboarding/Actinos/NativeOnbiardingActionButton.swift delete mode 100644 Sources/NativeUIKit/Controllers/Complex/Onboarding/Actinos/NativeOnboardingActionsController.swift delete mode 100644 Sources/NativeUIKit/Controllers/Complex/Onboarding/Features/NativeOnboardingFeatureView.swift delete mode 100644 Sources/NativeUIKit/Controllers/Complex/Onboarding/Features/NativeOnboardingFeaturesController.swift delete mode 100644 Sources/NativeUIKit/Controllers/Complex/Onboarding/NativeOnboardingController.swift delete mode 100644 Sources/NativeUIKit/Controllers/Complex/Profile/NativeAvatarHeaderView.swift delete mode 100644 Sources/NativeUIKit/Controllers/Complex/Profile/NativeProfileController.swift delete mode 100644 Sources/NativeUIKit/Controllers/Complex/Profile/NativeProfileHeaderView.swift delete mode 100644 Sources/NativeUIKit/Controllers/Navigation/NativeNavigationController.swift delete mode 100644 Sources/NativeUIKit/Controllers/Scroll/NativeHeaderController.swift delete mode 100644 Sources/NativeUIKit/Controllers/Scroll/NativeHeaderFullWidthImageController.swift delete mode 100644 Sources/NativeUIKit/Controllers/Scroll/NativeHeaderTextFieldController.swift delete mode 100644 Sources/NativeUIKit/Controllers/Table/Header/NativeHeaderTableController+HeaderContainerView.swift delete mode 100644 Sources/NativeUIKit/Controllers/Table/Header/NativeHeaderTableController.swift delete mode 100644 Sources/NativeUIKit/Controls/Buttons/NativeLargeActionButton.swift delete mode 100644 Sources/NativeUIKit/Controls/Buttons/NativeMediumActionButton.swift delete mode 100644 Sources/NativeUIKit/Controls/Buttons/NativeSmallActionButton.swift delete mode 100644 Sources/NativeUIKit/Controls/NativeLargeTextField.swift delete mode 100644 Sources/NativeUIKit/Controls/NativePlayPauseCompactButton.swift delete mode 100644 Sources/NativeUIKit/Labels/NativeFooterView.swift delete mode 100644 Sources/NativeUIKit/Labels/NativeModalHeaderView.swift delete mode 100644 Sources/NativeUIKit/NativeAppearance.swift delete mode 100644 Sources/NativeUIKit/NativeLayout.swift delete mode 100644 Sources/NativeUIKit/Table/Button/CellProvider+Button.swift delete mode 100644 Sources/NativeUIKit/Table/Button/NativeDiffableLeftButton.swift delete mode 100644 Sources/NativeUIKit/Table/Button/NativeLeftButtonTableViewCell.swift delete mode 100644 Sources/NativeUIKit/Table/Empty/CellProvider+Empty.swift delete mode 100644 Sources/NativeUIKit/Table/Empty/NativeEmptyRowItem.swift delete mode 100644 Sources/NativeUIKit/Table/Empty/NativeEmptyTableViewCell.swift delete mode 100644 Sources/NativeUIKit/Table/LargeHeader/CellProvider+LargeHeader.swift delete mode 100644 Sources/NativeUIKit/Table/LargeHeader/NativeLargeHeaderItem.swift delete mode 100644 Sources/NativeUIKit/Table/LargeHeader/NativeLargeHeaderViewExtension.swift delete mode 100644 Sources/NativeUIKit/Views/NativeAvatarView.swift delete mode 100644 Sources/NativeUIKit/Views/NativeLargeHeaderView.swift delete mode 100644 Sources/NativeUIKit/Views/NativePlaceholderView.swift delete mode 100644 Sources/NativeUIKit/Views/NativePromoView.swift create mode 100644 Sources/UIKitExtension/FillFile.swift diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..82b0889 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: [sparrowcode] diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index b28f7b0..0350fe3 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -3,13 +3,5 @@ name: Bug report about: Create a report to help us improve title: '' labels: bug -assignees: ivanvorobei +assignees: ivanvorobei, svyatoynick --- - -**Details** - - iOS Version [e.g. 15.2] - - Framework Version [e.g. 1.0.2] - - Installed via [e.g. SPM, Cocoapods, Manually] - -**Describe the Bug** -A clear and concise description of what the bug is. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 0a6e21d..3a1bfa7 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -3,9 +3,5 @@ name: Feature request about: Suggest an idea for this project title: '' labels: enhancement -assignees: ivanvorobei - +assignees: ivanvorobei, svyatoynick --- - -**Feature Description** -Describe what functionality you want to see. diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md deleted file mode 100644 index 62725cd..0000000 --- a/.github/ISSUE_TEMPLATE/question.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -name: Question -about: Something is not clear with the project -title: '' -labels: question -assignees: ivanvorobei - ---- - -**Describe the Problem** -A clear and concise description of what you want to do. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 8e2d91c..66bffd1 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,7 +1,2 @@ ## Goal - -## Checklist - -- [] Testing in compability platforms -- [] Installed correct via `Swift Package Manager` and `Cocoapods` diff --git a/Assets/Readme/Elements/NativeAvatarView.svg b/Assets/Readme/Elements/NativeAvatarView.svg deleted file mode 100644 index b24adc8..0000000 --- a/Assets/Readme/Elements/NativeAvatarView.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - NativeAvatarView - - - - - - - - - - \ No newline at end of file diff --git a/Assets/Readme/Elements/NativeLargeActionButton.svg b/Assets/Readme/Elements/NativeLargeActionButton.svg deleted file mode 100644 index 6943f11..0000000 --- a/Assets/Readme/Elements/NativeLargeActionButton.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - NativeLargeActionButton - - - - - - - - - - \ No newline at end of file diff --git a/Assets/Readme/Elements/NativePlayPauseCompactButton.svg b/Assets/Readme/Elements/NativePlayPauseCompactButton.svg deleted file mode 100644 index 6ae5ffc..0000000 --- a/Assets/Readme/Elements/NativePlayPauseCompactButton.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - NativePlayPauseCompactButton - - - - - - \ No newline at end of file diff --git a/Assets/Readme/Elements/NativeSmallActionButton.svg b/Assets/Readme/Elements/NativeSmallActionButton.svg deleted file mode 100644 index 137e799..0000000 --- a/Assets/Readme/Elements/NativeSmallActionButton.svg +++ /dev/null @@ -1,20 +0,0 @@ - - - NativeSmallActionButton - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Assets/Readme/spm-install-preview.png b/Assets/Readme/spm-install-preview.png deleted file mode 100644 index 2fd564f5cf26ba526e3d5ab49b1c1d1e5f19deb5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44092 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 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3391492..1a8eb1b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,7 +1,6 @@ # Contributing -Here provided more info about project, contribution process and recomended changes. -Please, read it before pull request or create issue. +Here provided info about contribution process and recommendations. ## Codestyle @@ -28,5 +27,3 @@ Here you find all which using in project: - // MARK: - Internal - // MARK: - Models - // MARK: - Ovveride - -If you can't find valid, add new to codestyle agreements please. Other can be use if class is large and need struct it even without adding to codestyle agreements. diff --git a/Example App/NativeUIKit.xcodeproj/project.pbxproj b/Example App/NativeUIKit.xcodeproj/project.pbxproj deleted file mode 100644 index 93d4015..0000000 --- a/Example App/NativeUIKit.xcodeproj/project.pbxproj +++ /dev/null @@ -1,807 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 52; - objects = { - -/* Begin PBXBuildFile section */ - F4C33DFA26C92E52001A28B1 /* RootController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4C33DF326C92E52001A28B1 /* RootController.swift */; }; - F4C33DFB26C92E52001A28B1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F4C33DF526C92E52001A28B1 /* Assets.xcassets */; }; - F4C33DFC26C92E52001A28B1 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F4C33DF626C92E52001A28B1 /* LaunchScreen.storyboard */; }; - F4C33DFD26C92E52001A28B1 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4C33DF826C92E52001A28B1 /* AppDelegate.swift */; }; - F4C33E0026C92EBB001A28B1 /* SparrowKit in Frameworks */ = {isa = PBXBuildFile; productRef = F4C33DFF26C92EBB001A28B1 /* SparrowKit */; }; - F4C33E0326C92EEA001A28B1 /* NativeUIKit in Frameworks */ = {isa = PBXBuildFile; productRef = F4C33E0226C92EEA001A28B1 /* NativeUIKit */; }; - F4C33E2326C93186001A28B1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F4C33E1F26C93186001A28B1 /* Assets.xcassets */; }; - F4C33E2426C93186001A28B1 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4C33E2026C93186001A28B1 /* AppDelegate.swift */; }; - F4C33E2726C931BD001A28B1 /* SparrowKit in Frameworks */ = {isa = PBXBuildFile; productRef = F4C33E2626C931BD001A28B1 /* SparrowKit */; }; - F4C33E2926C931BF001A28B1 /* NativeUIKit in Frameworks */ = {isa = PBXBuildFile; productRef = F4C33E2826C931BF001A28B1 /* NativeUIKit */; }; - F4C33E2D26C93202001A28B1 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F4C33E2C26C93202001A28B1 /* LaunchScreen.storyboard */; }; - F4C33E3426C932C8001A28B1 /* Interface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F4C33E3226C932C8001A28B1 /* Interface.storyboard */; }; - F4C33E3626C932C9001A28B1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F4C33E3526C932C9001A28B1 /* Assets.xcassets */; }; - F4C33E3D26C932CA001A28B1 /* watchOS Example Extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = F4C33E3C26C932CA001A28B1 /* watchOS Example Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; - F4C33E4426C932CA001A28B1 /* ExtensionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4C33E4326C932CA001A28B1 /* ExtensionDelegate.swift */; }; - F4C33E4826C932CB001A28B1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F4C33E4726C932CB001A28B1 /* Assets.xcassets */; }; - F4C33E4C26C932CB001A28B1 /* watchOS Example.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = F4C33E3026C932C8001A28B1 /* watchOS Example.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - F4C33E3E26C932CA001A28B1 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = F4C33DD126C92DF8001A28B1 /* Project object */; - proxyType = 1; - remoteGlobalIDString = F4C33E3B26C932CA001A28B1; - remoteInfo = "watchOS Example Extension"; - }; - F4C33E4A26C932CB001A28B1 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = F4C33DD126C92DF8001A28B1 /* Project object */; - proxyType = 1; - remoteGlobalIDString = F4C33E2F26C932C8001A28B1; - remoteInfo = "watchOS Example"; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - F4C33E5026C932CB001A28B1 /* Embed App Extensions */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 13; - files = ( - F4C33E3D26C932CA001A28B1 /* watchOS Example Extension.appex in Embed App Extensions */, - ); - name = "Embed App Extensions"; - runOnlyForDeploymentPostprocessing = 0; - }; - F4C33E5426C932CB001A28B1 /* Embed Watch Content */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = "$(CONTENTS_FOLDER_PATH)/Watch"; - dstSubfolderSpec = 16; - files = ( - F4C33E4C26C932CB001A28B1 /* watchOS Example.app in Embed Watch Content */, - ); - name = "Embed Watch Content"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - F41E02AA276C836700C7BD05 /* NativeUIKit */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = NativeUIKit; path = ..; sourceTree = ""; }; - F4C33DD926C92DF8001A28B1 /* iOS Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "iOS Example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; - F4C33DF126C92E52001A28B1 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - F4C33DF326C92E52001A28B1 /* RootController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RootController.swift; sourceTree = ""; }; - F4C33DF526C92E52001A28B1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - F4C33DF726C92E52001A28B1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; - F4C33DF826C92E52001A28B1 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - F4C33E0A26C9315F001A28B1 /* tvOS Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "tvOS Example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; - F4C33E1F26C93186001A28B1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - F4C33E2026C93186001A28B1 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - F4C33E2126C93186001A28B1 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - F4C33E2C26C93202001A28B1 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; }; - F4C33E3026C932C8001A28B1 /* watchOS Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "watchOS Example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; - F4C33E3326C932C8001A28B1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Interface.storyboard; sourceTree = ""; }; - F4C33E3526C932C9001A28B1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - F4C33E3726C932C9001A28B1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - F4C33E3C26C932CA001A28B1 /* watchOS Example Extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "watchOS Example Extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; - F4C33E4326C932CA001A28B1 /* ExtensionDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionDelegate.swift; sourceTree = ""; }; - F4C33E4726C932CB001A28B1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - F4C33E4926C932CB001A28B1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - F4C33DD626C92DF8001A28B1 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - F4C33E0326C92EEA001A28B1 /* NativeUIKit in Frameworks */, - F4C33E0026C92EBB001A28B1 /* SparrowKit in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - F4C33E0726C9315F001A28B1 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - F4C33E2926C931BF001A28B1 /* NativeUIKit in Frameworks */, - F4C33E2726C931BD001A28B1 /* SparrowKit in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - F4C33E3926C932CA001A28B1 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - F4C33DD026C92DF8001A28B1 = { - isa = PBXGroup; - children = ( - F41E02AA276C836700C7BD05 /* NativeUIKit */, - F4C33DDB26C92DF8001A28B1 /* iOS Example */, - F4C33E0B26C9315F001A28B1 /* tvOS Example */, - F4C33E3126C932C8001A28B1 /* watchOS Example */, - F4C33E4026C932CA001A28B1 /* watchOS Example Extension */, - F4C33DDA26C92DF8001A28B1 /* Products */, - F4C33E0126C92EEA001A28B1 /* Frameworks */, - ); - sourceTree = ""; - }; - F4C33DDA26C92DF8001A28B1 /* Products */ = { - isa = PBXGroup; - children = ( - F4C33DD926C92DF8001A28B1 /* iOS Example.app */, - F4C33E0A26C9315F001A28B1 /* tvOS Example.app */, - F4C33E3026C932C8001A28B1 /* watchOS Example.app */, - F4C33E3C26C932CA001A28B1 /* watchOS Example Extension.appex */, - ); - name = Products; - sourceTree = ""; - }; - F4C33DDB26C92DF8001A28B1 /* iOS Example */ = { - isa = PBXGroup; - children = ( - F4C33DF426C92E52001A28B1 /* App */, - F4C33DF226C92E52001A28B1 /* Scenes */, - F4C33DF126C92E52001A28B1 /* Info.plist */, - ); - path = "iOS Example"; - sourceTree = ""; - }; - F4C33DF226C92E52001A28B1 /* Scenes */ = { - isa = PBXGroup; - children = ( - F4C33DF326C92E52001A28B1 /* RootController.swift */, - ); - path = Scenes; - sourceTree = ""; - }; - F4C33DF426C92E52001A28B1 /* App */ = { - isa = PBXGroup; - children = ( - F4C33DF826C92E52001A28B1 /* AppDelegate.swift */, - F4C33DF526C92E52001A28B1 /* Assets.xcassets */, - F4C33DF626C92E52001A28B1 /* LaunchScreen.storyboard */, - ); - path = App; - sourceTree = ""; - }; - F4C33E0126C92EEA001A28B1 /* Frameworks */ = { - isa = PBXGroup; - children = ( - ); - name = Frameworks; - sourceTree = ""; - }; - F4C33E0B26C9315F001A28B1 /* tvOS Example */ = { - isa = PBXGroup; - children = ( - F4C33E2026C93186001A28B1 /* AppDelegate.swift */, - F4C33E1F26C93186001A28B1 /* Assets.xcassets */, - F4C33E2126C93186001A28B1 /* Info.plist */, - F4C33E2C26C93202001A28B1 /* LaunchScreen.storyboard */, - ); - path = "tvOS Example"; - sourceTree = ""; - }; - F4C33E3126C932C8001A28B1 /* watchOS Example */ = { - isa = PBXGroup; - children = ( - F4C33E3226C932C8001A28B1 /* Interface.storyboard */, - F4C33E3526C932C9001A28B1 /* Assets.xcassets */, - F4C33E3726C932C9001A28B1 /* Info.plist */, - ); - path = "watchOS Example"; - sourceTree = ""; - }; - F4C33E4026C932CA001A28B1 /* watchOS Example Extension */ = { - isa = PBXGroup; - children = ( - F4C33E4326C932CA001A28B1 /* ExtensionDelegate.swift */, - F4C33E4726C932CB001A28B1 /* Assets.xcassets */, - F4C33E4926C932CB001A28B1 /* Info.plist */, - ); - path = "watchOS Example Extension"; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - F4C33DD826C92DF8001A28B1 /* iOS Example */ = { - isa = PBXNativeTarget; - buildConfigurationList = F4C33DED26C92DF9001A28B1 /* Build configuration list for PBXNativeTarget "iOS Example" */; - buildPhases = ( - F4C33DD526C92DF8001A28B1 /* Sources */, - F4C33DD626C92DF8001A28B1 /* Frameworks */, - F4C33DD726C92DF8001A28B1 /* Resources */, - F4C33E5426C932CB001A28B1 /* Embed Watch Content */, - ); - buildRules = ( - ); - dependencies = ( - F4C33E4B26C932CB001A28B1 /* PBXTargetDependency */, - ); - name = "iOS Example"; - packageProductDependencies = ( - F4C33DFF26C92EBB001A28B1 /* SparrowKit */, - F4C33E0226C92EEA001A28B1 /* NativeUIKit */, - ); - productName = NativeUIKit; - productReference = F4C33DD926C92DF8001A28B1 /* iOS Example.app */; - productType = "com.apple.product-type.application"; - }; - F4C33E0926C9315F001A28B1 /* tvOS Example */ = { - isa = PBXNativeTarget; - buildConfigurationList = F4C33E1926C93160001A28B1 /* Build configuration list for PBXNativeTarget "tvOS Example" */; - buildPhases = ( - F4C33E0626C9315F001A28B1 /* Sources */, - F4C33E0726C9315F001A28B1 /* Frameworks */, - F4C33E0826C9315F001A28B1 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "tvOS Example"; - packageProductDependencies = ( - F4C33E2626C931BD001A28B1 /* SparrowKit */, - F4C33E2826C931BF001A28B1 /* NativeUIKit */, - ); - productName = "tvOS Example"; - productReference = F4C33E0A26C9315F001A28B1 /* tvOS Example.app */; - productType = "com.apple.product-type.application"; - }; - F4C33E2F26C932C8001A28B1 /* watchOS Example */ = { - isa = PBXNativeTarget; - buildConfigurationList = F4C33E5126C932CB001A28B1 /* Build configuration list for PBXNativeTarget "watchOS Example" */; - buildPhases = ( - F4C33E2E26C932C8001A28B1 /* Resources */, - F4C33E5026C932CB001A28B1 /* Embed App Extensions */, - ); - buildRules = ( - ); - dependencies = ( - F4C33E3F26C932CA001A28B1 /* PBXTargetDependency */, - ); - name = "watchOS Example"; - productName = "watchOS Example"; - productReference = F4C33E3026C932C8001A28B1 /* watchOS Example.app */; - productType = "com.apple.product-type.application.watchapp2"; - }; - F4C33E3B26C932CA001A28B1 /* watchOS Example Extension */ = { - isa = PBXNativeTarget; - buildConfigurationList = F4C33E4D26C932CB001A28B1 /* Build configuration list for PBXNativeTarget "watchOS Example Extension" */; - buildPhases = ( - F4C33E3826C932CA001A28B1 /* Sources */, - F4C33E3926C932CA001A28B1 /* Frameworks */, - F4C33E3A26C932CA001A28B1 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "watchOS Example Extension"; - productName = "watchOS Example Extension"; - productReference = F4C33E3C26C932CA001A28B1 /* watchOS Example Extension.appex */; - productType = "com.apple.product-type.watchkit2-extension"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - F4C33DD126C92DF8001A28B1 /* Project object */ = { - isa = PBXProject; - attributes = { - LastSwiftUpdateCheck = 1250; - LastUpgradeCheck = 1250; - TargetAttributes = { - F4C33DD826C92DF8001A28B1 = { - CreatedOnToolsVersion = 12.5.1; - }; - F4C33E0926C9315F001A28B1 = { - CreatedOnToolsVersion = 12.5.1; - LastSwiftMigration = 1250; - }; - F4C33E2F26C932C8001A28B1 = { - CreatedOnToolsVersion = 12.5.1; - }; - F4C33E3B26C932CA001A28B1 = { - CreatedOnToolsVersion = 12.5.1; - }; - }; - }; - buildConfigurationList = F4C33DD426C92DF8001A28B1 /* Build configuration list for PBXProject "NativeUIKit" */; - compatibilityVersion = "Xcode 9.3"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = F4C33DD026C92DF8001A28B1; - packageReferences = ( - F4C33DFE26C92EBB001A28B1 /* XCRemoteSwiftPackageReference "SparrowKit" */, - ); - productRefGroup = F4C33DDA26C92DF8001A28B1 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - F4C33DD826C92DF8001A28B1 /* iOS Example */, - F4C33E0926C9315F001A28B1 /* tvOS Example */, - F4C33E2F26C932C8001A28B1 /* watchOS Example */, - F4C33E3B26C932CA001A28B1 /* watchOS Example Extension */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - F4C33DD726C92DF8001A28B1 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - F4C33DFC26C92E52001A28B1 /* LaunchScreen.storyboard in Resources */, - F4C33DFB26C92E52001A28B1 /* Assets.xcassets in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - F4C33E0826C9315F001A28B1 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - F4C33E2326C93186001A28B1 /* Assets.xcassets in Resources */, - F4C33E2D26C93202001A28B1 /* LaunchScreen.storyboard in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - F4C33E2E26C932C8001A28B1 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - F4C33E3626C932C9001A28B1 /* Assets.xcassets in Resources */, - F4C33E3426C932C8001A28B1 /* Interface.storyboard in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - F4C33E3A26C932CA001A28B1 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - F4C33E4826C932CB001A28B1 /* Assets.xcassets in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - F4C33DD526C92DF8001A28B1 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - F4C33DFD26C92E52001A28B1 /* AppDelegate.swift in Sources */, - F4C33DFA26C92E52001A28B1 /* RootController.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - F4C33E0626C9315F001A28B1 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - F4C33E2426C93186001A28B1 /* AppDelegate.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - F4C33E3826C932CA001A28B1 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - F4C33E4426C932CA001A28B1 /* ExtensionDelegate.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - F4C33E3F26C932CA001A28B1 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = F4C33E3B26C932CA001A28B1 /* watchOS Example Extension */; - targetProxy = F4C33E3E26C932CA001A28B1 /* PBXContainerItemProxy */; - }; - F4C33E4B26C932CB001A28B1 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = F4C33E2F26C932C8001A28B1 /* watchOS Example */; - targetProxy = F4C33E4A26C932CB001A28B1 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin PBXVariantGroup section */ - F4C33DF626C92E52001A28B1 /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - F4C33DF726C92E52001A28B1 /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = ""; - }; - F4C33E3226C932C8001A28B1 /* Interface.storyboard */ = { - isa = PBXVariantGroup; - children = ( - F4C33E3326C932C8001A28B1 /* Base */, - ); - name = Interface.storyboard; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - F4C33DEB26C92DF9001A28B1 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 14.5; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - }; - name = Debug; - }; - F4C33DEC26C92DF9001A28B1 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 14.5; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - SDKROOT = iphoneos; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - F4C33DEE26C92DF9001A28B1 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = ""; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = 5KKK9SKF79; - INFOPLIST_FILE = "iOS Example/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = by.ivanvorobei.opensource.nativeuikit.ios; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - F4C33DEF26C92DF9001A28B1 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = ""; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = 5KKK9SKF79; - INFOPLIST_FILE = "iOS Example/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = by.ivanvorobei.opensource.nativeuikit.ios; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Release; - }; - F4C33E1A26C93160001A28B1 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = ""; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = 5KKK9SKF79; - INFOPLIST_FILE = "tvOS Example/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = by.ivanvorobei.opensource.nativeuikit.tvos; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = appletvos; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 3; - TVOS_DEPLOYMENT_TARGET = 14.5; - }; - name = Debug; - }; - F4C33E1B26C93160001A28B1 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = ""; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = 5KKK9SKF79; - INFOPLIST_FILE = "tvOS Example/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = by.ivanvorobei.opensource.nativeuikit.tvos; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = appletvos; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 3; - TVOS_DEPLOYMENT_TARGET = 14.5; - }; - name = Release; - }; - F4C33E4E26C932CB001A28B1 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_COMPLICATION_NAME = Complication; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = 5KKK9SKF79; - INFOPLIST_FILE = "watchOS Example Extension/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@executable_path/../../Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = by.ivanvorobei.opensource.nativeuikit.ios.watchkitapp.watchkitextension; - PRODUCT_NAME = "${TARGET_NAME}"; - SDKROOT = watchos; - SKIP_INSTALL = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 4; - WATCHOS_DEPLOYMENT_TARGET = 7.4; - }; - name = Debug; - }; - F4C33E4F26C932CB001A28B1 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_COMPLICATION_NAME = Complication; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = 5KKK9SKF79; - INFOPLIST_FILE = "watchOS Example Extension/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@executable_path/../../Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = by.ivanvorobei.opensource.nativeuikit.ios.watchkitapp.watchkitextension; - PRODUCT_NAME = "${TARGET_NAME}"; - SDKROOT = watchos; - SKIP_INSTALL = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 4; - WATCHOS_DEPLOYMENT_TARGET = 7.4; - }; - name = Release; - }; - F4C33E5226C932CB001A28B1 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = ""; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = 5KKK9SKF79; - IBSC_MODULE = watchOS_Example_Extension; - INFOPLIST_FILE = "watchOS Example/Info.plist"; - PRODUCT_BUNDLE_IDENTIFIER = by.ivanvorobei.opensource.nativeuikit.ios.watchkitapp; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = watchos; - SKIP_INSTALL = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 4; - WATCHOS_DEPLOYMENT_TARGET = 7.4; - }; - name = Debug; - }; - F4C33E5326C932CB001A28B1 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = ""; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = 5KKK9SKF79; - IBSC_MODULE = watchOS_Example_Extension; - INFOPLIST_FILE = "watchOS Example/Info.plist"; - PRODUCT_BUNDLE_IDENTIFIER = by.ivanvorobei.opensource.nativeuikit.ios.watchkitapp; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = watchos; - SKIP_INSTALL = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 4; - WATCHOS_DEPLOYMENT_TARGET = 7.4; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - F4C33DD426C92DF8001A28B1 /* Build configuration list for PBXProject "NativeUIKit" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - F4C33DEB26C92DF9001A28B1 /* Debug */, - F4C33DEC26C92DF9001A28B1 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - F4C33DED26C92DF9001A28B1 /* Build configuration list for PBXNativeTarget "iOS Example" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - F4C33DEE26C92DF9001A28B1 /* Debug */, - F4C33DEF26C92DF9001A28B1 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - F4C33E1926C93160001A28B1 /* Build configuration list for PBXNativeTarget "tvOS Example" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - F4C33E1A26C93160001A28B1 /* Debug */, - F4C33E1B26C93160001A28B1 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - F4C33E4D26C932CB001A28B1 /* Build configuration list for PBXNativeTarget "watchOS Example Extension" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - F4C33E4E26C932CB001A28B1 /* Debug */, - F4C33E4F26C932CB001A28B1 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - F4C33E5126C932CB001A28B1 /* Build configuration list for PBXNativeTarget "watchOS Example" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - F4C33E5226C932CB001A28B1 /* Debug */, - F4C33E5326C932CB001A28B1 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - -/* Begin XCRemoteSwiftPackageReference section */ - F4C33DFE26C92EBB001A28B1 /* XCRemoteSwiftPackageReference "SparrowKit" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/ivanvorobei/SparrowKit"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 3.4.6; - }; - }; -/* End XCRemoteSwiftPackageReference section */ - -/* Begin XCSwiftPackageProductDependency section */ - F4C33DFF26C92EBB001A28B1 /* SparrowKit */ = { - isa = XCSwiftPackageProductDependency; - package = F4C33DFE26C92EBB001A28B1 /* XCRemoteSwiftPackageReference "SparrowKit" */; - productName = SparrowKit; - }; - F4C33E0226C92EEA001A28B1 /* NativeUIKit */ = { - isa = XCSwiftPackageProductDependency; - productName = NativeUIKit; - }; - F4C33E2626C931BD001A28B1 /* SparrowKit */ = { - isa = XCSwiftPackageProductDependency; - package = F4C33DFE26C92EBB001A28B1 /* XCRemoteSwiftPackageReference "SparrowKit" */; - productName = SparrowKit; - }; - F4C33E2826C931BF001A28B1 /* NativeUIKit */ = { - isa = XCSwiftPackageProductDependency; - productName = NativeUIKit; - }; -/* End XCSwiftPackageProductDependency section */ - }; - rootObject = F4C33DD126C92DF8001A28B1 /* Project object */; -} diff --git a/Example App/NativeUIKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Example App/NativeUIKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 919434a..0000000 --- a/Example App/NativeUIKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/Example App/NativeUIKit.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Example App/NativeUIKit.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d9810..0000000 --- a/Example App/NativeUIKit.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/Example App/NativeUIKit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Example App/NativeUIKit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved deleted file mode 100644 index bce3df5..0000000 --- a/Example App/NativeUIKit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ /dev/null @@ -1,34 +0,0 @@ -{ - "object": { - "pins": [ - { - "package": "SparrowKit", - "repositoryURL": "https://github.com/ivanvorobei/SparrowKit", - "state": { - "branch": null, - "revision": "4a6283d0e5e010a99c038bd103a8c883245aafa5", - "version": "3.5.4" - } - }, - { - "package": "SPDiffable", - "repositoryURL": "https://github.com/ivanvorobei/SPDiffable", - "state": { - "branch": null, - "revision": "49d6a86438d0e82efc8da1e0202bceee9b1f5666", - "version": "4.0.6" - } - }, - { - "package": "SPPerspective", - "repositoryURL": "https://github.com/ivanvorobei/SPPerspective", - "state": { - "branch": null, - "revision": "580ed06a5daec94c5095379971570289b429fa3d", - "version": "1.4.1" - } - } - ] - }, - "version": 1 -} diff --git a/Example App/NativeUIKit.xcodeproj/project.xcworkspace/xcuserdata/ivanvorobei.xcuserdatad/UserInterfaceState.xcuserstate b/Example App/NativeUIKit.xcodeproj/project.xcworkspace/xcuserdata/ivanvorobei.xcuserdatad/UserInterfaceState.xcuserstate deleted file mode 100644 index c4086de67fd6cb93bcae1d95b9b842a34a905c0b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 233476 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�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 diff --git a/Example App/NativeUIKit.xcodeproj/xcshareddata/xcschemes/iOS Example.xcscheme b/Example App/NativeUIKit.xcodeproj/xcshareddata/xcschemes/iOS Example.xcscheme deleted file mode 100644 index 9c07ce7..0000000 --- a/Example App/NativeUIKit.xcodeproj/xcshareddata/xcschemes/iOS Example.xcscheme +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Example App/NativeUIKit.xcodeproj/xcshareddata/xcschemes/watchOS Example.xcscheme b/Example App/NativeUIKit.xcodeproj/xcshareddata/xcschemes/watchOS Example.xcscheme deleted file mode 100644 index 23df4a6..0000000 --- a/Example App/NativeUIKit.xcodeproj/xcshareddata/xcschemes/watchOS Example.xcscheme +++ /dev/null @@ -1,105 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Example App/NativeUIKit.xcodeproj/xcuserdata/ivanvorobei.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/Example App/NativeUIKit.xcodeproj/xcuserdata/ivanvorobei.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist deleted file mode 100644 index f259397..0000000 --- a/Example App/NativeUIKit.xcodeproj/xcuserdata/ivanvorobei.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ /dev/null @@ -1,6 +0,0 @@ - - - diff --git a/Example App/NativeUIKit.xcodeproj/xcuserdata/ivanvorobei.xcuserdatad/xcschemes/xcschememanagement.plist b/Example App/NativeUIKit.xcodeproj/xcuserdata/ivanvorobei.xcuserdatad/xcschemes/xcschememanagement.plist deleted file mode 100644 index 52d946c..0000000 --- a/Example App/NativeUIKit.xcodeproj/xcuserdata/ivanvorobei.xcuserdatad/xcschemes/xcschememanagement.plist +++ /dev/null @@ -1,37 +0,0 @@ - - - - - SchemeUserState - - iOS Example.xcscheme_^#shared#^_ - - orderHint - 0 - - tvOS Example.xcscheme_^#shared#^_ - - orderHint - 3 - - watchOS Example.xcscheme_^#shared#^_ - - orderHint - 2 - - - SuppressBuildableAutocreation - - F4C33DD826C92DF8001A28B1 - - primary - - - F4C33E2F26C932C8001A28B1 - - primary - - - - - diff --git a/Example App/iOS Example/App/AppDelegate.swift b/Example App/iOS Example/App/AppDelegate.swift deleted file mode 100644 index 5208746..0000000 --- a/Example App/iOS Example/App/AppDelegate.swift +++ /dev/null @@ -1,36 +0,0 @@ -// The MIT License (MIT) -// Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) -// -// 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 SparrowKit -import NativeUIKit - -@UIApplicationMain -class AppDelegate: SPAppWindowDelegate { - - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - let rootController = RootController() - let navigationController = NativeNavigationController(rootViewController: rootController) - makeKeyAndVisible(viewController: navigationController, tint: .systemBlue) - return true - } -} - diff --git a/Example App/iOS Example/App/Assets.xcassets/AppIcon.appiconset/Contents.json b/Example App/iOS Example/App/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index 9221b9b..0000000 --- a/Example App/iOS Example/App/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,98 +0,0 @@ -{ - "images" : [ - { - "idiom" : "iphone", - "scale" : "2x", - "size" : "20x20" - }, - { - "idiom" : "iphone", - "scale" : "3x", - "size" : "20x20" - }, - { - "idiom" : "iphone", - "scale" : "2x", - "size" : "29x29" - }, - { - "idiom" : "iphone", - "scale" : "3x", - "size" : "29x29" - }, - { - "idiom" : "iphone", - "scale" : "2x", - "size" : "40x40" - }, - { - "idiom" : "iphone", - "scale" : "3x", - "size" : "40x40" - }, - { - "idiom" : "iphone", - "scale" : "2x", - "size" : "60x60" - }, - { - "idiom" : "iphone", - "scale" : "3x", - "size" : "60x60" - }, - { - "idiom" : "ipad", - "scale" : "1x", - "size" : "20x20" - }, - { - "idiom" : "ipad", - "scale" : "2x", - "size" : "20x20" - }, - { - "idiom" : "ipad", - "scale" : "1x", - "size" : "29x29" - }, - { - "idiom" : "ipad", - "scale" : "2x", - "size" : "29x29" - }, - { - "idiom" : "ipad", - "scale" : "1x", - "size" : "40x40" - }, - { - "idiom" : "ipad", - "scale" : "2x", - "size" : "40x40" - }, - { - "idiom" : "ipad", - "scale" : "1x", - "size" : "76x76" - }, - { - "idiom" : "ipad", - "scale" : "2x", - "size" : "76x76" - }, - { - "idiom" : "ipad", - "scale" : "2x", - "size" : "83.5x83.5" - }, - { - "idiom" : "ios-marketing", - "scale" : "1x", - "size" : "1024x1024" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Example App/iOS Example/App/Assets.xcassets/Contents.json b/Example App/iOS Example/App/Assets.xcassets/Contents.json deleted file mode 100644 index 73c0059..0000000 --- a/Example App/iOS Example/App/Assets.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Example App/iOS Example/App/Base.lproj/LaunchScreen.storyboard b/Example App/iOS Example/App/Base.lproj/LaunchScreen.storyboard deleted file mode 100644 index 865e932..0000000 --- a/Example App/iOS Example/App/Base.lproj/LaunchScreen.storyboard +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Example App/iOS Example/Info.plist b/Example App/iOS Example/Info.plist deleted file mode 100644 index a8624ed..0000000 --- a/Example App/iOS Example/Info.plist +++ /dev/null @@ -1,45 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - LSRequiresIPhoneOS - - UIApplicationSupportsIndirectInputEvents - - UILaunchStoryboardName - LaunchScreen - UIRequiredDeviceCapabilities - - armv7 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - - diff --git a/Example App/iOS Example/Scenes/RootController.swift b/Example App/iOS Example/Scenes/RootController.swift deleted file mode 100644 index 7be592b..0000000 --- a/Example App/iOS Example/Scenes/RootController.swift +++ /dev/null @@ -1,29 +0,0 @@ -// The MIT License (MIT) -// Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) -// -// 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 SparrowKit -import NativeUIKit - -class RootController: UIViewController { - - -} diff --git a/Example App/tvOS Example/AppDelegate.swift b/Example App/tvOS Example/AppDelegate.swift deleted file mode 100644 index 01f6b83..0000000 --- a/Example App/tvOS Example/AppDelegate.swift +++ /dev/null @@ -1,5 +0,0 @@ -import UIKit -import SparrowKit - -@main -class AppDelegate: SPAppWindowDelegate {} diff --git a/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json b/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json deleted file mode 100644 index 2e00335..0000000 --- a/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "images" : [ - { - "idiom" : "tv" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json b/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json deleted file mode 100644 index 73c0059..0000000 --- a/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json b/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json deleted file mode 100644 index de59d88..0000000 --- a/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - }, - "layers" : [ - { - "filename" : "Front.imagestacklayer" - }, - { - "filename" : "Middle.imagestacklayer" - }, - { - "filename" : "Back.imagestacklayer" - } - ] -} diff --git a/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json b/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json deleted file mode 100644 index 2e00335..0000000 --- a/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "images" : [ - { - "idiom" : "tv" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json b/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json deleted file mode 100644 index 73c0059..0000000 --- a/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json b/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json deleted file mode 100644 index 2e00335..0000000 --- a/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "images" : [ - { - "idiom" : "tv" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Contents.json b/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Contents.json deleted file mode 100644 index 73c0059..0000000 --- a/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json b/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json deleted file mode 100644 index 795cce1..0000000 --- a/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "images" : [ - { - "idiom" : "tv", - "scale" : "1x" - }, - { - "idiom" : "tv", - "scale" : "2x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json b/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json deleted file mode 100644 index 73c0059..0000000 --- a/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json b/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json deleted file mode 100644 index de59d88..0000000 --- a/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - }, - "layers" : [ - { - "filename" : "Front.imagestacklayer" - }, - { - "filename" : "Middle.imagestacklayer" - }, - { - "filename" : "Back.imagestacklayer" - } - ] -} diff --git a/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json b/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json deleted file mode 100644 index 795cce1..0000000 --- a/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "images" : [ - { - "idiom" : "tv", - "scale" : "1x" - }, - { - "idiom" : "tv", - "scale" : "2x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json b/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json deleted file mode 100644 index 73c0059..0000000 --- a/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json b/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json deleted file mode 100644 index 795cce1..0000000 --- a/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "images" : [ - { - "idiom" : "tv", - "scale" : "1x" - }, - { - "idiom" : "tv", - "scale" : "2x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Contents.json b/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Contents.json deleted file mode 100644 index 73c0059..0000000 --- a/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json b/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json deleted file mode 100644 index f47ba43..0000000 --- a/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "assets" : [ - { - "filename" : "App Icon - App Store.imagestack", - "idiom" : "tv", - "role" : "primary-app-icon", - "size" : "1280x768" - }, - { - "filename" : "App Icon.imagestack", - "idiom" : "tv", - "role" : "primary-app-icon", - "size" : "400x240" - }, - { - "filename" : "Top Shelf Image Wide.imageset", - "idiom" : "tv", - "role" : "top-shelf-image-wide", - "size" : "2320x720" - }, - { - "filename" : "Top Shelf Image.imageset", - "idiom" : "tv", - "role" : "top-shelf-image", - "size" : "1920x720" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json b/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json deleted file mode 100644 index b65f0cd..0000000 --- a/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "images" : [ - { - "idiom" : "tv", - "scale" : "1x" - }, - { - "idiom" : "tv", - "scale" : "2x" - }, - { - "idiom" : "tv-marketing", - "scale" : "1x" - }, - { - "idiom" : "tv-marketing", - "scale" : "2x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json b/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json deleted file mode 100644 index b65f0cd..0000000 --- a/Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "images" : [ - { - "idiom" : "tv", - "scale" : "1x" - }, - { - "idiom" : "tv", - "scale" : "2x" - }, - { - "idiom" : "tv-marketing", - "scale" : "1x" - }, - { - "idiom" : "tv-marketing", - "scale" : "2x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Example App/tvOS Example/Assets.xcassets/Contents.json b/Example App/tvOS Example/Assets.xcassets/Contents.json deleted file mode 100644 index 73c0059..0000000 --- a/Example App/tvOS Example/Assets.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Example App/tvOS Example/Info.plist b/Example App/tvOS Example/Info.plist deleted file mode 100644 index 25869ef..0000000 --- a/Example App/tvOS Example/Info.plist +++ /dev/null @@ -1,34 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - LSRequiresIPhoneOS - - UILaunchStoryboardName - LaunchScreen - UIMainStoryboardFile - Main - UIRequiredDeviceCapabilities - - arm64 - - UIUserInterfaceStyle - Automatic - - diff --git a/Example App/tvOS Example/LaunchScreen.storyboard b/Example App/tvOS Example/LaunchScreen.storyboard deleted file mode 100644 index 195dc8c..0000000 --- a/Example App/tvOS Example/LaunchScreen.storyboard +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Example App/watchOS Example Extension/Assets.xcassets/Complication.complicationset/Circular.imageset/Contents.json b/Example App/watchOS Example Extension/Assets.xcassets/Complication.complicationset/Circular.imageset/Contents.json deleted file mode 100644 index ed7de25..0000000 --- a/Example App/watchOS Example Extension/Assets.xcassets/Complication.complicationset/Circular.imageset/Contents.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "images" : [ - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : "<=145" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">161" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">145" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">183" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Example App/watchOS Example Extension/Assets.xcassets/Complication.complicationset/Contents.json b/Example App/watchOS Example Extension/Assets.xcassets/Complication.complicationset/Contents.json deleted file mode 100644 index e8b3252..0000000 --- a/Example App/watchOS Example Extension/Assets.xcassets/Complication.complicationset/Contents.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "assets" : [ - { - "filename" : "Circular.imageset", - "idiom" : "watch", - "role" : "circular" - }, - { - "filename" : "Extra Large.imageset", - "idiom" : "watch", - "role" : "extra-large" - }, - { - "filename" : "Graphic Bezel.imageset", - "idiom" : "watch", - "role" : "graphic-bezel" - }, - { - "filename" : "Graphic Circular.imageset", - "idiom" : "watch", - "role" : "graphic-circular" - }, - { - "filename" : "Graphic Corner.imageset", - "idiom" : "watch", - "role" : "graphic-corner" - }, - { - "filename" : "Graphic Extra Large.imageset", - "idiom" : "watch", - "role" : "graphic-extra-large" - }, - { - "filename" : "Graphic Large Rectangular.imageset", - "idiom" : "watch", - "role" : "graphic-large-rectangular" - }, - { - "filename" : "Modular.imageset", - "idiom" : "watch", - "role" : "modular" - }, - { - "filename" : "Utilitarian.imageset", - "idiom" : "watch", - "role" : "utilitarian" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Example App/watchOS Example Extension/Assets.xcassets/Complication.complicationset/Extra Large.imageset/Contents.json b/Example App/watchOS Example Extension/Assets.xcassets/Complication.complicationset/Extra Large.imageset/Contents.json deleted file mode 100644 index ed7de25..0000000 --- a/Example App/watchOS Example Extension/Assets.xcassets/Complication.complicationset/Extra Large.imageset/Contents.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "images" : [ - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : "<=145" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">161" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">145" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">183" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Example App/watchOS Example Extension/Assets.xcassets/Complication.complicationset/Graphic Bezel.imageset/Contents.json b/Example App/watchOS Example Extension/Assets.xcassets/Complication.complicationset/Graphic Bezel.imageset/Contents.json deleted file mode 100644 index 9685a7f..0000000 --- a/Example App/watchOS Example Extension/Assets.xcassets/Complication.complicationset/Graphic Bezel.imageset/Contents.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "images" : [ - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">161" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">183" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Example App/watchOS Example Extension/Assets.xcassets/Complication.complicationset/Graphic Circular.imageset/Contents.json b/Example App/watchOS Example Extension/Assets.xcassets/Complication.complicationset/Graphic Circular.imageset/Contents.json deleted file mode 100644 index 9685a7f..0000000 --- a/Example App/watchOS Example Extension/Assets.xcassets/Complication.complicationset/Graphic Circular.imageset/Contents.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "images" : [ - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">161" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">183" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Example App/watchOS Example Extension/Assets.xcassets/Complication.complicationset/Graphic Corner.imageset/Contents.json b/Example App/watchOS Example Extension/Assets.xcassets/Complication.complicationset/Graphic Corner.imageset/Contents.json deleted file mode 100644 index 9685a7f..0000000 --- a/Example App/watchOS Example Extension/Assets.xcassets/Complication.complicationset/Graphic Corner.imageset/Contents.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "images" : [ - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">161" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">183" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Example App/watchOS Example Extension/Assets.xcassets/Complication.complicationset/Graphic Extra Large.imageset/Contents.json b/Example App/watchOS Example Extension/Assets.xcassets/Complication.complicationset/Graphic Extra Large.imageset/Contents.json deleted file mode 100644 index ed7de25..0000000 --- a/Example App/watchOS Example Extension/Assets.xcassets/Complication.complicationset/Graphic Extra Large.imageset/Contents.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "images" : [ - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : "<=145" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">161" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">145" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">183" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Example App/watchOS Example Extension/Assets.xcassets/Complication.complicationset/Graphic Large Rectangular.imageset/Contents.json b/Example App/watchOS Example Extension/Assets.xcassets/Complication.complicationset/Graphic Large Rectangular.imageset/Contents.json deleted file mode 100644 index 9685a7f..0000000 --- a/Example App/watchOS Example Extension/Assets.xcassets/Complication.complicationset/Graphic Large Rectangular.imageset/Contents.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "images" : [ - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">161" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">183" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Example App/watchOS Example Extension/Assets.xcassets/Complication.complicationset/Modular.imageset/Contents.json b/Example App/watchOS Example Extension/Assets.xcassets/Complication.complicationset/Modular.imageset/Contents.json deleted file mode 100644 index ed7de25..0000000 --- a/Example App/watchOS Example Extension/Assets.xcassets/Complication.complicationset/Modular.imageset/Contents.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "images" : [ - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : "<=145" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">161" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">145" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">183" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Example App/watchOS Example Extension/Assets.xcassets/Complication.complicationset/Utilitarian.imageset/Contents.json b/Example App/watchOS Example Extension/Assets.xcassets/Complication.complicationset/Utilitarian.imageset/Contents.json deleted file mode 100644 index ed7de25..0000000 --- a/Example App/watchOS Example Extension/Assets.xcassets/Complication.complicationset/Utilitarian.imageset/Contents.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "images" : [ - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : "<=145" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">161" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">145" - }, - { - "idiom" : "watch", - "scale" : "2x", - "screen-width" : ">183" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Example App/watchOS Example Extension/Assets.xcassets/Contents.json b/Example App/watchOS Example Extension/Assets.xcassets/Contents.json deleted file mode 100644 index 73c0059..0000000 --- a/Example App/watchOS Example Extension/Assets.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Example App/watchOS Example Extension/ExtensionDelegate.swift b/Example App/watchOS Example Extension/ExtensionDelegate.swift deleted file mode 100644 index 0f219f4..0000000 --- a/Example App/watchOS Example Extension/ExtensionDelegate.swift +++ /dev/null @@ -1,3 +0,0 @@ -import WatchKit - -class ExtensionDelegate: NSObject, WKExtensionDelegate {} diff --git a/Example App/watchOS Example Extension/Info.plist b/Example App/watchOS Example Extension/Info.plist deleted file mode 100644 index ae74f16..0000000 --- a/Example App/watchOS Example Extension/Info.plist +++ /dev/null @@ -1,36 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - CLKComplicationPrincipalClass - $(PRODUCT_MODULE_NAME).ComplicationController - NSExtension - - NSExtensionAttributes - - WKAppBundleIdentifier - by.ivanvorobei.opensource.nativeuikit.ios.watchkitapp - - NSExtensionPointIdentifier - com.apple.watchkit - - WKExtensionDelegateClassName - $(PRODUCT_MODULE_NAME).ExtensionDelegate - - diff --git a/Example App/watchOS Example/Assets.xcassets/AccentColor.colorset/Contents.json b/Example App/watchOS Example/Assets.xcassets/AccentColor.colorset/Contents.json deleted file mode 100644 index eb87897..0000000 --- a/Example App/watchOS Example/Assets.xcassets/AccentColor.colorset/Contents.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "colors" : [ - { - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Example App/watchOS Example/Assets.xcassets/AppIcon.appiconset/Contents.json b/Example App/watchOS Example/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index d06b66a..0000000 --- a/Example App/watchOS Example/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "images" : [ - { - "idiom" : "watch", - "role" : "notificationCenter", - "scale" : "2x", - "size" : "24x24", - "subtype" : "38mm" - }, - { - "idiom" : "watch", - "role" : "notificationCenter", - "scale" : "2x", - "size" : "27.5x27.5", - "subtype" : "42mm" - }, - { - "idiom" : "watch", - "role" : "companionSettings", - "scale" : "2x", - "size" : "29x29" - }, - { - "idiom" : "watch", - "role" : "companionSettings", - "scale" : "3x", - "size" : "29x29" - }, - { - "idiom" : "watch", - "role" : "appLauncher", - "scale" : "2x", - "size" : "40x40", - "subtype" : "38mm" - }, - { - "idiom" : "watch", - "role" : "appLauncher", - "scale" : "2x", - "size" : "44x44", - "subtype" : "40mm" - }, - { - "idiom" : "watch", - "role" : "appLauncher", - "scale" : "2x", - "size" : "50x50", - "subtype" : "44mm" - }, - { - "idiom" : "watch", - "role" : "quickLook", - "scale" : "2x", - "size" : "86x86", - "subtype" : "38mm" - }, - { - "idiom" : "watch", - "role" : "quickLook", - "scale" : "2x", - "size" : "98x98", - "subtype" : "42mm" - }, - { - "idiom" : "watch", - "role" : "quickLook", - "scale" : "2x", - "size" : "108x108", - "subtype" : "44mm" - }, - { - "idiom" : "watch-marketing", - "scale" : "1x", - "size" : "1024x1024" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Example App/watchOS Example/Assets.xcassets/Contents.json b/Example App/watchOS Example/Assets.xcassets/Contents.json deleted file mode 100644 index 73c0059..0000000 --- a/Example App/watchOS Example/Assets.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Example App/watchOS Example/Base.lproj/Interface.storyboard b/Example App/watchOS Example/Base.lproj/Interface.storyboard deleted file mode 100644 index cf05d24..0000000 --- a/Example App/watchOS Example/Base.lproj/Interface.storyboard +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/Example App/watchOS Example/Info.plist b/Example App/watchOS Example/Info.plist deleted file mode 100644 index ac58b18..0000000 --- a/Example App/watchOS Example/Info.plist +++ /dev/null @@ -1,31 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - - WKCompanionAppBundleIdentifier - by.ivanvorobei.opensource.nativeuikit.ios - WKWatchKitApp - - - diff --git a/LICENSE b/LICENSE index a943a88..a0468ed 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021 Ivan Vorobei +Copyright (c) 2022 Sparrow Code Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Sources/NativeUIKit/Bars/NativeBorderedView.swift b/Sources/NativeUIKit/Bars/NativeBorderedView.swift deleted file mode 100644 index 9eb59d5..0000000 --- a/Sources/NativeUIKit/Bars/NativeBorderedView.swift +++ /dev/null @@ -1,114 +0,0 @@ -// The MIT License (MIT) -// Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) -// -// 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. - -#if canImport(UIKit) && (os(iOS)) -import UIKit -import SparrowKit - -/** - NativeUIKit: View with porder on some position. - For change position check property. - */ -open class NativeBorderedView: SPView { - - // MARK: - Data - - /** - NativeUIKit: Border Position. - */ - open var position: Position = .top { - didSet { - layoutSubviews() - } - } - - // MARK: - Views - - private let borderView = SPView().do { - if #available(iOS 13.0, *) { - $0.backgroundColor = .separator - } else { - $0.backgroundColor = .systemGray - } - } - - // MARK: - Init - - public init(position: Position) { - self.position = position - super.init() - } - - public required init?(coder aDecoder: NSCoder) { - super.init(coder: aDecoder) - } - - open override func commonInit() { - super.commonInit() - addSubview(borderView) - } - - // MARK: - Actions - - /** - NativeUIKit: Show or hide border. - - - parameter visible: New visible state of border. - - parameter animated: State for apply changes animatable or not. - */ - open func setBorderVisible(_ visible: Bool, animated: Bool) { - let work = { [weak self] in - guard let self = self else { return } - self.borderView.alpha = visible ? 1 : 0 - } - if animated { - UIView.animate(withDuration: 0.3, delay: 0, options: [.beginFromCurrentState, .allowUserInteraction], animations: { - work() - }) - } else { - work() - } - } - - // MARK: - Layout - - open override func layoutSubviews() { - super.layoutSubviews() - borderView.frame.setWidth(frame.width) - borderView.frame.setHeight(0.5) - borderView.frame.origin.x = 0 - switch position { - case .top: - borderView.frame.origin.y = 0 - case .bottom: - borderView.frame.setMaxY(frame.height) - } - } - - // MARK: - Models - - public enum Position { - - case top - case bottom - } -} -#endif diff --git a/Sources/NativeUIKit/Bars/NativeMimicrateBarView.swift b/Sources/NativeUIKit/Bars/NativeMimicrateBarView.swift deleted file mode 100644 index 32543f8..0000000 --- a/Sources/NativeUIKit/Bars/NativeMimicrateBarView.swift +++ /dev/null @@ -1,78 +0,0 @@ -// The MIT License (MIT) -// Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) -// -// 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. - -#if canImport(UIKit) && (os(iOS)) -import UIKit -import SparrowKit - -/** - NativeUIKit: View with mimicrate to navigation and toolbar views. - */ -open class NativeMimicrateBarView: SPView { - - // MARK: - Views - - public let borderedView: NativeBorderedView - public let backgroundView: UIVisualEffectView - - // MARK: - Init - - public init(borderPosition: NativeBorderedView.Position) { - self.borderedView = NativeBorderedView(position: borderPosition) - self.backgroundView = UIVisualEffectView.init(style: .regular) - super.init() - } - - public required init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - open override func commonInit() { - super.commonInit() - addSubview(backgroundView) - addSubview(borderedView) - setVisible(progress: 1) - } - - // MARK: - Actions - - /** - NativeUIKit: Show or hide bar. - - - parameter progress: Value in range from `0` to `1` to visible or not view. - */ - open func setVisible(progress: CGFloat) { - var alpha: CGFloat = progress - if progress < 0 { alpha = 0 } - if progress > 1 { alpha = 1 } - self.borderedView.alpha = alpha - self.backgroundView.alpha = alpha - } - - // MARK: - Layout - - open override func layoutSubviews() { - super.layoutSubviews() - backgroundView.setEqualSuperviewBounds() - borderedView.setEqualSuperviewBounds() - } -} -#endif diff --git a/Sources/NativeUIKit/Bars/NativeMimicrateNavigationBarView.swift b/Sources/NativeUIKit/Bars/NativeMimicrateNavigationBarView.swift deleted file mode 100644 index b378472..0000000 --- a/Sources/NativeUIKit/Bars/NativeMimicrateNavigationBarView.swift +++ /dev/null @@ -1,38 +0,0 @@ -// The MIT License (MIT) -// Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) -// -// 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. - -#if canImport(UIKit) && (os(iOS)) -import UIKit -import SparrowKit - -open class NativeMimicrateNavigationBarView: NativeMimicrateBarView { - - // MARK: - Init - - public init() { - super.init(borderPosition: .bottom) - } - - public required init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} -#endif diff --git a/Sources/NativeUIKit/Bars/ToolBar/LargeAction/NativeLargeActionToolBarView.swift b/Sources/NativeUIKit/Bars/ToolBar/LargeAction/NativeLargeActionToolBarView.swift deleted file mode 100644 index 04c77d9..0000000 --- a/Sources/NativeUIKit/Bars/ToolBar/LargeAction/NativeLargeActionToolBarView.swift +++ /dev/null @@ -1,100 +0,0 @@ -// The MIT License (MIT) -// Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) -// -// 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. - -#if canImport(UIKit) && (os(iOS)) -import UIKit -import SparrowKit - -open class NativeLargeActionToolBarView: NativeMimicrateToolBarView { - - // MARK: - Views - - public let activityIndicatorView = UIActivityIndicatorView() - - public let actionButton = NativeLargeActionButton().do { - $0.applyDefaultAppearance(with: .tintedColorful) - } - - public let footerLabel = SPLabel().do { - $0.font = .preferredFont(forTextStyle: .footnote) - $0.numberOfLines = .zero - if #available(iOS 13.0, *) { - $0.textColor = .secondaryLabel - } else { - $0.textColor = .black.alpha(0.5) - } - } - - // MARK: - Init - - open override func commonInit() { - super.commonInit() - addSubview(activityIndicatorView) - addSubview(actionButton) - addSubview(footerLabel) - } - - // MARK: - Actions - - open func setLoading(_ state: Bool) { - if state { - activityIndicatorView.startAnimating() - actionButton.isHidden = true - footerLabel.isHidden = true - } else { - activityIndicatorView.stopAnimating() - actionButton.isHidden = false - footerLabel.isHidden = false - } - } - - // MARK: - Layout - - internal let footerLeftInset: CGFloat = 20 - - open override func layoutSubviews() { - super.layoutSubviews() - actionButton.layout(y: layoutMargins.top) - if footerLabel.text != nil { - footerLabel.layoutDynamicHeight(x: layoutMargins.left + footerLeftInset, y: actionButton.frame.maxY + 12, width: layoutWidth - footerLeftInset) - } - - let contentHeight: CGFloat = { - if footerLabel.text == nil { - return actionButton.frame.maxY - } else { - return footerLabel.frame.maxY - } - }() - activityIndicatorView.setXCenter() - activityIndicatorView.center.y = contentHeight / 2 - } - - open override func sizeThatFits(_ size: CGSize) -> CGSize { - layoutSubviews() - if footerLabel.text == nil { - return .init(width: size.width, height: actionButton.frame.maxY + layoutMargins.bottom) - } else { - return .init(width: size.width, height: footerLabel.frame.maxY + layoutMargins.bottom) - } - } -} -#endif diff --git a/Sources/NativeUIKit/Bars/ToolBar/LargeAction/NativeLargeSmallActionToolBarView.swift b/Sources/NativeUIKit/Bars/ToolBar/LargeAction/NativeLargeSmallActionToolBarView.swift deleted file mode 100644 index 4043d54..0000000 --- a/Sources/NativeUIKit/Bars/ToolBar/LargeAction/NativeLargeSmallActionToolBarView.swift +++ /dev/null @@ -1,63 +0,0 @@ -// The MIT License (MIT) -// Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) -// -// 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. - -#if canImport(UIKit) && (os(iOS)) -import UIKit -import SparrowKit - -open class NativeLargeSmallActionToolBarView: NativeLargeActionToolBarView { - - // MARK: - Views - - public let secondActionButton = SPDimmedButton().do { - $0.applyDefaultAppearance(with: .tintedContent) - $0.titleLabel?.font = UIFont.preferredFont(forTextStyle: .headline, addPoints: -1) - } - - // MARK: - Init - - open override func commonInit() { - super.commonInit() - addSubview(secondActionButton) - } - - // MARK: - Actions - - open override func setLoading(_ state: Bool) { - super.setLoading(state) - secondActionButton.isHidden = state - } - - // MARK: - Layout - - open override func layoutSubviews() { - super.layoutSubviews() - secondActionButton.setWidthAndFit(width: layoutWidth) - secondActionButton.frame.origin.y = actionButton.frame.maxY + 12 - secondActionButton.setXCenter() - } - - open override func sizeThatFits(_ size: CGSize) -> CGSize { - layoutSubviews() - return .init(width: size.width, height: secondActionButton.frame.maxY + layoutMargins.bottom) - } -} -#endif diff --git a/Sources/NativeUIKit/Bars/ToolBar/LargeAction/NativeSkipableLargeActionToolBarView.swift b/Sources/NativeUIKit/Bars/ToolBar/LargeAction/NativeSkipableLargeActionToolBarView.swift deleted file mode 100644 index 1a9bdd5..0000000 --- a/Sources/NativeUIKit/Bars/ToolBar/LargeAction/NativeSkipableLargeActionToolBarView.swift +++ /dev/null @@ -1,63 +0,0 @@ -// The MIT License (MIT) -// Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) -// -// 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. - -#if canImport(UIKit) && (os(iOS)) -import UIKit -import SparrowKit - -open class NativeSkipableLargeActionToolBarView: NativeLargeActionToolBarView { - - // MARK: - Views - - public let skipButton = SPDimmedButton().do { - $0.applyDefaultAppearance(with: .init(content: .systemGray, background: .clear)) - $0.titleLabel?.font = UIFont.preferredFont(forTextStyle: .body, weight: .semibold) - } - - // MARK: - Init - - open override func commonInit() { - super.commonInit() - addSubview(skipButton) - } - - // MARK: - Actions - - open override func setLoading(_ state: Bool) { - super.setLoading(state) - skipButton.isHidden = state - } - - // MARK: - Layout - - open override func layoutSubviews() { - super.layoutSubviews() - skipButton.setWidthAndFit(width: layoutWidth) - skipButton.frame.origin.y = actionButton.frame.maxY + 12 - skipButton.setXCenter() - } - - open override func sizeThatFits(_ size: CGSize) -> CGSize { - layoutSubviews() - return .init(width: size.width, height: skipButton.frame.maxY + layoutMargins.bottom) - } -} -#endif diff --git a/Sources/NativeUIKit/Bars/ToolBar/NativeAppleAuthToolBarView.swift b/Sources/NativeUIKit/Bars/ToolBar/NativeAppleAuthToolBarView.swift deleted file mode 100644 index 7607901..0000000 --- a/Sources/NativeUIKit/Bars/ToolBar/NativeAppleAuthToolBarView.swift +++ /dev/null @@ -1,75 +0,0 @@ -// The MIT License (MIT) -// Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) -// -// 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. - -#if canImport(UIKit) && canImport(AuthenticationServices) && (os(iOS)) -import UIKit -import SparrowKit -import AuthenticationServices - -@available(iOS 13.0, *) -open class NativeAppleAuthToolBarView: NativeMimicrateToolBarView { - - // MARK: - Views - - public let authButton = ASAuthorizationAppleIDButton() - - public let footerLabel = SPLabel().do { - $0.font = .preferredFont(forTextStyle: .footnote) - $0.numberOfLines = .zero - $0.textColor = .secondaryLabel - } - - // MARK: - Init - - open override func commonInit() { - super.commonInit() - addSubview(authButton) - addSubview(footerLabel) - } - - // MARK: - Layout - - internal let footerLeftInset: CGFloat = 20 - - open override func layoutSubviews() { - super.layoutSubviews() - - let authButtonWidth = min(readableWidth, NativeLayout.Sizes.actionable_area_maximum_width) - authButton.frame.setWidth(authButtonWidth) - authButton.frame.setHeight(52) - authButton.setXCenter() - authButton.frame.origin.y = layoutMargins.top - - if footerLabel.text != nil { - footerLabel.layoutDynamicHeight(x: layoutMargins.left + footerLeftInset, y: authButton.frame.maxY + 12, width: layoutWidth - footerLeftInset) - } - } - - open override func sizeThatFits(_ size: CGSize) -> CGSize { - layoutSubviews() - if footerLabel.text == nil { - return .init(width: size.width, height: authButton.frame.maxY + layoutMargins.bottom) - } else { - return .init(width: size.width, height: footerLabel.frame.maxY + layoutMargins.bottom) - } - } -} -#endif diff --git a/Sources/NativeUIKit/Bars/ToolBar/NativeMimicrateNavigationToolBarView.swift b/Sources/NativeUIKit/Bars/ToolBar/NativeMimicrateNavigationToolBarView.swift deleted file mode 100644 index 1878046..0000000 --- a/Sources/NativeUIKit/Bars/ToolBar/NativeMimicrateNavigationToolBarView.swift +++ /dev/null @@ -1,55 +0,0 @@ -// The MIT License (MIT) -// Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) -// -// 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. - -#if canImport(UIKit) && (os(iOS)) -import UIKit -import SparrowKit - -open class NativeMimicrateToolBarView: NativeMimicrateBarView { - - // MARK: - Init - - public init() { - super.init(borderPosition: .top) - } - - public required init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - open override func commonInit() { - super.commonInit() - insetsLayoutMarginsFromSafeArea = false - layoutMargins.top = 16 - } - - // MARK: - Layout - - open override func layoutSubviews() { - super.layoutSubviews() - guard let superview = superview else { return } - let contentWidth = min(440, superview.readableWidth) - let horizontalMargin = (frame.width - contentWidth) / 2 - layoutMargins.left = horizontalMargin - layoutMargins.right = horizontalMargin - } -} -#endif diff --git a/Sources/NativeUIKit/Bars/ToolBar/NativeSmallActionToolBarView.swift b/Sources/NativeUIKit/Bars/ToolBar/NativeSmallActionToolBarView.swift deleted file mode 100644 index 68ab3d8..0000000 --- a/Sources/NativeUIKit/Bars/ToolBar/NativeSmallActionToolBarView.swift +++ /dev/null @@ -1,54 +0,0 @@ -// The MIT License (MIT) -// Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) -// -// 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. - -#if canImport(UIKit) && (os(iOS)) -import UIKit -import SparrowKit - -open class NativeSmallActionToolBarView: NativeMimicrateToolBarView { - - public let actionButton = SPDimmedButton().do { - $0.applyDefaultAppearance(with: .tintedContent) - $0.titleLabel?.font = UIFont.preferredFont(forTextStyle: .title3, weight: .semibold) - } - - // MARK: - Init - - open override func commonInit() { - super.commonInit() - addSubview(actionButton) - } - - // MARK: - Layout - - open override func layoutSubviews() { - super.layoutSubviews() - actionButton.setWidthAndFit(width: layoutWidth) - actionButton.frame.origin.y = layoutMargins.top + 16 - actionButton.setXCenter() - } - - open override func sizeThatFits(_ size: CGSize) -> CGSize { - layoutSubviews() - return .init(width: size.width, height: actionButton.frame.maxY + layoutMargins.bottom) - } -} -#endif diff --git a/Sources/NativeUIKit/Collection/LargeHeader/NativeLargeHeaderCollectionView.swift b/Sources/NativeUIKit/Collection/LargeHeader/NativeLargeHeaderCollectionView.swift deleted file mode 100644 index 1bf86cb..0000000 --- a/Sources/NativeUIKit/Collection/LargeHeader/NativeLargeHeaderCollectionView.swift +++ /dev/null @@ -1,54 +0,0 @@ -// The MIT License (MIT) -// Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) -// -// 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. - -#if canImport(UIKit) && (os(iOS)) -import UIKit -import SparrowKit - -open class NativeLargeHeaderCollectionView: SPCollectionReusableView { - - public let headerView = NativeLargeHeaderView() - - open override func commonInit() { - super.commonInit() - insetsLayoutMarginsFromSafeArea = false - layoutMargins = .zero - addSubview(headerView) - } - - open override func layoutSubviews() { - super.layoutSubviews() - headerView.layout(y: layoutMargins.top) - } - - open override func sizeThatFits(_ size: CGSize) -> CGSize { - let superSize = super.sizeThatFits(size) - return .init(width: superSize.width, height: headerView.frame.height + layoutMargins.bottom) - } - - static public func size(for item: NativeLargeHeaderItem, in collectionView: UICollectionView) -> CGSize { - let view = NativeLargeHeaderView() - view.configure(with: item, section: .zero) - view.setWidthAndFit(width: collectionView.layoutWidth) - return .init(width: collectionView.frame.width, height: view.frame.height) - } -} -#endif diff --git a/Sources/NativeUIKit/Collection/LargeHeader/SPDiffableCollectionHeaderFooterProvider+LargeHeader.swift b/Sources/NativeUIKit/Collection/LargeHeader/SPDiffableCollectionHeaderFooterProvider+LargeHeader.swift deleted file mode 100644 index 67b20bb..0000000 --- a/Sources/NativeUIKit/Collection/LargeHeader/SPDiffableCollectionHeaderFooterProvider+LargeHeader.swift +++ /dev/null @@ -1,38 +0,0 @@ -// The MIT License (MIT) -// Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) -// -// 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 SPDiffable - -@available(iOS 13.0, tvOS 13.0, *) -extension SPDiffableCollectionDataSource.HeaderFooterProvider { - - #if (os(iOS)) - public static var largeHeader: SPDiffableCollectionDataSource.HeaderFooterProvider { - return SPDiffableCollectionDataSource.HeaderFooterProvider.init { collectionView, kind, indexPath, item in - guard let header = item as? NativeLargeHeaderItem else { return nil } - let view = collectionView.dequeueReusableSupplementaryView(withCalss: NativeLargeHeaderCollectionView.self, kind: kind, for: indexPath) - view.headerView.configure(with: header, section: indexPath.section) - return view - } - } - #endif -} diff --git a/Sources/NativeUIKit/Collection/NativePromoCollectionViewCell.swift b/Sources/NativeUIKit/Collection/NativePromoCollectionViewCell.swift deleted file mode 100644 index 98366fb..0000000 --- a/Sources/NativeUIKit/Collection/NativePromoCollectionViewCell.swift +++ /dev/null @@ -1,59 +0,0 @@ -// The MIT License (MIT) -// Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) -// -// 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. - -#if canImport(UIKit) && (os(iOS)) -import UIKit -import SparrowKit -import SPPerspective - -open class NativePromoCollectionViewCell: SPCollectionViewCell { - - // MARK: - Views - - public let promoView = NativePromoView() - - // MARK: - Init - - open override func commonInit() { - super.commonInit() - contentView.layoutMargins = .zero - contentView.addSubview(promoView) - } - - open override func prepareForReuse() { - super.prepareForReuse() - promoView.iconView.resetPerspective() - } - - // MARK: - Layout - - open override func layoutSubviews() { - super.layoutSubviews() - promoView.layout(y: contentView.layoutMargins.top) - } - - open override func sizeThatFits(_ size: CGSize) -> CGSize { - let superSize = super.sizeThatFits(size) - layoutSubviews() - return .init(width: superSize.width, height: promoView.frame.maxY + contentView.layoutMargins.bottom) - } -} -#endif diff --git a/Sources/NativeUIKit/Controllers/Complex/Onboarding/Actinos/NativeOnbiardingActionButton.swift b/Sources/NativeUIKit/Controllers/Complex/Onboarding/Actinos/NativeOnbiardingActionButton.swift deleted file mode 100644 index 0bb62f2..0000000 --- a/Sources/NativeUIKit/Controllers/Complex/Onboarding/Actinos/NativeOnbiardingActionButton.swift +++ /dev/null @@ -1,151 +0,0 @@ -// The MIT License (MIT) -// Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) -// -// 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. - -#if canImport(UIKit) && (os(iOS)) -import UIKit -import SparrowKit - -public class NativeOnbiardingActionButton: SPDimmedButton { - - // MARK: - Data - - private let model: ActionModel - - // MARK: - Views - - let disclouserIndicator = SPDimmedButton().do { - if #available(iOS 13, *) { - $0.setImage(.system("chevron.right", font: .preferredFont(forTextStyle: .body, weight: .medium)).alwaysTemplate) - $0.tintColor = .tertiaryLabel - } - } - - let actionIconView = SPImageView().do { - $0.contentMode = .scaleAspectFit - } - - let actionTitleLabel = SPLabel().do { - $0.font = .preferredFont(forTextStyle: .body, weight: .semibold) - if #available(iOS 13.0, *) { - $0.textColor = .label - } else { - $0.textColor = .black - } - $0.numberOfLines = .zero - } - - let actionDescriptionLabel = SPLabel().do { - $0.font = .preferredFont(forTextStyle: .subheadline) - if #available(iOS 13.0, *) { - $0.textColor = .secondaryLabel - } else { - $0.textColor = .black - } - $0.numberOfLines = .zero - } - - // MARK: - Init - - init(with model: ActionModel) { - self.model = model - super.init() - actionIconView.image = model.iconImage - actionTitleLabel.text = model.title - actionDescriptionLabel.text = model.description - - addTarget(self, action: #selector(self.didTap), for: .touchUpInside) - } - - required init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - public override func commonInit() { - super.commonInit() - higlightStyle = .background - if #available(iOS 13.0, *) { - applyDefaultAppearance(with: .init(content: .label, background: .secondarySystemBackground)) - } - roundCorners(radius: NativeLayout.Spaces.default_less) - layoutMargins = .init(horizontal: NativeLayout.Spaces.default_more, vertical: NativeLayout.Spaces.default_more) - addSubviews(actionIconView, actionTitleLabel, actionDescriptionLabel, disclouserIndicator) - } - - // MARK: - Layout - - public override func layoutSubviews() { - super.layoutSubviews() - actionIconView.frame = .init(side: 28) - actionIconView.frame.origin.x = layoutMargins.left - - disclouserIndicator.sizeToFit() - disclouserIndicator.setMaxXToSuperviewRightMargin() - - let leftSpace: CGFloat = NativeLayout.Spaces.default_more - let rightSpace: CGFloat = NativeLayout.Spaces.default - let labelWidth = layoutWidth - actionIconView.frame.width - disclouserIndicator.frame.width - leftSpace - rightSpace - - actionTitleLabel.layoutDynamicHeight(width: labelWidth) - actionTitleLabel.frame.origin.x = actionIconView.frame.maxX + leftSpace - actionTitleLabel.frame.origin.y = layoutMargins.top - - actionDescriptionLabel.layoutDynamicHeight(width: labelWidth) - actionDescriptionLabel.frame.origin.x = actionTitleLabel.frame.origin.x - actionDescriptionLabel.frame.origin.y = actionTitleLabel.frame.maxY + NativeLayout.Spaces.step - - disclouserIndicator.setYCenter() - actionIconView.setYCenter() - } - - public override func sizeThatFits(_ size: CGSize) -> CGSize { - layoutSubviews() - return .init(width: size.width, height: actionDescriptionLabel.frame.maxY + layoutMargins.bottom) - } - - // MARK: - Action - - @objc func didTap() { - self.model.action() - } - - // MARK: - Models - - /** - Wrapper of data for action model. - - - important: Recomended save app tint color for icons like native. - */ - public class ActionModel { - - let iconImage: UIImage - let title: String - let description: String - let action: ()->Void - - public init(iconImage: UIImage, title: String, description: String, action: @escaping ()->Void) { - self.iconImage = iconImage - self.title = title - self.description = description - self.action = action - } - } -} -#endif diff --git a/Sources/NativeUIKit/Controllers/Complex/Onboarding/Actinos/NativeOnboardingActionsController.swift b/Sources/NativeUIKit/Controllers/Complex/Onboarding/Actinos/NativeOnboardingActionsController.swift deleted file mode 100644 index 560a551..0000000 --- a/Sources/NativeUIKit/Controllers/Complex/Onboarding/Actinos/NativeOnboardingActionsController.swift +++ /dev/null @@ -1,85 +0,0 @@ -// The MIT License (MIT) -// Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) -// -// 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. - -#if canImport(UIKit) && (os(iOS)) -import UIKit -import SparrowKit - -open class NativeOnboardingActionsController: NativeHeaderController { - - private var views: [NativeOnbiardingActionButton] = [] - - // MARK: - Init - - public init( - iconImage: UIImage?, - title: String, - subtitle: String - ) { - super.init(image: iconImage, title: title, subtitle: subtitle) - } - - public required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - // MARK: - Lifecycle - - open override func viewDidLoad() { - super.viewDidLoad() - if #available(iOS 13.0, *) { - self.view.backgroundColor = .systemBackground - } else { - view.backgroundColor = .white - } - scrollView.addSubviews(views) - } - - open func setActions(_ models: [NativeOnbiardingActionButton.ActionModel]) { - // Clean old - views.forEach({ $0.removeFromSuperview() }) - views.removeAll() - - // Add new - views = models.map({ NativeOnbiardingActionButton(with: $0) }) - - // Added like subviews - if isViewLoaded { - scrollView.addSubviews(views) - } - } - - // MARK: - Layout - - open override func viewDidLayoutSubviews() { - super.viewDidLayoutSubviews() - var currentYPosition = headerView.frame.maxY + NativeLayout.Spaces.default_double - let elementWidth: CGFloat = min(scrollView.readableWidth, 320) - for (_, itemView) in views.enumerated() { - itemView.setWidthAndFit(width: elementWidth) - itemView.setXCenter() - itemView.frame.origin.y = currentYPosition - currentYPosition = itemView.frame.maxY + NativeLayout.Spaces.default_half - } - scrollView.contentSize = .init(width: scrollView.frame.width, height: views.last?.frame.maxY ?? .zero) - } -} -#endif diff --git a/Sources/NativeUIKit/Controllers/Complex/Onboarding/Features/NativeOnboardingFeatureView.swift b/Sources/NativeUIKit/Controllers/Complex/Onboarding/Features/NativeOnboardingFeatureView.swift deleted file mode 100644 index 85368a3..0000000 --- a/Sources/NativeUIKit/Controllers/Complex/Onboarding/Features/NativeOnboardingFeatureView.swift +++ /dev/null @@ -1,132 +0,0 @@ -// The MIT License (MIT) -// Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) -// -// 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. - -#if canImport(UIKit) && (os(iOS)) -import UIKit -import SparrowKit - -public class NativeOnboardingFeatureView: SPView { - - // MARK: - Data - - let model: FeatureModel - - // MARK: - Views - - let iconView = SPImageView().do { - $0.contentMode = .scaleAspectFit - } - - let titleLabel = SPLabel().do { - $0.font = .preferredFont(forTextStyle: .title3, weight: .semibold) - if #available(iOS 13.0, *) { - $0.textColor = .label - } else { - $0.textColor = .black - } - $0.numberOfLines = .zero - } - - let descriptionLabel = SPLabel().do { - $0.font = .preferredFont(forTextStyle: .body) - if #available(iOS 13.0, *) { - $0.textColor = .secondaryLabel - } else { - $0.textColor = .black - } - $0.numberOfLines = .zero - } - - // MARK: - Init - - init(with model: FeatureModel) { - self.model = model - super.init() - iconView.image = model.iconImage - titleLabel.text = model.title - descriptionLabel.text = model.description - } - - required init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - public override func commonInit() { - super.commonInit() - backgroundColor = .clear - roundCorners(radius: NativeLayout.Spaces.default_less) - layoutMargins = .init(horizontal: NativeLayout.Spaces.default_more, vertical: NativeLayout.Spaces.default_more) - addSubviews(iconView, titleLabel, descriptionLabel) - } - - // MARK: - Layout - - public override func layoutSubviews() { - super.layoutSubviews() - iconView.frame = .init(side: 42) - iconView.frame.origin.y = layoutMargins.top - iconView.frame.origin.x = layoutMargins.left - - let labelWidth = layoutWidth - iconView.frame.width - NativeLayout.Spaces.default - - titleLabel.layoutDynamicHeight(width: labelWidth) - titleLabel.setMaxXToSuperviewRightMargin() - titleLabel.frame.origin.y = layoutMargins.top - - descriptionLabel.layoutDynamicHeight(width: labelWidth) - descriptionLabel.frame.origin.x = titleLabel.frame.origin.x - descriptionLabel.frame.origin.y = titleLabel.frame.maxY + NativeLayout.Spaces.step - } - - public override func sizeThatFits(_ size: CGSize) -> CGSize { - layoutSubviews() - return .init(width: size.width, height: descriptionLabel.frame.maxY + layoutMargins.bottom) - } - - // MARK: - Actions - - func setProgress(_ value: CGFloat) { - if #available(iOS 13.0, *) { - self.backgroundColor = .secondarySystemBackground.alpha(1 - value) - } - } - - // MARK: - Models - - /** - Wrapper of data for feature model. - - - important: Recomended use custom tint color for icons like native. - */ - public class FeatureModel { - - let iconImage: UIImage - let title: String - let description: String - - public init(iconImage: UIImage, title: String, description: String) { - self.iconImage = iconImage - self.title = title - self.description = description - } - } -} -#endif diff --git a/Sources/NativeUIKit/Controllers/Complex/Onboarding/Features/NativeOnboardingFeaturesController.swift b/Sources/NativeUIKit/Controllers/Complex/Onboarding/Features/NativeOnboardingFeaturesController.swift deleted file mode 100644 index a3836c1..0000000 --- a/Sources/NativeUIKit/Controllers/Complex/Onboarding/Features/NativeOnboardingFeaturesController.swift +++ /dev/null @@ -1,102 +0,0 @@ -// The MIT License (MIT) -// Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) -// -// 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. - -#if canImport(UIKit) && (os(iOS)) -import UIKit -import SparrowKit - -open class NativeOnboardingFeaturesController: NativeHeaderController { - - private var views: [NativeOnboardingFeatureView] = [] - - // MARK: - Init - - public init( - iconImage: UIImage?, - title: String, - subtitle: String - ) { - super.init(image: iconImage, title: title, subtitle: subtitle) - } - - public required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - // MARK: - Lifecycle - - open override func viewDidLoad() { - super.viewDidLoad() - if #available(iOS 13.0, *) { - self.view.backgroundColor = .systemBackground - } else { - view.backgroundColor = .white - } - scrollView.addSubviews(views) - } - - open func setFeatures(_ models: [NativeOnboardingFeatureView.FeatureModel]) { - // Clean old - views.forEach({ $0.removeFromSuperview() }) - views.removeAll() - - // Add new - views = models.map({ NativeOnboardingFeatureView(with: $0) }) - - // Added like subviews - if isViewLoaded { - scrollView.addSubviews(views) - } - } - - // MARK: - Layout - - open override func viewDidLayoutSubviews() { - super.viewDidLayoutSubviews() - var currentYPosition = headerView.frame.maxY + NativeLayout.Spaces.default_double - for (_, itemView) in views.enumerated() { - itemView.setWidthAndFit(width: scrollView.readableWidth) - itemView.setXCenter() - itemView.frame.origin.y = currentYPosition - currentYPosition = itemView.frame.maxY + NativeLayout.Spaces.default_half - } - scrollView.contentSize = .init(width: scrollView.frame.width, height: views.last?.frame.maxY ?? .zero) - } - - public func scrollViewDidScroll(_ scrollView: UIScrollView) { - for itemView in views { - let progress = progressForFeatureView(itemView) - itemView.setProgress(progress) - } - } - - private func progressForFeatureView(_ view: NativeOnboardingFeatureView) -> CGFloat { - let offsetY = scrollView.contentOffset.y + scrollView.safeAreaInsets.top + scrollView.frame.height - scrollView.safeAreaInsets.bottom - let correction: CGFloat = NativeLayout.Spaces.Scroll.bottom_inset_reach_end - let topSafeArea = scrollView.safeAreaInsets.top + correction - let startPosition = view.frame.origin.y + topSafeArea - let progress = (offsetY - startPosition) / view.frame.height - if progress < .zero { return .zero } - if progress > 1 { return 1 } - return progress - } -} -#endif diff --git a/Sources/NativeUIKit/Controllers/Complex/Onboarding/NativeOnboardingController.swift b/Sources/NativeUIKit/Controllers/Complex/Onboarding/NativeOnboardingController.swift deleted file mode 100644 index 8837747..0000000 --- a/Sources/NativeUIKit/Controllers/Complex/Onboarding/NativeOnboardingController.swift +++ /dev/null @@ -1,86 +0,0 @@ -// The MIT License (MIT) -// Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) -// -// 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. - -#if canImport(UIKit) && (os(iOS)) -import UIKit -import SparrowKit -import SPPageController - -open class NativeOnboardingController: SPPageController, NativeOnboardingManagerDelegate { - - private let controllers: [UIViewController] - private let completion: SPCompletion - - // MARK: - Init - - public init(controllers: [UIViewController], completion: @escaping SPCompletion) { - - self.controllers = controllers - self.completion = completion - - var childControllers: [UIViewController] = [] - for controller in controllers { - let navigationController = NativeNavigationController(rootViewController: controller) - navigationController.inheritLayoutMarginsForСhilds = true - navigationController.inheritLayoutMarginsForNavigationBar = true - navigationController.view.preservesSuperviewLayoutMargins = true - childControllers.append(navigationController) - } - - super.init(childControllers: childControllers, system: .page) - - controllers.forEach { controller in - let onboardingChildController = controller as! NativeOnboardingChildInterface - onboardingChildController.onboardingManagerDelegate = self - } - - allowScroll = false - allowDismissWithGester = false - } - - public required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - // MARK: - OnboardingManagerDelegate - - public func onboardingActionComplete(for controller: UIViewController) { - guard let index = controllers.firstIndex(of: controller) else { return } - if index == (controllers.count - 1) { - dismiss(animated: true) { [weak self] in - self?.completion() - } - } else { - safeScrollTo(index: index + 1, animated: true) - } - } -} - -public protocol NativeOnboardingManagerDelegate: AnyObject { - - func onboardingActionComplete(for controller: UIViewController) -} - -public protocol NativeOnboardingChildInterface: AnyObject { - - var onboardingManagerDelegate: NativeOnboardingManagerDelegate? { get set } -} -#endif diff --git a/Sources/NativeUIKit/Controllers/Complex/Profile/NativeAvatarHeaderView.swift b/Sources/NativeUIKit/Controllers/Complex/Profile/NativeAvatarHeaderView.swift deleted file mode 100644 index 4fe82d7..0000000 --- a/Sources/NativeUIKit/Controllers/Complex/Profile/NativeAvatarHeaderView.swift +++ /dev/null @@ -1,79 +0,0 @@ -// The MIT License (MIT) -// Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) -// -// 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. - -#if canImport(UIKit) && (os(iOS)) -import UIKit -import SparrowKit - -@available(iOS 13.0, *) -open class NativeAvatarHeaderView: SPView { - - // MARK: - Public - - public let avatarView = NativeAvatarView().do { - $0.avatarAppearance = .placeholder - $0.isEditable = true - } - - // MARK: - Private - - private var extendView = SPView() - - // MARK: - Init - - open override func commonInit() { - super.commonInit() - backgroundColor = .clear - addSubviews([extendView, avatarView]) - - layoutMargins = .init( - top: NativeLayout.Spaces.default, - left: NativeLayout.Spaces.default_double, - bottom: NativeLayout.Spaces.default_more, - right: NativeLayout.Spaces.default_double - ) - } - - // MARK: - Ovveride - - open override var backgroundColor: UIColor? { - didSet { - extendView.backgroundColor = backgroundColor - } - } - - // MARK: - Layout - - open override func layoutSubviews() { - super.layoutSubviews() - extendView.frame = .init(x: .zero, maxY: .zero, width: frame.width, height: 1000) - - avatarView.sizeToFit() - avatarView.setXCenter() - avatarView.frame.origin.y = layoutMargins.top - } - - open override func sizeThatFits(_ size: CGSize) -> CGSize { - layoutSubviews() - return .init(width: frame.width, height: avatarView.frame.maxY + layoutMargins.bottom) - } -} -#endif diff --git a/Sources/NativeUIKit/Controllers/Complex/Profile/NativeProfileController.swift b/Sources/NativeUIKit/Controllers/Complex/Profile/NativeProfileController.swift deleted file mode 100644 index ac748bc..0000000 --- a/Sources/NativeUIKit/Controllers/Complex/Profile/NativeProfileController.swift +++ /dev/null @@ -1,51 +0,0 @@ -// The MIT License (MIT) -// Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) -// -// 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. - -#if canImport(UIKit) && (os(iOS)) -import UIKit -import SparrowKit -import SPDiffable - -@available(iOS 13.0, *) -open class NativeProfileController: NativeHeaderTableController { - - // MARK: - Views - - public let headerView = NativeAvatarHeaderView() - - // MARK: - Init - - public init(style: UITableView.Style = .insetGrouped) { - super.init(style: style, headerView: headerView) - } - - public required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - // MARK: - Lifecycle - - open override func viewDidLoad() { - super.viewDidLoad() - navigationItem.title = .empty - } -} -#endif diff --git a/Sources/NativeUIKit/Controllers/Complex/Profile/NativeProfileHeaderView.swift b/Sources/NativeUIKit/Controllers/Complex/Profile/NativeProfileHeaderView.swift deleted file mode 100644 index c32b701..0000000 --- a/Sources/NativeUIKit/Controllers/Complex/Profile/NativeProfileHeaderView.swift +++ /dev/null @@ -1,144 +0,0 @@ -// The MIT License (MIT) -// Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) -// -// 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. - -#if canImport(UIKit) && (os(iOS)) -import UIKit -import SparrowKit - -@available(iOS 13.0, *) -open class NativeProfileHeaderView: SPView { - - // MARK: - Public - - public let avatarView = NativeAvatarView().do { - $0.avatarAppearance = .placeholder - $0.isEditable = true - } - - public let nameLabel = SPLabel().do { - $0.font = UIFont.preferredFont(forTextStyle: .title2, weight: .semibold, addPoints: 2) - $0.textColor = .label - $0.numberOfLines = 1 - $0.textAlignment = .center - // $0.adjustsFontSizeToFitWidth = true - // $0.minimumScaleFactor = 0.5 - $0.text = nil - } - - public let namePlaceholderLabel = SPLabel().do { - $0.font = UIFont.preferredFont(forTextStyle: .title2, weight: .semibold, addPoints: 2) - $0.textColor = .secondaryLabel - $0.numberOfLines = 1 - $0.textAlignment = .center - // $0.adjustsFontSizeToFitWidth = true - // $0.minimumScaleFactor = 0.5 - $0.text = nil - } - - // Add tap to clipboard - public let emailButton = SPDimmedButton().do { - $0.titleLabel?.font = UIFont.preferredFont(forTextStyle: .body, weight: .regular, addPoints: -2) - // $0.titleLabel?.adjustsFontSizeToFitWidth = true - // $0.titleLabel?.minimumScaleFactor = 0.5 - $0.titleImageInset = 2 - $0.titleLabel?.textAlignment = .center - } - - // MARK: - Private - - private var extendView = SPView() - - var nameTextObserer: NSKeyValueObservation? - - // MARK: - Init - - open override func commonInit() { - super.commonInit() - backgroundColor = .clear - addSubviews([extendView, avatarView, nameLabel, namePlaceholderLabel, emailButton]) - - layoutMargins = .init( - top: NativeLayout.Spaces.default_half, - left: NativeLayout.Spaces.default_double, - bottom: NativeLayout.Spaces.default_half, - right: NativeLayout.Spaces.default_double - ) - - self.nameLabel.isHidden = !(self.nameLabel == self.usingNameLabel) - self.namePlaceholderLabel.isHidden = !(self.namePlaceholderLabel == self.usingNameLabel) - - nameTextObserer = nameLabel.observe(\.text) { [weak self] _, _ in - guard let self = self else { return } - self.nameLabel.isHidden = !(self.nameLabel == self.usingNameLabel) - self.namePlaceholderLabel.isHidden = !(self.namePlaceholderLabel == self.usingNameLabel) - } - } - - // MARK: - Ovveride - - open override var backgroundColor: UIColor? { - didSet { - extendView.backgroundColor = backgroundColor - } - } - - // MARK: - Layout - - private var usingNameLabel: SPLabel { - if nameLabel.text?.isEmptyContent ?? true { - return namePlaceholderLabel - } else { - return nameLabel - } - } - - open override func layoutSubviews() { - super.layoutSubviews() - extendView.frame = .init(x: .zero, maxY: .zero, width: frame.width, height: 1000) - - avatarView.sizeToFit() - avatarView.setXCenter() - avatarView.frame.origin.y = layoutMargins.top - - let textWidth: CGFloat = layoutWidth * 0.8 - - nameLabel.layoutDynamicHeight(width: textWidth) - nameLabel.setXCenter() - nameLabel.frame.origin.y = avatarView.frame.maxY + NativeLayout.Spaces.default_half - - namePlaceholderLabel.layoutDynamicHeight(width: textWidth) - namePlaceholderLabel.setXCenter() - namePlaceholderLabel.frame.origin.y = nameLabel.frame.origin.y - - emailButton.sizeToFit() - if emailButton.frame.width > textWidth { - emailButton.frame.setWidth(textWidth) - } - emailButton.setXCenter() - emailButton.frame.origin.y = usingNameLabel.frame.maxY - } - - open override func sizeThatFits(_ size: CGSize) -> CGSize { - layoutSubviews() - return .init(width: frame.width, height: emailButton.frame.maxY + layoutMargins.bottom) - } -} -#endif diff --git a/Sources/NativeUIKit/Controllers/Navigation/NativeNavigationController.swift b/Sources/NativeUIKit/Controllers/Navigation/NativeNavigationController.swift deleted file mode 100644 index 9628476..0000000 --- a/Sources/NativeUIKit/Controllers/Navigation/NativeNavigationController.swift +++ /dev/null @@ -1,80 +0,0 @@ -// The MIT License (MIT) -// Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) -// -// 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. - -#if canImport(UIKit) && (os(iOS)) -import UIKit -import SparrowKit - -open class NativeNavigationController: SPNavigationController { - - // MARK: - Views - - open var mimicrateToolBarView: NativeMimicrateToolBarView? = nil { - willSet { - if let toolBarView = mimicrateToolBarView { - toolBarView.removeFromSuperview() - } - } - didSet { - if let toolBarView = mimicrateToolBarView { - view.addSubview(toolBarView) - } - } - } - - open override func setToolbarHidden(_ hidden: Bool, animated: Bool) { - if let barView = mimicrateToolBarView { - if animated { - - if barView.isHidden && !hidden { - barView.isHidden = false - barView.alpha = .zero - } - - UIView.animate(withDuration: 0.12, delay: .zero, options: [.beginFromCurrentState, .curveEaseInOut], animations: { - barView.alpha = hidden ? .zero : 1 - }) { completed in - barView.isHidden = hidden - } - } else { - barView.isHidden = hidden - } - } else { - super.setToolbarHidden(hidden, animated: animated) - } - } - - // MARK: - Layout - - open override func viewDidLayoutSubviews() { - super.viewDidLayoutSubviews() - if let toolBarView = mimicrateToolBarView { - toolBarView.layoutMargins.bottom = systemSafeAreaInsets.bottom + 16 - toolBarView.setWidthAndFit(width: view.frame.width) - toolBarView.frame.setMaxY(view.frame.height) - let toolBarFrameFitHeight = toolBarView.frame.height - systemSafeAreaInsets.bottom - if additionalSafeAreaInsets.bottom != toolBarFrameFitHeight { - additionalSafeAreaInsets.bottom = toolBarFrameFitHeight - } - } - } -} -#endif diff --git a/Sources/NativeUIKit/Controllers/Scroll/NativeHeaderController.swift b/Sources/NativeUIKit/Controllers/Scroll/NativeHeaderController.swift deleted file mode 100644 index 2d7d01a..0000000 --- a/Sources/NativeUIKit/Controllers/Scroll/NativeHeaderController.swift +++ /dev/null @@ -1,57 +0,0 @@ -// The MIT License (MIT) -// Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) -// -// 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. - -#if canImport(UIKit) && (os(iOS)) -import UIKit -import SparrowKit - -open class NativeHeaderController: SPScrollController { - - // MARK: - Views - - public let headerView: NativeModalHeaderView - - // MARK: - Init - - public init(image: UIImage?, title: String, subtitle: String) { - self.headerView = NativeModalHeaderView(image: image, title: title, subtitle: subtitle) - super.init() - } - - public required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - // MARK: - Lifecycle - - open override func viewDidLoad() { - super.viewDidLoad() - scrollView.contentInset.bottom = NativeLayout.Spaces.Scroll.bottom_inset_reach_end - scrollView.addSubview(headerView) - } - - open override func viewDidLayoutSubviews() { - super.viewDidLayoutSubviews() - headerView.layout(y: NativeLayout.Spaces.default) - scrollView.contentSize = .init(width: view.frame.width, height: headerView.frame.maxY) - } -} -#endif diff --git a/Sources/NativeUIKit/Controllers/Scroll/NativeHeaderFullWidthImageController.swift b/Sources/NativeUIKit/Controllers/Scroll/NativeHeaderFullWidthImageController.swift deleted file mode 100644 index 0b8a579..0000000 --- a/Sources/NativeUIKit/Controllers/Scroll/NativeHeaderFullWidthImageController.swift +++ /dev/null @@ -1,60 +0,0 @@ -// The MIT License (MIT) -// Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) -// -// 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. - -#if canImport(UIKit) && (os(iOS)) -import UIKit -import SparrowKit - -@available(iOS 13.0, *) -open class NativeHeaderFullWidthImageController: SPScrollController { - - // MARK: - Views - - public let headerImageView = SPImageView().do { - $0.backgroundColor = .systemGray.alpha(0.1) - $0.contentMode = .scaleAspectFill - } - - public let titlesView = NativeModalHeaderView() - - // MARK: - Lifecycle - - open override func viewDidLoad() { - super.viewDidLoad() - view.backgroundColor = .secondarySystemBackground - scrollView.showsVerticalScrollIndicator = false - scrollView.addSubviews([headerImageView, titlesView]) - navigationController?.navigationBar.setAppearance(.transparentAlways) - } - - // MARK: - Layout - - open override func viewDidLayoutSubviews() { - super.viewDidLayoutSubviews() - headerImageView.frame = .init(x: .zero, y: -scrollView.layoutMargins.top, width: view.frame.width, height: view.frame.height * 0.45) - titlesView.layout(y: headerImageView.frame.maxY + NativeLayout.Spaces.default_double) - scrollView.contentSize = .init( - width: view.frame.width, - height: titlesView.frame.maxY + NativeLayout.Spaces.default_double - ) - } -} -#endif diff --git a/Sources/NativeUIKit/Controllers/Scroll/NativeHeaderTextFieldController.swift b/Sources/NativeUIKit/Controllers/Scroll/NativeHeaderTextFieldController.swift deleted file mode 100644 index 7274274..0000000 --- a/Sources/NativeUIKit/Controllers/Scroll/NativeHeaderTextFieldController.swift +++ /dev/null @@ -1,59 +0,0 @@ -// The MIT License (MIT) -// Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) -// -// 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. - -#if canImport(UIKit) && (os(iOS)) -import UIKit -import SparrowKit - -open class NativeHeaderTextFieldController: NativeHeaderController { - - // MARK: - Views - - public let textField = NativeLargeTextField().do { - $0.returnKeyType = .done - $0.autocapitalizationType = .words - } - - public let footerView = NativeFooterView() - - // MARK: - Lifecycle - - open override func viewDidLoad() { - super.viewDidLoad() - if #available(iOS 13.0, *) { - view.backgroundColor = .systemBackground - } - scrollView.addSubviews(textField, footerView) - dismissKeyboardWhenTappedAround() - } - - // MARK: - Layout - - open override func viewDidLayoutSubviews() { - super.viewDidLayoutSubviews() - textField.layout(y: headerView.frame.maxY + NativeLayout.Spaces.default + NativeLayout.Spaces.default_half) - footerView.setWidthAndFit(width: textField.frame.width) - footerView.frame.origin.x = textField.frame.origin.x - footerView.frame.origin.y = textField.frame.maxY - scrollView.contentSize = .init(width: view.frame.width, height: footerView.frame.maxY) - } -} -#endif diff --git a/Sources/NativeUIKit/Controllers/Table/Header/NativeHeaderTableController+HeaderContainerView.swift b/Sources/NativeUIKit/Controllers/Table/Header/NativeHeaderTableController+HeaderContainerView.swift deleted file mode 100644 index e9c702f..0000000 --- a/Sources/NativeUIKit/Controllers/Table/Header/NativeHeaderTableController+HeaderContainerView.swift +++ /dev/null @@ -1,71 +0,0 @@ -// The MIT License (MIT) -// Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) -// -// 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. - -#if canImport(UIKit) && (os(iOS)) -import UIKit -import SparrowKit -import SPDiffable - -@available(iOS 13.0, *) -extension NativeHeaderTableController { - - open class HeaderContainerView: SPView { - - // MARK: - Public - - public let contentView: UIView - - // MARK: - Init - - public init(contentView: UIView) { - self.contentView = contentView - super.init() - } - - public required init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - public override func commonInit() { - super.commonInit() - insetsLayoutMarginsFromSafeArea = false - contentView.insetsLayoutMarginsFromSafeArea = false - layoutMargins = .zero - addSubview(contentView) - } - - // MARK: - Layout - - public override func layoutSubviews() { - super.layoutSubviews() - contentView.setWidthAndFit(width: layoutWidth) - contentView.frame.origin.x = layoutMargins.left - contentView.frame.origin.y = layoutMargins.top - } - - public override func sizeThatFits(_ size: CGSize) -> CGSize { - frame.setWidth(size.width) - layoutSubviews() - return .init(width: size.width, height: contentView.frame.maxY + layoutMargins.bottom) - } - } -} -#endif diff --git a/Sources/NativeUIKit/Controllers/Table/Header/NativeHeaderTableController.swift b/Sources/NativeUIKit/Controllers/Table/Header/NativeHeaderTableController.swift deleted file mode 100644 index dce3b98..0000000 --- a/Sources/NativeUIKit/Controllers/Table/Header/NativeHeaderTableController.swift +++ /dev/null @@ -1,77 +0,0 @@ -// The MIT License (MIT) -// Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) -// -// 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. - -#if canImport(UIKit) && (os(iOS)) -import UIKit -import SparrowKit -import SPDiffable - -@available(iOS 13.0, *) -open class NativeHeaderTableController: SPDiffableTableController { - - // MARK: - Public - - open var headerContainerView: HeaderContainerView - - // MARK: - Init - - public init(style: UITableView.Style, headerView: UIView) { - self.headerContainerView = HeaderContainerView(contentView: headerView) - super.init(style: style) - } - - public required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - // MARK: - Lifecycle - - open override func viewDidLoad() { - super.viewDidLoad() - headerContainerView.setWidthAndFit(width: view.frame.width) - tableView.tableHeaderView = headerContainerView - } - - // MARK: - Layout - - open override func viewDidLayoutSubviews() { - super.viewDidLayoutSubviews() - headerContainerView.contentView.layoutMargins.left = tableView.layoutMargins.left - headerContainerView.contentView.layoutMargins.right = tableView.layoutMargins.right - headerContainerView.setWidthAndFit(width: view.frame.width) - if cachedHeaderHeight != headerContainerView.frame.height { - cachedHeaderHeight = headerContainerView.frame.height - DispatchQueue.main.async { [weak self] in - guard let self = self else { return } - self.diffableDataSource?.updateLayout(animated: true, completion: nil) - } - } - } - - private var cachedHeaderHeight: CGFloat? = nil - - // MARK: - Public - - open func setSpaceBetweenHeaderAndCells(_ value: CGFloat) { - headerContainerView.layoutMargins.bottom = value - } -} -#endif diff --git a/Sources/NativeUIKit/Controls/Buttons/NativeLargeActionButton.swift b/Sources/NativeUIKit/Controls/Buttons/NativeLargeActionButton.swift deleted file mode 100644 index 6774564..0000000 --- a/Sources/NativeUIKit/Controls/Buttons/NativeLargeActionButton.swift +++ /dev/null @@ -1,118 +0,0 @@ -// The MIT License (MIT) -// Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) -// -// 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. - -#if canImport(UIKit) && (os(iOS)) -import UIKit -import SparrowKit - -/** - NativeUIKit: Large action button. - Usually using at bottom of screen. - */ -open class NativeLargeActionButton: SPDimmedButton { - - // MARK: - Init - - open override func commonInit() { - super.commonInit() - insetsLayoutMarginsFromSafeArea = false - preservesSuperviewLayoutMargins = false - titleLabel?.font = UIFont.preferredFont(forTextStyle: .headline, addPoints: 1) - titleLabel?.numberOfLines = 1 - highlightOpacity = NativeAppearance.actionable_element_highlight_opacity - titleImageInset = 6 - contentEdgeInsets = .init(horizontal: 10, vertical: 12) - roundCorners(radius: NativeAppearance.Corners.readable_area) - } - - // MARK: - Public - - /** - NativeUIKit: Wrapper of set content and color of button. - - - parameter title: Text which using like title. - - parameter icon: Object of `UIImage`, using like icon. - - parameter colorise: Color of button in default state. - */ - public func set(title: String, icon: UIImage?, colorise: SPDimmedButton.Colorise) { - setTitle(title) - if let icon = icon { - setImage(icon.alwaysTemplate) - } - applyDefaultAppearance(with: colorise) - } - - // MARK: - Layout - - /** - NativeUIKit: Layout wrapper. Native way for layout button. - */ - open func layout(y: CGFloat) { - guard let superview = self.superview else { return } - sizeToFit() - let width = min(superview.readableWidth, NativeLayout.Sizes.actionable_area_maximum_width) - frame.setWidth(width) - setXCenter() - frame.origin.y = y - } - - /** - NativeUIKit: Layout wrapper. Native way for layout button. - */ - open func layout(maxY: CGFloat) { - guard let superview = self.superview else { return } - sizeToFit() - let width = min(superview.readableWidth, NativeLayout.Sizes.actionable_area_maximum_width) - frame.setWidth(width) - setXCenter() - frame.setMaxY(maxY) - } - - open override func sizeThatFits(_ size: CGSize) -> CGSize { - let superSize = super.sizeThatFits(size) - let width = superSize.width - - var height = superSize.height - if let titleLabel = titleLabel, let imageView = imageView, let _ = imageView.image { - if titleLabel.frame.height > .zero && imageView.frame.height > .zero { - let imageCorrection = imageView.frame.height - titleLabel.frame.height - height -= imageCorrection - } - } - - return CGSize(width: width, height: height) - } - - // MARK: - Wrappers - - public static var defaultCornerRadius: CGFloat { - let button = NativeLargeActionButton() - return button.layer.cornerRadius - } - - public static var defaultHeight: CGFloat { - let button = NativeLargeActionButton() - button.setTitle(.space) - button.sizeToFit() - return button.frame.height - } -} -#endif diff --git a/Sources/NativeUIKit/Controls/Buttons/NativeMediumActionButton.swift b/Sources/NativeUIKit/Controls/Buttons/NativeMediumActionButton.swift deleted file mode 100644 index a9cd771..0000000 --- a/Sources/NativeUIKit/Controls/Buttons/NativeMediumActionButton.swift +++ /dev/null @@ -1,89 +0,0 @@ -// The MIT License (MIT) -// Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) -// -// 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. - -#if canImport(UIKit) && (os(iOS)) -import UIKit -import SparrowKit - -/** - NativeUIKit: Medium action button. - You see it in Apply Music play and shuffle buttons. - */ -open class NativeMediumActionButton: SPDimmedButton { - - // MARK: - Init - - open override func commonInit() { - super.commonInit() - titleLabel?.font = UIFont.preferredFont(forTextStyle: .body, weight: .semibold, addPoints: .zero) - titleLabel?.numberOfLines = 1 - titleImageInset = 8 - contentEdgeInsets = .init(horizontal: 15, vertical: 15) - roundCorners(radius: 12) - } - - // MARK: - Public - - /** - NativeUIKit: Wrapper of set content and color of button. - - - parameter title: Text which using like title. - - parameter icon: Object of `UIImage`, using like icon. Usually Apple doesn't use icon in this button. - - parameter colorise: Color of button in default state. - */ - public func set(title: String, icon: UIImage? = nil, colorise: SPDimmedButton.Colorise) { - setTitle(title) - if let icon = icon { - setImage(icon.alwaysTemplate) - } - applyDefaultAppearance(with: colorise) - } - - open override func sizeThatFits(_ size: CGSize) -> CGSize { - let superSize = super.sizeThatFits(size) - let width = superSize.width - - var height = superSize.height - if let titleLabel = titleLabel, let imageView = imageView, let _ = imageView.image { - if titleLabel.frame.height > .zero && imageView.frame.height > .zero { - let imageCorrection = imageView.frame.height - titleLabel.frame.height - height -= imageCorrection - } - } - - return CGSize(width: width, height: height) - } - - // MARK: - Wrappers - - public static var defaultCornerRadius: CGFloat { - let button = NativeMediumActionButton() - return button.layer.cornerRadius - } - - public static var defaultHeight: CGFloat { - let button = NativeMediumActionButton() - button.setTitle(.space) - button.sizeToFit() - return button.frame.height - } -} -#endif diff --git a/Sources/NativeUIKit/Controls/Buttons/NativeSmallActionButton.swift b/Sources/NativeUIKit/Controls/Buttons/NativeSmallActionButton.swift deleted file mode 100644 index 2def7eb..0000000 --- a/Sources/NativeUIKit/Controls/Buttons/NativeSmallActionButton.swift +++ /dev/null @@ -1,89 +0,0 @@ -// The MIT License (MIT) -// Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) -// -// 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. - -#if canImport(UIKit) && (os(iOS)) -import UIKit -import SparrowKit - -/** - NativeUIKit: Small action button. - Using inside cells. - */ -open class NativeSmallActionButton: SPDimmedButton { - - // MARK: - Init - - open override func commonInit() { - super.commonInit() - titleLabel?.font = UIFont.preferredFont(forTextStyle: .subheadline, weight: .bold, addPoints: -1) - titleLabel?.numberOfLines = 1 - titleImageInset = 6 - contentEdgeInsets = .init(horizontal: 10, vertical: 6) - } - - // MARK: - Public - - /** - NativeUIKit: Wrapper of set content and color of button. - - - parameter title: Text which using like title. - - parameter icon: Object of `UIImage`, using like icon. Usually Apple doesn't use icon in this button. - - parameter colorise: Color of button in default state. - */ - public func set(title: String, icon: UIImage? = nil, colorise: SPDimmedButton.Colorise) { - setTitle(title) - if let icon = icon { - setImage(icon.alwaysTemplate) - } - applyDefaultAppearance(with: colorise) - } - - // MARK: - Layout - - open override func layoutSubviews() { - super.layoutSubviews() - roundMinimumSide() - } - - open override func sizeThatFits(_ size: CGSize) -> CGSize { - let superSize = super.sizeThatFits(size) - var width = superSize.width - let minimumWidth: CGFloat = 70 - if width < minimumWidth { width = minimumWidth } - - var height = superSize.height - if let titleLabel = titleLabel, let imageView = imageView, let _ = imageView.image { - if titleLabel.frame.height > .zero && imageView.frame.height > .zero { - let imageCorrection = imageView.frame.height - titleLabel.frame.height - height -= imageCorrection - } - } - - return CGSize(width: width, height: height) - } - - // MARK: - Ovveride - - open override func setTitle(_ title: String?, for state: UIControl.State) { - super.setTitle(title?.uppercased(), for: state) - } -} -#endif diff --git a/Sources/NativeUIKit/Controls/NativeLargeTextField.swift b/Sources/NativeUIKit/Controls/NativeLargeTextField.swift deleted file mode 100644 index d55dc1b..0000000 --- a/Sources/NativeUIKit/Controls/NativeLargeTextField.swift +++ /dev/null @@ -1,80 +0,0 @@ -// The MIT License (MIT) -// Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) -// -// 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. - -#if canImport(UIKit) && (os(iOS)) -import UIKit -import SparrowKit - -open class NativeLargeTextField: SPInsetsTextField { - - // MARK: - Init - - open override func commonInit() { - super.commonInit() - if #available(iOS 13.0, *) { - backgroundColor = .secondarySystemBackground - } else { - backgroundColor = .white - } - layer.cornerRadius = NativeAppearance.Corners.readable_area - textAlignment = .center - clearButtonMode = .whileEditing - contentMode = .scaleAspectFit - font = .preferredFont(forTextStyle: .title2, weight: .bold, addPoints: .zero) - adjustsFontSizeToFitWidth = true - minimumFontSize = 16 - insets = .init(horizontal: 48, vertical: 14) - } - - // MARK: - Lifecycle - - open override func tintColorDidChange() { - super.tintColorDidChange() - textColor = tintColor - } - - // MARK: - Layout - - /** - NativeUIKit: Layout wrapper. Native way for layout button. - */ - open func layout(y: CGFloat) { - guard let superview = self.superview else { return } - sizeToFit() - let width = min(superview.readableWidth, NativeLayout.Sizes.actionable_area_maximum_width) - frame.setWidth(width) - setXCenter() - frame.origin.y = y - } - - /** - NativeUIKit: Layout wrapper. Native way for layout button. - */ - open func layout(maxY: CGFloat) { - guard let superview = self.superview else { return } - sizeToFit() - let width = min(superview.readableWidth, NativeLayout.Sizes.actionable_area_maximum_width) - frame.setWidth(width) - setXCenter() - frame.setMaxY(maxY) - } -} -#endif diff --git a/Sources/NativeUIKit/Controls/NativePlayPauseCompactButton.swift b/Sources/NativeUIKit/Controls/NativePlayPauseCompactButton.swift deleted file mode 100644 index 90b9140..0000000 --- a/Sources/NativeUIKit/Controls/NativePlayPauseCompactButton.swift +++ /dev/null @@ -1,67 +0,0 @@ -// The MIT License (MIT) -// Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) -// -// 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. - -#if canImport(UIKit) && (os(iOS)) -import UIKit -import SparrowKit - -@available(iOS 13, *) -open class NativePlayPauseCompactButton: SPDimmedButton { - - // MARK: - Data - - open var appearance: Appearance = .play { - didSet { - updateAppearance() - } - } - open var playImage = UIImage.system("play.fill", font: .preferredFont(forTextStyle: .body)) - open var pauseImage = UIImage.system("pause.fill", font: .preferredFont(forTextStyle: .body)) - - // MARK: - Init - - open override func commonInit() { - super.commonInit() - contentEdgeInsets = .init(side: 8) - applyDefaultAppearance(with: .tintedContent) - updateAppearance() - } - - // MARK: - Internal - - internal func updateAppearance() { - switch self.appearance { - case .play: - setImage(playImage) - case .pause: - setImage(pauseImage) - } - } - - // MARK: - Models - - public enum Appearance { - - case play - case pause - } -} -#endif diff --git a/Sources/NativeUIKit/Labels/NativeFooterView.swift b/Sources/NativeUIKit/Labels/NativeFooterView.swift deleted file mode 100644 index f599aa6..0000000 --- a/Sources/NativeUIKit/Labels/NativeFooterView.swift +++ /dev/null @@ -1,69 +0,0 @@ -// The MIT License (MIT) -// Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) -// -// 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. - -#if canImport(UIKit) && (os(iOS)) -import UIKit -import SparrowKit - -open class NativeFooterView: SPView { - - // MARK: - Views - - public let label = SPLabel().do { - $0.font = .preferredFont(forTextStyle: .footnote, addPoints: 1) - $0.numberOfLines = .zero - if #available(iOS 13.0, *) { - $0.textColor = .secondaryLabel - } else { - $0.textColor = .gray - } - } - - public override init() { - super.init() - } - - public init(text: String) { - super.init() - label.text = text - } - - required public init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - open override func commonInit() { - super.commonInit() - layoutMargins = .init(top: NativeLayout.Spaces.default_half, left: 16, bottom: NativeLayout.Spaces.default_half, right: .zero) - addSubview(label) - } - - open override func layoutSubviews() { - super.layoutSubviews() - label.layoutDynamicHeight(x: layoutMargins.left, y: layoutMargins.top, width: layoutWidth) - } - - open override func sizeThatFits(_ size: CGSize) -> CGSize { - layoutSubviews() - return .init(width: size.width, height: label.frame.maxY + layoutMargins.bottom) - } -} -#endif diff --git a/Sources/NativeUIKit/Labels/NativeModalHeaderView.swift b/Sources/NativeUIKit/Labels/NativeModalHeaderView.swift deleted file mode 100644 index 70152d1..0000000 --- a/Sources/NativeUIKit/Labels/NativeModalHeaderView.swift +++ /dev/null @@ -1,131 +0,0 @@ -// The MIT License (MIT) -// Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) -// -// 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. - -#if canImport(UIKit) && (os(iOS)) -import UIKit -import SparrowKit - -/** - NativeUIKit: Usually use in modal screen with large title and subtitle. - Image is optional. - */ -open class NativeModalHeaderView: SPView { - - // MARK: - Views - - public let iconImageView = SPImageView().do { - $0.contentMode = .scaleAspectFit - $0.tintColor = UIColor.tint - } - - public let titleLabel = SPLabel().do { - $0.numberOfLines = .zero - $0.font = UIFont.preferredFont(forTextStyle: .largeTitle, weight: .bold) - if #available(iOS 13.0, *) { - $0.textColor = .label - } else { - $0.textColor = .black - } - $0.textAlignment = .center - } - - public let subtitleLabel = SPLabel().do { - $0.numberOfLines = .zero - $0.font = UIFont.preferredFont(forTextStyle: .body) - if #available(iOS 13.0, *) { - $0.textColor = .secondaryLabel - } else { - $0.textColor = .black.alpha(0.5) - } - $0.textAlignment = .center - } - - // MARK: - Init - - public override init() { - super.init() - } - - public init(image: UIImage?, title: String, subtitle: String) { - super.init() - titleLabel.text = title - subtitleLabel.text = subtitle - iconImageView.image = image - } - - public required init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - open override func commonInit() { - super.commonInit() - backgroundColor = .clear - layoutMargins = .zero - addSubview(titleLabel) - addSubview(subtitleLabel) - addSubview(iconImageView) - } - - // MARK: - Layout - - /** - NativeUIKit: Layout wrapper. Native way for layout button. - */ - open func layout(y: CGFloat) { - guard let superview = self.superview else { return } - let width = min(superview.readableWidth, NativeLayout.Sizes.not_actionable_area_maximum_width) - setWidthAndFit(width: width) - setXCenter() - frame.origin.y = y - } - - /** - NativeUIKit: Layout wrapper. Native way for layout button. - */ - open func layout(maxY: CGFloat) { - guard let superview = self.superview else { return } - let width = min(superview.readableWidth, NativeLayout.Sizes.not_actionable_area_maximum_width) - setWidthAndFit(width: width) - setXCenter() - frame.setMaxY(maxY) - } - - open override func layoutSubviews() { - super.layoutSubviews() - iconImageView.frame = .init(side: NativeLayout.Spaces.default_double * 2) - iconImageView.setXCenter() - iconImageView.frame.origin.y = layoutMargins.top - - if let _ = iconImageView.image { - titleLabel.layoutDynamicHeight(x: .zero, y: iconImageView.frame.maxY + 12, width: layoutWidth) - } else { - titleLabel.layoutDynamicHeight(x: .zero, y: .zero, width: layoutWidth) - } - - subtitleLabel.layoutDynamicHeight(x: .zero, y: titleLabel.frame.maxY + 4, width: titleLabel.frame.width) - } - - open override func sizeThatFits(_ size: CGSize) -> CGSize { - layoutSubviews() - return .init(width: size.width, height: subtitleLabel.frame.maxY + layoutMargins.bottom) - } -} -#endif diff --git a/Sources/NativeUIKit/NativeAppearance.swift b/Sources/NativeUIKit/NativeAppearance.swift deleted file mode 100644 index 0ee6c9f..0000000 --- a/Sources/NativeUIKit/NativeAppearance.swift +++ /dev/null @@ -1,34 +0,0 @@ -// The MIT License (MIT) -// Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) -// -// 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. - -#if canImport(UIKit) && (os(iOS)) -import UIKit - -enum NativeAppearance { - - enum Corners { - - static var readable_area: CGFloat { 15 } - } - - static var actionable_element_highlight_opacity: CGFloat { 0.6 } -} -#endif diff --git a/Sources/NativeUIKit/NativeLayout.swift b/Sources/NativeUIKit/NativeLayout.swift deleted file mode 100644 index 99a574d..0000000 --- a/Sources/NativeUIKit/NativeLayout.swift +++ /dev/null @@ -1,58 +0,0 @@ -// The MIT License (MIT) -// Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) -// -// 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. - -#if canImport(UIKit) && (os(iOS)) -import UIKit - -public enum NativeLayout { - - public enum Sizes { - - public static var actionable_area_maximum_width: CGFloat { 408 } - public static var not_actionable_area_maximum_width: CGFloat { 382 } - } - - public enum Spaces { - - public static var step: CGFloat = 4 - - public static var default_less: CGFloat { step * 3 } - public static var `default`: CGFloat { step * 4 } - public static var default_more: CGFloat { step * 5 } - - public static var default_half: CGFloat { self.default / 2 } - public static var default_double: CGFloat { self.default * 2 } - - public enum Margins { - - public static var full_screen_horizontal: CGFloat { Spaces.default } - public static var modal_screen_horizontal: CGFloat { step * 5 } - } - - public enum Scroll { - - public static var top_inset_transparent_navigation: CGFloat { step * 2 } - public static var bottom_inset_reach_end: CGFloat { step * 9 } - public static var bottom_inset_when_keyboard_can_appear: CGFloat { step * 8 } - } - } -} -#endif diff --git a/Sources/NativeUIKit/Table/Button/CellProvider+Button.swift b/Sources/NativeUIKit/Table/Button/CellProvider+Button.swift deleted file mode 100644 index 5605bfb..0000000 --- a/Sources/NativeUIKit/Table/Button/CellProvider+Button.swift +++ /dev/null @@ -1,44 +0,0 @@ -// The MIT License (MIT) -// Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) -// -// 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. - -#if canImport(UIKit) && (os(iOS)) -import UIKit -import SPDiffable - -@available(iOS 13.0, *) -extension SPDiffableTableDataSource.CellProvider { - - public static var button: SPDiffableTableDataSource.CellProvider { - return SPDiffableTableDataSource.CellProvider() { (tableView, indexPath, item) -> UITableViewCell? in - guard let item = item as? NativeDiffableLeftButton else { return nil } - let cell = tableView.dequeueReusableCell(withClass: NativeLeftButtonTableViewCell.self, for: indexPath) - cell.textLabel?.text = item.text - cell.textLabel?.textColor = item.textColor - cell.detailTextLabel?.text = item.detail - cell.detailTextLabel?.textColor = item.detailColor - cell.imageView?.image = item.icon - cell.accessoryType = item.accessoryType - cell.higlightStyle = item.higlightStyle - return cell - } - } -} -#endif diff --git a/Sources/NativeUIKit/Table/Button/NativeDiffableLeftButton.swift b/Sources/NativeUIKit/Table/Button/NativeDiffableLeftButton.swift deleted file mode 100644 index bf392c5..0000000 --- a/Sources/NativeUIKit/Table/Button/NativeDiffableLeftButton.swift +++ /dev/null @@ -1,59 +0,0 @@ -// The MIT License (MIT) -// Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) -// -// 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. - -#if canImport(UIKit) && (os(iOS)) -import UIKit -import SparrowKit -import SPDiffable - -@available(iOS 13.0, *) -open class NativeDiffableLeftButton: SPDiffableTableRow { - - open var textColor: UIColor - open var detailColor: UIColor - open var higlightStyle: SPTableViewCell.HiglightStyle - - public init( - id: String? = nil, - text: String, - textColor: UIColor = .label, - detail: String? = nil, - detailColor: UIColor = .secondaryLabel, - icon: UIImage? = nil, - accessoryType: UITableViewCell.AccessoryType = .none, - higlightStyle: SPTableViewCell.HiglightStyle = .content, - action: SPDiffableTableRow.Action? = nil - ) { - self.textColor = textColor - self.detailColor = detailColor - self.higlightStyle = higlightStyle - - super.init( - id: id, - text: text, - detail: detail, - icon: icon, - accessoryType: accessoryType, - action: action - ) - } -} -#endif diff --git a/Sources/NativeUIKit/Table/Button/NativeLeftButtonTableViewCell.swift b/Sources/NativeUIKit/Table/Button/NativeLeftButtonTableViewCell.swift deleted file mode 100644 index 3d3d4a6..0000000 --- a/Sources/NativeUIKit/Table/Button/NativeLeftButtonTableViewCell.swift +++ /dev/null @@ -1,47 +0,0 @@ -// The MIT License (MIT) -// Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) -// -// 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. - -#if canImport(UIKit) && (os(iOS)) -import UIKit -import SparrowKit - -open class NativeLeftButtonTableViewCell: SPTableViewCell { - - public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { - super.init(style: .value1, reuseIdentifier: reuseIdentifier) - } - - required public init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - open override func commonInit() { - super.commonInit() - textLabel?.font = .preferredFont(forTextStyle: .body, weight: .medium) - higlightStyle = .content - } - - open override func sizeThatFits(_ size: CGSize) -> CGSize { - let superSize = super.sizeThatFits(size) - return .init(width: superSize.width, height: superSize.height + 6) - } -} -#endif diff --git a/Sources/NativeUIKit/Table/Empty/CellProvider+Empty.swift b/Sources/NativeUIKit/Table/Empty/CellProvider+Empty.swift deleted file mode 100644 index b3d5549..0000000 --- a/Sources/NativeUIKit/Table/Empty/CellProvider+Empty.swift +++ /dev/null @@ -1,40 +0,0 @@ -// The MIT License (MIT) -// Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) -// -// 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. - -#if canImport(UIKit) && (os(iOS)) -import UIKit -import SPDiffable - -@available(iOS 13.0, *) -extension SPDiffableTableDataSource.CellProvider { - - public static var empty: SPDiffableTableDataSource.CellProvider { - return SPDiffableTableDataSource.CellProvider() { (tableView, indexPath, item) -> UITableViewCell? in - guard let item = item as? NativeEmptyRowItem else { return nil } - let cell = tableView.dequeueReusableCell(withClass: NativeEmptyTableViewCell.self, for: indexPath) - cell.placeholderView.headerLabel.text = item.text - cell.placeholderView.descriptionLabel.text = item.detail - cell.verticalMargins = item.verticalMargins - return cell - } - } -} -#endif diff --git a/Sources/NativeUIKit/Table/Empty/NativeEmptyRowItem.swift b/Sources/NativeUIKit/Table/Empty/NativeEmptyRowItem.swift deleted file mode 100644 index ebb9fd2..0000000 --- a/Sources/NativeUIKit/Table/Empty/NativeEmptyRowItem.swift +++ /dev/null @@ -1,37 +0,0 @@ -// The MIT License (MIT) -// Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) -// -// 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. - -#if canImport(UIKit) && (os(iOS)) -import UIKit -import SparrowKit -import SPDiffable - -@available(iOS 13.0, *) -open class NativeEmptyRowItem: SPDiffableTableRow { - - var verticalMargins: NativeEmptyTableViewCell.Margins - - public init(id: String, verticalMargins: NativeEmptyTableViewCell.Margins, text: String, detail: String?) { - self.verticalMargins = verticalMargins - super.init(id: id, text: text, detail: detail) - } -} -#endif diff --git a/Sources/NativeUIKit/Table/Empty/NativeEmptyTableViewCell.swift b/Sources/NativeUIKit/Table/Empty/NativeEmptyTableViewCell.swift deleted file mode 100644 index ce3b6a4..0000000 --- a/Sources/NativeUIKit/Table/Empty/NativeEmptyTableViewCell.swift +++ /dev/null @@ -1,113 +0,0 @@ -// The MIT License (MIT) -// Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) -// -// 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. - -#if canImport(UIKit) && (os(iOS)) -import UIKit -import SparrowKit - -open class NativeEmptyTableViewCell: SPTableViewCell { - - // MARK: - Data - - open var verticalMargins: Margins = .medium { - didSet { - contentView.layoutMargins.top = self.verticalMargins.value - contentView.layoutMargins.bottom = self.verticalMargins.value - } - } - - // MARK: - Views - - public let placeholderView = NativePlaceholderView().do { - $0.isUserInteractionEnabled = false - } - - // MARK: - Init - - open override func commonInit() { - super.commonInit() - selectionStyle = .none - contentView.addSubview(placeholderView) - updateBackgroundColorByParent() - } - - // MARK: - Lifecycle - - open override func tintColorDidChange() { - super.tintColorDidChange() - updateBackgroundColorByParent() - } - - // MARK: - Layout - - open override func layoutSubviews() { - super.layoutSubviews() - placeholderView.frame.origin.y = contentView.layoutMargins.top - placeholderView.setWidthAndFit(width: contentView.layoutWidth) - placeholderView.setXCenter() - } - - open override func sizeThatFits(_ size: CGSize) -> CGSize { - let superSize = super.sizeThatFits(size) - layoutSubviews() - let calculatedHeight = placeholderView.frame.maxY + contentView.layoutMargins.bottom - let bottomInset = calculatedHeight * (1 - 0.94) - return .init(width: superSize.width, height: calculatedHeight + bottomInset) - } - - // MARK: - Actions - - private func updateBackgroundColorByParent() { - if #available(iOS 13.0, *) { - backgroundColor = UIColor.systemDownedGroupedBackground - } - /*if let superView = self.superview?.superview { - let superViewBackgroundColor = superView.backgroundColor ?? .clear - if superViewBackgroundColor == .systemGroupedBackground { - backgroundColor = UIColor.init( - light: superViewBackgroundColor.mixWithColor(.darkGray, amount: 0.09).mixWithColor(.systemBlue, amount: 0.01), - dark: .secondarySystemGroupedBackground.alpha(0.7) - ) - } else { - // Here not implemented becouse not using never for now. - backgroundColor = .red - } - }*/ - } - - // MARK: - Models - - public enum Margins { - - case small - case medium - case large - - var value: CGFloat { - switch self { - case .small: return 16 - case .medium: return 32 - case .large: return 48 - } - } - } -} -#endif diff --git a/Sources/NativeUIKit/Table/LargeHeader/CellProvider+LargeHeader.swift b/Sources/NativeUIKit/Table/LargeHeader/CellProvider+LargeHeader.swift deleted file mode 100644 index ea99733..0000000 --- a/Sources/NativeUIKit/Table/LargeHeader/CellProvider+LargeHeader.swift +++ /dev/null @@ -1,38 +0,0 @@ -// The MIT License (MIT) -// Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) -// -// 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. - -#if canImport(UIKit) && (os(iOS)) -import UIKit -import SPDiffable - -@available(iOS 13.0, *) -extension SPDiffableTableDataSource.HeaderFooterProvider { - - public static var largeHeader: SPDiffableTableDataSource.HeaderFooterProvider { - return SPDiffableTableDataSource.HeaderFooterProvider() { (tableView, section, item) -> UIView? in - guard let header = item as? NativeLargeHeaderItem else { return nil } - let view = NativeLargeHeaderView() - view.configure(with: header, section: section) - return view - } - } -} -#endif diff --git a/Sources/NativeUIKit/Table/LargeHeader/NativeLargeHeaderItem.swift b/Sources/NativeUIKit/Table/LargeHeader/NativeLargeHeaderItem.swift deleted file mode 100644 index 496a780..0000000 --- a/Sources/NativeUIKit/Table/LargeHeader/NativeLargeHeaderItem.swift +++ /dev/null @@ -1,42 +0,0 @@ -// The MIT License (MIT) -// Copyright © 2020 Ivan Vorobei (hello@ivanvorobei.io) -// -// 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. - -#if canImport(UIKit) && (os(iOS) || os(tvOS)) -import UIKit -import SPDiffable - -/** - SPDiffable: Native large header with button. - */ -open class NativeLargeHeaderItem: SPDiffableItem, SPDiffableItemActionable { - - open var title: String - open var actionTitle: String? - open var action: Action? - - public init(id: String? = nil, title: String, actionTitle: String? = nil, action: Action? = nil) { - self.title = title - self.actionTitle = actionTitle - self.action = action - super.init(id: id ?? title) - } -} -#endif diff --git a/Sources/NativeUIKit/Table/LargeHeader/NativeLargeHeaderViewExtension.swift b/Sources/NativeUIKit/Table/LargeHeader/NativeLargeHeaderViewExtension.swift deleted file mode 100644 index 4e8ac29..0000000 --- a/Sources/NativeUIKit/Table/LargeHeader/NativeLargeHeaderViewExtension.swift +++ /dev/null @@ -1,37 +0,0 @@ -// The MIT License (MIT) -// Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) -// -// 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. - -#if canImport(UIKit) && (os(iOS)) -import UIKit - -extension NativeLargeHeaderView { - - func configure(with item: NativeLargeHeaderItem, section: Int) { - titleLabel.text = item.title - if let actionTitle = item.actionTitle { - button.setTitle(actionTitle) - diffableButtonAction = { - item.action?(item, .init(row: .zero, section: section)) - } - } - } -} -#endif diff --git a/Sources/NativeUIKit/Views/NativeAvatarView.swift b/Sources/NativeUIKit/Views/NativeAvatarView.swift deleted file mode 100644 index b647578..0000000 --- a/Sources/NativeUIKit/Views/NativeAvatarView.swift +++ /dev/null @@ -1,255 +0,0 @@ -// The MIT License (MIT) -// Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) -// -// 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. - -#if canImport(UIKit) && (os(iOS)) -import UIKit -import SparrowKit - -/** - NativeUIKit: Native avatar view. - - Using SFSymbols for content. - You shoud update avatar by set `avatarAppearance` to valid state. - For change colors or content check images and colorises properties. - For change show edit button set `isEditable` to true. - */ -@available(iOS 13, *) -open class NativeAvatarView: SPView { - - // MARK: - Views - - public lazy var activityIndicatorView = UIActivityIndicatorView().do { - $0.stopAnimating() - } - - public lazy var avatarButton = SPButton().do { - $0.imageView?.contentMode = .scaleAspectFill - $0.layer.masksToBounds = true - $0.contentVerticalAlignment = .fill - $0.contentHorizontalAlignment = .fill - } - - public lazy var placeholderButton = SPDimmedButton().do { - $0.imageView?.contentMode = .scaleAspectFit - $0.setImage(placeholderImage) - $0.applyDefaultAppearance(with: placeholderColorise) - } - - public lazy var indicatorButton = SPDimmedButton().do { - $0.contentEdgeInsets = .init(side: 4) - } - - // MARK: - Data - - /** - NativeUIKit: Indicate if current state has avatar. - */ - open var hasAvatar: Bool { - switch avatarAppearance { - case .avatar(_): return true - default: return false - } - } - - /** - NativeUIKit: Appearance of avatar button. Can be placeholder or with content. - State loading show loading indicator and hide all other views. - */ - open var avatarAppearance: AvatarAppearance = .placeholder { - didSet { - updateAvatarAppearance() - updateEditAppearance() - } - } - - /** - NativeUIKit: Hidden or visible edit button. - */ - open var isEditable: Bool = false { - didSet { - updateEditAppearance() - } - } - - open var placeholderImage = generatePlaceholderImage(fontSize: 80, fontWeight: .medium) { - didSet { - placeholderButton.setImage(placeholderImage) - } - } - - open var placeholderColorise = SPDimmedButton.Colorise.init(content: .init(light: .systemGray3, dark: .systemGray2), background: .clear) { - didSet { - placeholderButton.applyDefaultAppearance(with: placeholderColorise) - } - } - - open var indicatorAddImage = UIImage.system("plus", font: .preferredFont(forTextStyle: .title3, weight: .bold)) { - didSet { - updateEditAppearance() - } - } - - open var indicatorAddColorise = SPDimmedButton.Colorise.init(content: .white, background: .systemGreen) { - didSet { - updateEditAppearance() - } - } - - open var indicatorEditImage = UIImage.system("pencil", font: .preferredFont(forTextStyle: .title3, weight: .bold)) { - didSet { - updateEditAppearance() - } - } - - open var indicatorEditColorise = SPDimmedButton.Colorise.init(content: .white, background: .systemBlue) { - didSet { - updateEditAppearance() - } - } - - // MARK: - Init - - open override func commonInit() { - super.commonInit() - layoutMargins = .zero - addSubview(placeholderButton) - addSubview(avatarButton) - addSubview(indicatorButton) - addSubview(activityIndicatorView) - updateAvatarAppearance() - updateEditAppearance() - } - - // MARK: - Layout - - open override func layoutSubviews() { - super.layoutSubviews() - - placeholderButton.frame = .init(side: layoutWidth) - placeholderButton.frame.origin.y = layoutMargins.top - placeholderButton.center.x = frame.width / 2 - - let avatarSideSize = min(placeholderButton.frame.width, placeholderButton.frame.height) * 0.93 - avatarButton.frame = .init(side: avatarSideSize) - avatarButton.center = placeholderButton.center - avatarButton.roundMinimumSide() - indicatorButton.sizeToFit() - indicatorButton.roundMinimumSide() - let sqrt2 = CGFloat(sqrt(2)) - let indicatorCenter = ((avatarButton.frame.width * (sqrt2 + 1)) / (2 * sqrt2)) * 1.02 - indicatorButton.center = .init(x: indicatorCenter, y: indicatorCenter) - - activityIndicatorView.center = placeholderButton.center - } - - open override func sizeThatFits(_ size: CGSize) -> CGSize { - placeholderButton.sizeToFit() - frame.setWidth(placeholderButton.frame.width + layoutMargins.left + layoutMargins.right) - layoutSubviews() - if isEditable { - return .init( - width: max(placeholderButton.frame.maxX, indicatorButton.frame.maxX) + layoutMargins.bottom, - height: max(placeholderButton.frame.maxY, indicatorButton.frame.maxY) + layoutMargins.bottom - ) - } else { - return .init( - width: placeholderButton.frame.maxX + layoutMargins.bottom, - height: placeholderButton.frame.maxY + layoutMargins.bottom - ) - } - } - - // MARK: - Public - - static public func generatePlaceholderImage(fontSize: CGFloat, fontWeight: UIFont.Weight) -> UIImage { - return UIImage.system("person.crop.circle.fill", font: .systemFont(ofSize: fontSize, weight: fontWeight)) - } - - // MARK: - Internal - - /** - NativeUIKit: Call for update to current avatar appearance. - */ - internal func updateAvatarAppearance() { - switch avatarAppearance { - case .loading: - activityIndicatorView.startAnimating() - avatarButton.isHidden = true - placeholderButton.isHidden = true - case .placeholder: - activityIndicatorView.stopAnimating() - avatarButton.isHidden = true - placeholderButton.isHidden = false - case .avatar(let image): - activityIndicatorView.stopAnimating() - avatarButton.isHidden = false - avatarButton.setImage(image) - placeholderButton.isHidden = true - } - } - - /** - NativeUIKit: Call for update to current edit appearance. - */ - internal func updateEditAppearance() { - if isEditable { - switch avatarAppearance { - case .loading: - indicatorButton.isHidden = true - case .placeholder: - indicatorButton.isHidden = false - indicatorButton.setImage(indicatorAddImage) - indicatorButton.applyDefaultAppearance(with: indicatorAddColorise) - case .avatar(_): - indicatorButton.isHidden = false - indicatorButton.setImage(indicatorEditImage) - indicatorButton.applyDefaultAppearance(with: indicatorEditColorise) - } - } else { - indicatorButton.isHidden = true - } - - [placeholderButton, avatarButton, indicatorButton].forEach({ $0.isUserInteractionEnabled = isEditable }) - } - - // MARK: - Models - - /** - NativeUIKit: Appearance states for avatar view. - */ - public enum AvatarAppearance { - - case loading - case placeholder - case avatar(_ image: UIImage) - } - - /** - NativeUIKit: Appearance states for edit button. - */ - public enum EditAppearance { - - case none - case edit - case add - } -} -#endif diff --git a/Sources/NativeUIKit/Views/NativeLargeHeaderView.swift b/Sources/NativeUIKit/Views/NativeLargeHeaderView.swift deleted file mode 100644 index de07e5a..0000000 --- a/Sources/NativeUIKit/Views/NativeLargeHeaderView.swift +++ /dev/null @@ -1,97 +0,0 @@ -// The MIT License (MIT) -// Copyright © 2020 Ivan Vorobei (hello@ivanvorobei.io) -// -// 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. - -#if canImport(UIKit) && (os(iOS)) -import UIKit -import SparrowKit -import SPDiffable - -open class NativeLargeHeaderView: SPView { - - // MARK: - Views - - public let titleLabel = SPLabel().do { - $0.font = UIFont.preferredFont(forTextStyle: .title2, weight: .semibold) - if #available(iOS 13.0, *) { - $0.textColor = .label - } else { - $0.textColor = .black - } - } - - public let button = SPDimmedButton().do { - $0.applyDefaultAppearance(with: .tintedContent) - } - - // MARK: - Init - - open override func commonInit() { - super.commonInit() - layoutMargins = .init(top: NativeLayout.Spaces.default_double, left: .zero, bottom: 14, right: .zero) - insetsLayoutMarginsFromSafeArea = false - preservesSuperviewLayoutMargins = false - addSubviews(titleLabel, button) - } - - // MARK: - Layout - - open func layout(y: CGFloat) { - guard let superview = self.superview else { return } - setWidthAndFit(width: superview.layoutWidth) - setXCenter() - frame.origin.y = y - } - - open override func layoutSubviews() { - super.layoutSubviews() - titleLabel.layoutDynamicHeight( - x: layoutMargins.left, - y: layoutMargins.top, - width: layoutWidth - ) - button.sizeToFit() - button.setMaxXToSuperviewRightMargin() - button.center.y = titleLabel.center.y - } - - open override func sizeThatFits(_ size: CGSize) -> CGSize { - let superSize = super.sizeThatFits(size) - layoutSubviews() - return .init(width: superSize.width, height: titleLabel.frame.maxY + layoutMargins.bottom) - } - - // MARK: - Private - - var diffableButtonAction: (()->Void)? = nil { - didSet { - if let _ = diffableButtonAction { - self.button.addTarget(self, action: #selector(self.diffableButtonTargetAction), for: .touchUpInside) - } else { - self.button.removeTargetsAndActions() - } - } - } - - @objc func diffableButtonTargetAction() { - self.diffableButtonAction?() - } -} -#endif diff --git a/Sources/NativeUIKit/Views/NativePlaceholderView.swift b/Sources/NativeUIKit/Views/NativePlaceholderView.swift deleted file mode 100644 index 0278e7e..0000000 --- a/Sources/NativeUIKit/Views/NativePlaceholderView.swift +++ /dev/null @@ -1,157 +0,0 @@ -// The MIT License (MIT) -// Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) -// -// 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. - -#if canImport(UIKit) && (os(iOS)) -import UIKit -import SparrowKit - -open class NativePlaceholderView: SPButton { - - // MARK: - Views - - public let iconImageView = SPImageView().do { - $0.contentMode = .scaleAspectFit - } - - public let headerLabel = SPLabel().do { - $0.numberOfLines = .zero - $0.textAlignment = .center - $0.font = UIFont.preferredFont(forTextStyle: .title1, weight: .bold) - } - - public let descriptionLabel = SPLabel().do { - $0.numberOfLines = .zero - $0.textAlignment = .center - $0.font = UIFont.preferredFont(forTextStyle: .body, weight: .regular) - } - - // MARK: - Init - - public override init() { - super.init() - } - - public init(icon: UIImage, title: String, subtitle: String) { - super.init() - iconImageView.image = icon.alwaysTemplate - headerLabel.text = title - descriptionLabel.text = subtitle - } - - public required init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - open override func commonInit() { - super.commonInit() - layoutMargins = .zero - if #available(iOS 13.0, *) { - tintColor = .tertiaryLabel - } else { - tintColor = UIColor.black.alpha(0.3) - } - backgroundColor = .clear - iconImageView.tintColor = tintColor - addSubview(iconImageView) - headerLabel.textColor = tintColor - addSubview(headerLabel) - descriptionLabel.textColor = tintColor - addSubview(descriptionLabel) - } - - // MARK: - Ovveride - - open override var isHighlighted: Bool { - didSet { - UIView.animate(withDuration: 0.1, delay: 0, options: [.beginFromCurrentState, .allowUserInteraction], animations: { - self.alpha = self.isHighlighted ? NativeAppearance.actionable_element_highlight_opacity : 1 - }, completion: nil) - } - } - - // MARK: - Actions - - open func setVisible(_ state: Bool, animated: Bool) { - let work = { [weak self] in - guard let self = self else { return } - self.alpha = state ? 1 : .zero - } - if animated { - UIView.animate(withDuration: 0.45, delay: .zero, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: [.allowUserInteraction, .beginFromCurrentState], animations: { - work() - }, completion: nil) - } else { - work() - } - } - - // MARK: - Layout - - /** - NativeUIKit: Layout wrapper. Native way for layout placeholder. - */ - open func layout(y: CGFloat) { - guard let superview = self.superview else { return } - sizeToFit() - let width = min(superview.readableWidth, NativeLayout.Sizes.actionable_area_maximum_width) - frame.setWidth(width) - setXCenter() - frame.origin.y = y - } - - /** - NativeUIKit: Layout wrapper. Native way for layout placeholder. - */ - open func layoutCenter() { - guard let superview = self.superview else { return } - let width = min(superview.readableWidth, NativeLayout.Sizes.actionable_area_maximum_width) - setWidthAndFit(width: width) - setXCenter() - switch superview { - case _ as UICollectionView: - center.y = (superview.layoutHeight / 2) * 0.94 - case _ as UITableView: - center.y = (superview.layoutHeight / 2) * 0.94 - default: - center.y = (superview.frame.height / 2) * 0.94 - } - - } - - open override func layoutSubviews() { - super.layoutSubviews() - iconImageView.sizeToFit() - iconImageView.setXCenter() - iconImageView.frame.origin.y = layoutMargins.top - headerLabel.layoutDynamicHeight(width: layoutWidth) - headerLabel.frame.origin.y = iconImageView.frame.maxY + 12 - headerLabel.setXCenter() - descriptionLabel.layoutDynamicHeight(width: layoutWidth) - descriptionLabel.frame.origin.y = headerLabel.frame.maxY + 4 - descriptionLabel.setXCenter() - } - - open override func sizeThatFits(_ size: CGSize) -> CGSize { - layoutSubviews() - return .init(width: size.width, height: descriptionLabel.frame.maxY + layoutMargins.bottom) - } -} -#endif diff --git a/Sources/NativeUIKit/Views/NativePromoView.swift b/Sources/NativeUIKit/Views/NativePromoView.swift deleted file mode 100644 index 2288e4a..0000000 --- a/Sources/NativeUIKit/Views/NativePromoView.swift +++ /dev/null @@ -1,153 +0,0 @@ -// The MIT License (MIT) -// Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) -// -// 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. - -#if canImport(UIKit) && (os(iOS)) -import UIKit -import SparrowKit -import SPPerspective - -open class NativePromoView: SPView { - - // MARK: - Views - - public let titleLabel = SPLabel().do { - $0.font = UIFont.preferredFont(forTextStyle: .title2, weight: .semibold) - if #available(iOS 13.0, *) { - $0.textColor = .label - } else { - $0.textColor = .black - } - $0.numberOfLines = .zero - $0.textAlignment = .center - } - - public let descriptionLabel = SPLabel().do { - $0.font = UIFont.preferredFont(forTextStyle: .body, weight: .regular) - if #available(iOS 13.0, *) { - $0.textColor = .secondaryLabel - } else { - $0.textColor = .black.alpha(0.5) - } - $0.numberOfLines = .zero - $0.textAlignment = .center - } - - public let iconView = SPImageView().do { - $0.backgroundColor = .clear - } - - public let button = SPDimmedButton().do { - $0.applyDefaultAppearance(with: .tintedContent) - $0.titleLabel?.font = UIFont.preferredFont(forTextStyle: .body, weight: .semibold, addPoints: 2) - } - - public let areaView = SPView().do { - if #available(iOS 13.0, *) { - $0.backgroundColor = UIColor.secondarySystemBackground - } - $0.layer.cornerRadius = 15 - } - - // MARK: - Init - - open override func commonInit() { - super.commonInit() - layoutMargins = .zero - addSubview(areaView) - addSubview(titleLabel) - addSubview(descriptionLabel) - addSubview(iconView) - addSubview(button) - areaView.layoutMargins = .init(horizontal: 24, vertical: 24) - iconView.applyPerspective(.iOS14WidgetAnimatable) - } - - // MARK: - Layout - - open func layout(y: CGFloat) { - guard let superview = self.superview else { return } - var width = min(superview.readableWidth, NativeLayout.Sizes.actionable_area_maximum_width) - if (width == .zero) && (superview.layoutWidth != .zero) { width = superview.layoutWidth } - setWidthAndFit(width: width) - setXCenter() - frame.origin.y = y - } - - open override func layoutSubviews() { - super.layoutSubviews() - areaView.frame = .init(x: layoutMargins.left, y: layoutMargins.top, width: layoutWidth, height: areaView.frame.height) - - let labelsWidth = areaView.layoutWidth - - titleLabel.layoutDynamicHeight(width: labelsWidth) - titleLabel.setXCenter() - titleLabel.frame.origin.y = areaView.frame.origin.y + areaView.layoutMargins.top - - descriptionLabel.layoutDynamicHeight(width: labelsWidth) - descriptionLabel.setXCenter() - descriptionLabel.frame.origin.y = titleLabel.frame.maxY + 3 - - let iconSideSize = min(titleLabel.frame.width * 0.5, 120) - iconView.frame.setWidth(iconSideSize) - iconView.frame.setHeight(iconSideSize) - iconView.setXCenter() - iconView.frame.origin.y = descriptionLabel.frame.maxY + 16 - - areaView.frame.setHeight(iconView.frame.origin.y + iconView.frame.height / 2) - - button.sizeToFit() - button.setXCenter() - button.frame.origin.y = iconView.frame.maxY + 24 - } - - open override func sizeThatFits(_ size: CGSize) -> CGSize { - layoutSubviews() - return .init(width: size.width, height: button.frame.maxY + layoutMargins.bottom) - } -} - -open class NativePromoContainerView: SPView { - - // MARK: - Views - - public let promoView = NativePromoView() - - // MARK: - Init - - open override func commonInit() { - super.commonInit() - layoutMargins = .zero - addSubview(promoView) - } - - // MARK: - Layout - - open override func layoutSubviews() { - super.layoutSubviews() - promoView.layout(y: layoutMargins.top) - } - - open override func sizeThatFits(_ size: CGSize) -> CGSize { - layoutSubviews() - return .init(width: size.width, height: promoView.frame.maxY + layoutMargins.bottom) - } -} -#endif diff --git a/Sources/UIKitExtension/FillFile.swift b/Sources/UIKitExtension/FillFile.swift new file mode 100644 index 0000000..1db5470 --- /dev/null +++ b/Sources/UIKitExtension/FillFile.swift @@ -0,0 +1 @@ +import UIKit From febb5d1b903db1e6e1cf13c59a864b082c441051 Mon Sep 17 00:00:00 2001 From: Ivan Vorobei Date: Tue, 10 May 2022 18:47:32 +0300 Subject: [PATCH 02/16] Update README.md --- README.md | 210 +++++++----------------------------------------------- 1 file changed, 25 insertions(+), 185 deletions(-) diff --git a/README.md b/README.md index 039e02b..59548dd 100644 --- a/README.md +++ b/README.md @@ -1,215 +1,55 @@ -# NativeUIKit +# UIKitExtension -Mimicrated views and controls to native Apple appearance. If you have any ideas of what elements can be added, let me know. Here provided all elements which available, tap for open docs for it. +### Community -

    - - - -   - - +

    + + -   - - + + -   - - - -

    - -If you like the project, don't forget to `put star ★`
    Check out my other libraries: - -

    - - + +

    ## Navigate - [Installation](#installation) - - [Swift Package Manager](#swift-package-manager) + - [Swift Package Manager](#swift-package-manager)\ - [Manually](#manually) -- [Classes](#usage) - - [NativeLargeActionButton](#NativeLargeActionButton) - - [NativeSmallActionButton](#NativeSmallActionButton) - - [NativeAvatarView](#NativeAvatarView) - - [NativePlayPauseCompactButton](#NativePlayPauseCompactButton) -- [Сontribution](#сontribution) -- [Other Projects](#other-projects) -- [Russian Community](#russian-community) +- [Apps Using](#apps-using) ## Installation -Ready for use on iOS 12+, tvOS 12+ & watchOS 6.0+. Works with Swift 5+. Required Xcode 12.0 and higher. - - +Ready to use on iOS 11+. Supports iOS, tvOS, and `SwiftUI`. ### Swift Package Manager -The [Swift Package Manager](https://swift.org/package-manager/) is a tool for managing the distribution of Swift code. It’s integrated with the Swift build system to automate the process of downloading, compiling, and linking dependencies. - -To integrate using Xcode 12, specify it in `File > Swift Packages > Add Package Dependency...`: - -```ogdl -https://github.com/ivanvorobei/NativeUIKit -``` - -### Manually - -If you prefer not to use any of dependency managers, you can integrate manually. Put `Sources/NativeUIKit` folder in your Xcode project. Make sure to enable `Copy items if needed` and `Create groups`. - -## Classes - - -### [NativeLargeActionButton](https://github.com/ivanvorobei/NativeUIKit/blob/main/Sources/NativeUIKit/NativeLargeActionButton.swift) - -Usually used at the bottom of the screen. You can set an icon. You can set how to change the style when you click. Supports states `disabled` and `dimmed`. +In Xcode go to `File` -> `Packages` -> `Update to Latest Package Versions` and insert url: -

    - - - -

    - -Next code for usage: - -```swift -// Set Appearance and Content -let button = NativeLargeActionButton() -button.setImage(UIImage.init(systemName: "plus.circle.fill")!) -button.higlightStyle = .background -button.applyDefaultAppearance(with: .init(content: .custom(.white), background: .tint)) - -// or use Wrapper -button.set( - title: "Large Action", - icon: UIImage.init(systemName: "plus.circle.fill")!, - colorise: .init(content: .custom(.white), background: .tint) -) ``` - -Button support system layouts by `sizeToFit()`. Next code allow you to layout button with cutom width: - -```swift -button.sizeToFit() -button.frame = .init(x: 0, y: 0, width: 300, height: button.frame.height) +https://github.com/sparrowcode/SwiftBoost ``` -### [NativeSmallActionButton](https://github.com/ivanvorobei/NativeUIKit/blob/main/Sources/NativeUIKit/NativeSmallActionButton.swift) - -You definitely saw this button in the AppStore. You can use it without the icon.
    -Supports states `disabled` and `dimmed`. - -

    - - - -

    - -Next code for usage: +or adding it to the `dependencies` value of your `Package.swift`: ```swift -// Set Appearance and Content -let button = NativeSmallActionButton() -button.higlightStyle = .background -button.applyDefaultAppearance(with: .init(content: .custom(.white), background: .tint)) - -// or use Wrapper -button.set( - title: "Edit", - icon: nil, - colorise: .init(content: .custom(.white), background: .tint) -) +dependencies: [ + .package(url: "https://github.com/sparrowcode/UIKitExtension", .upToNextMajor(from: "2.0.0")) +] ``` -Button support system layouts by `sizeToFit()`. Next code allow you to layout button: - -```swift -button.sizeToFit() -``` - -### [NativeAvatarView](https://github.com/ivanvorobei/NativeUIKit/blob/main/Sources/NativeUIKit/NativeAvatarView.swift) - - - - - -Avatar view is container with 3 buttons - placeholder, avatar and indicator. For change avatar set `avatarAppearance`. For show or hide edit button set `isEditable`. - -Layout availbe by size to fit for content. In this case change placeholder size to valid SFSymbol for have correct fit. By default font side is 80 point. Or you can set fixed frame and ignore fitting size. - -```swift -let avatarView = NativeAvatarView() - -// You can set target, actions or even contex menu like usual button. -avatarView.placeholderButton.addTarget(self, action: #selector(didTap), for: .touchUpInside) -avatarView.avatarButton.addTarget(self, action: #selector(didTap), for: .touchUpInside) -avatarView.indicatorButton.addTarget(self, action: #selector(didTap), for: .touchUpInside) - -// For show edit button. -avatarView.isEditable = true - -// Layout. -// For fit by images. -avatarView.sizeToFit() -// By custom frame: -avatarView.frame = .init(x: 0, y: 0, width: 60, height: 60) -``` - -### [NativePlayPauseCompactButton](https://github.com/ivanvorobei/NativeUIKit/blob/main/Sources/NativeUIKit/NativePlayPauseCompactButton.swift) - -

    - - - -

    - -Simple play/pause button. - -```swift -let button = NativePlayPauseCompactButton() -// For fit to valid size. -button.sizeToFit() -// For change appearance. -button.appearance = .play //.pause -``` - -## Сontribution - -My English is very bad. You can see this once you read the documentation. I would really like to have clean and nice documentation. If you see gramatical errors and can help fix the Readme, please contact me hello@ivanvorobei.io or make a Pull Request. Thank you in advance! - -## Other Projects - -I love being helpful. Here I have provided a list of libraries that I keep up to date. For see `video previews` of libraries without install open [opensource.ivanvorobei.io](https://opensource.ivanvorobei.io) website.
    -I have libraries with native interface and managing permissions. Also available pack of useful extensions for boost your development process. - -

    - - - - - - -

    +### Manually -## Russian Community +If you prefer not to use any of dependency managers, you can integrate manually. Put `Sources/SwiftBoost` folder in your Xcode project. Make sure to enable `Copy items if needed` and `Create groups`. -Подписывайся в телеграмм-канал, если хочешь получать уведомления о новых туториалах.
    -Со сложными и непонятными задачами помогут в чате. +## Apps Using

    - - - - - - + + + +

    - -Видео-туториалы выклыдываю на [YouTube](https://tutorials.ivanvorobei.io/youtube): - -[![Tutorials on YouTube](https://cdn.ivanvorobei.io/github/readme/youtube-preview.jpg)](https://tutorials.ivanvorobei.io/youtube) From 83e4d4f850091d1fb8f5b0b3978155f8761480fe Mon Sep 17 00:00:00 2001 From: Ivan Vorobei Date: Tue, 10 May 2022 18:49:15 +0300 Subject: [PATCH 03/16] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 59548dd..827e023 100644 --- a/README.md +++ b/README.md @@ -23,14 +23,14 @@ ## Installation -Ready to use on iOS 11+. Supports iOS, tvOS, and `SwiftUI`. +Ready to use on iOS 12+, tvOS 12+, watchOS 6.0+. ### Swift Package Manager In Xcode go to `File` -> `Packages` -> `Update to Latest Package Versions` and insert url: ``` -https://github.com/sparrowcode/SwiftBoost +https://github.com/sparrowcode/UIKitExtension ``` or adding it to the `dependencies` value of your `Package.swift`: @@ -43,7 +43,7 @@ dependencies: [ ### Manually -If you prefer not to use any of dependency managers, you can integrate manually. Put `Sources/SwiftBoost` folder in your Xcode project. Make sure to enable `Copy items if needed` and `Create groups`. +If you prefer not to use any of dependency managers, you can integrate manually. Put `Sources/UIKitExtension` folder in your Xcode project. Make sure to enable `Copy items if needed` and `Create groups`. ## Apps Using From 85b36a254d458d0f97131f653abd575e7c861610 Mon Sep 17 00:00:00 2001 From: Ivan Vorobei Date: Tue, 10 May 2022 18:50:23 +0300 Subject: [PATCH 04/16] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 827e023..c2e25b2 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ ### Community

    - + @@ -17,7 +17,7 @@ ## Navigate - [Installation](#installation) - - [Swift Package Manager](#swift-package-manager)\ + - [Swift Package Manager](#swift-package-manager) - [Manually](#manually) - [Apps Using](#apps-using) From b6ba1f25785eb2b4d764ed43df7bc7c397ad26c2 Mon Sep 17 00:00:00 2001 From: Ivan Vorobei Date: Tue, 10 May 2022 18:51:50 +0300 Subject: [PATCH 05/16] Update Package.swift --- Package.swift | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/Package.swift b/Package.swift index 457358e..ce4056d 100644 --- a/Package.swift +++ b/Package.swift @@ -3,7 +3,7 @@ import PackageDescription let package = Package( - name: "NativeUIKit", + name: "UIKitExtension", platforms: [ .iOS(.v12), .tvOS(.v12), @@ -11,26 +11,15 @@ let package = Package( ], products: [ .library( - name: "NativeUIKit", targets: ["NativeUIKit"] + name: "UIKitExtension", targets: ["UIKitExtension"] ) ], - dependencies: [ - .package(url: "https://github.com/ivanvorobei/SparrowKit", .upToNextMajor(from: "3.6.0")), - .package(url: "https://github.com/ivanvorobei/SPPerspective", .upToNextMajor(from: "1.4.1")), - .package(url: "https://github.com/ivanvorobei/SPDiffable", .upToNextMajor(from: "4.1.0")), - .package(url: "https://github.com/ivanvorobei/SPPageController", .upToNextMajor(from: "1.3.2")) - ], + dependencies: [], targets: [ .target( - name: "NativeUIKit", - dependencies: [ - .product(name: "SparrowKit", package: "SparrowKit"), - .product(name: "SPPerspective", package: "SPPerspective"), - .product(name: "SPDiffable", package: "SPDiffable"), - .product(name: "SPPageController", package: "SPPageController") - ], + name: "UIKitExtension", swiftSettings: [ - .define("NATIVEUIKIT_SPM") + .define("UIKITEXTENSION_SPM") ] ) ], From ca5672d2cb3459687317b58a52940dbda3cf47c1 Mon Sep 17 00:00:00 2001 From: Ivan Vorobei Date: Sun, 15 May 2022 17:32:18 +0300 Subject: [PATCH 06/16] Clean sources. --- .gitignore | 5 +---- Package.swift | 7 ++++--- README.md | 2 +- Sources/{UIKitExtension => FRMNAME}/FillFile.swift | 0 TODO.md | 9 --------- 5 files changed, 6 insertions(+), 17 deletions(-) rename Sources/{UIKitExtension => FRMNAME}/FillFile.swift (100%) delete mode 100644 TODO.md diff --git a/.gitignore b/.gitignore index edbeebc..4f40064 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,6 @@ -# osX files +# macOS Files .DS_Store .Trashes # Swift Package Manager .swiftpm - -# Assets -Elements.sketch diff --git a/Package.swift b/Package.swift index ce4056d..d373cfc 100644 --- a/Package.swift +++ b/Package.swift @@ -5,13 +5,14 @@ import PackageDescription let package = Package( name: "UIKitExtension", platforms: [ - .iOS(.v12), - .tvOS(.v12), + .iOS(.v13), + .tvOS(.v13), .watchOS(.v6) ], products: [ .library( - name: "UIKitExtension", targets: ["UIKitExtension"] + name: "UIKitExtension", + targets: ["UIKitExtension"] ) ], dependencies: [], diff --git a/README.md b/README.md index c2e25b2..e547cc2 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ ## Installation -Ready to use on iOS 12+, tvOS 12+, watchOS 6.0+. +Ready to use on iOS 13+, tvOS 13+ & watchOS 6+ ### Swift Package Manager diff --git a/Sources/UIKitExtension/FillFile.swift b/Sources/FRMNAME/FillFile.swift similarity index 100% rename from Sources/UIKitExtension/FillFile.swift rename to Sources/FRMNAME/FillFile.swift diff --git a/TODO.md b/TODO.md deleted file mode 100644 index 7ae73c8..0000000 --- a/TODO.md +++ /dev/null @@ -1,9 +0,0 @@ -# TODO - -Here provided ideas or features which will be implemented soon. - -- Readme. -- Example app. -- Add to list in opensource. -- Add docs. -- Added examples of controllers. From 532f380ba205e99c887a3d140c0046a93a9e4bef Mon Sep 17 00:00:00 2001 From: Ivan Vorobei Date: Tue, 19 Jul 2022 13:39:56 +0300 Subject: [PATCH 07/16] Added app delegates. --- Package.swift | 10 +- Sources/FRMNAME/FillFile.swift | 1 - .../Scenes/UICommonWindowSceneDelegate.swift | 54 +++++ .../Scenes/UIScenesApplicationDelegate.swift | 8 + .../Window/UIWindowApplicationDelegate.swift | 52 ++++ Sources/UIKitExtension/CommonInit.swift | 225 ++++++++++++++++++ .../UICommonNavigationController.swift | 76 ++++++ .../Extensions/UIColorExtension.swift | 45 ++++ .../Extensions/UIImageExtension.swift | 25 ++ .../UIViewControllerExtension.swift | 18 ++ Sources/UIKitExtension/Import.swift | 2 + 11 files changed, 512 insertions(+), 4 deletions(-) delete mode 100644 Sources/FRMNAME/FillFile.swift create mode 100644 Sources/UIKitExtension/AppDelegate/Scenes/UICommonWindowSceneDelegate.swift create mode 100644 Sources/UIKitExtension/AppDelegate/Scenes/UIScenesApplicationDelegate.swift create mode 100644 Sources/UIKitExtension/AppDelegate/Window/UIWindowApplicationDelegate.swift create mode 100644 Sources/UIKitExtension/CommonInit.swift create mode 100644 Sources/UIKitExtension/Controllers/UICommonNavigationController.swift create mode 100644 Sources/UIKitExtension/Extensions/UIColorExtension.swift create mode 100644 Sources/UIKitExtension/Extensions/UIImageExtension.swift create mode 100644 Sources/UIKitExtension/Extensions/UIViewControllerExtension.swift create mode 100644 Sources/UIKitExtension/Import.swift diff --git a/Package.swift b/Package.swift index d373cfc..4e0efe1 100644 --- a/Package.swift +++ b/Package.swift @@ -6,8 +6,7 @@ let package = Package( name: "UIKitExtension", platforms: [ .iOS(.v13), - .tvOS(.v13), - .watchOS(.v6) + .tvOS(.v13) ], products: [ .library( @@ -15,10 +14,15 @@ let package = Package( targets: ["UIKitExtension"] ) ], - dependencies: [], + dependencies: [ + .package(url: "https://github.com/sparrowcode/SwiftBoost", .upToNextMajor(from: "4.0.0")), + ], targets: [ .target( name: "UIKitExtension", + dependencies: [ + .product(name: "SwiftBoost", package: "SwiftBoost") + ], swiftSettings: [ .define("UIKITEXTENSION_SPM") ] diff --git a/Sources/FRMNAME/FillFile.swift b/Sources/FRMNAME/FillFile.swift deleted file mode 100644 index 1db5470..0000000 --- a/Sources/FRMNAME/FillFile.swift +++ /dev/null @@ -1 +0,0 @@ -import UIKit diff --git a/Sources/UIKitExtension/AppDelegate/Scenes/UICommonWindowSceneDelegate.swift b/Sources/UIKitExtension/AppDelegate/Scenes/UICommonWindowSceneDelegate.swift new file mode 100644 index 0000000..89a795a --- /dev/null +++ b/Sources/UIKitExtension/AppDelegate/Scenes/UICommonWindowSceneDelegate.swift @@ -0,0 +1,54 @@ +#if canImport(UIKit) && (os(iOS)) +import UIKit + +/** + UIKitExtension: Basic class for scene. + + Has ready use window property and assets funcs for present window. + */ +@available(iOS 13.0, *) +open class UICommonWindowSceneDelegate: UIResponder, UIWindowSceneDelegate { + + /** + UIKitExtension: Ready-use window property. + + For correct configure, call `makeKeyAndVisible`. + */ + open var window: UIWindow? + + /** + UIKitExtension: Configure window and make it present. + + This method get controller after apply tint and window scene, in some cases it critical. For example tint of window get before valid tint set. It happen with first window and launch. For solve it use this. + + - parameter scene: Scene which sync to window. + - parameter createViewControllerHandler: Handler for get root controller for window. + - parameter tint: Tint color for window. + */ + open func makeKeyAndVisible(in scene: UIWindowScene, createViewControllerHandler: () -> UIViewController, tint: UIColor) { + window = UIWindow(frame: scene.coordinateSpace.bounds) + window?.windowScene = scene + window?.tintColor = tint + window?.rootViewController = createViewControllerHandler() + window?.makeKeyAndVisible() + } + + /** + UIKitExtension: Configure window and make it present. + + Set scene, tint and already created controller. + + - warning: + If you get troubles with tinting, please, check other method `makeKeyAndVisible`. + + - parameter scene: Scene which sync to window. + - parameter viewController: Root controller for window. + - parameter tint: Tint color for window. + */ + open func makeKeyAndVisible(in scene: UIWindowScene, viewController: UIViewController, tint: UIColor) { + makeKeyAndVisible(in: scene, createViewControllerHandler: { + return viewController + }, tint: tint) + } +} +#endif diff --git a/Sources/UIKitExtension/AppDelegate/Scenes/UIScenesApplicationDelegate.swift b/Sources/UIKitExtension/AppDelegate/Scenes/UIScenesApplicationDelegate.swift new file mode 100644 index 0000000..1e55b36 --- /dev/null +++ b/Sources/UIKitExtension/AppDelegate/Scenes/UIScenesApplicationDelegate.swift @@ -0,0 +1,8 @@ +#if canImport(UIKit) && (os(iOS)) +import UIKit + +/** + UIKitExtension: App class which using with scenes. + */ +open class UIScenesApplicationDelegate: UIResponder, UIApplicationDelegate {} +#endif diff --git a/Sources/UIKitExtension/AppDelegate/Window/UIWindowApplicationDelegate.swift b/Sources/UIKitExtension/AppDelegate/Window/UIWindowApplicationDelegate.swift new file mode 100644 index 0000000..654e2b6 --- /dev/null +++ b/Sources/UIKitExtension/AppDelegate/Window/UIWindowApplicationDelegate.swift @@ -0,0 +1,52 @@ +#if canImport(UIKit) && (os(iOS) || os(tvOS)) +import UIKit + +/** + UIKitExtension: App which not using with scenes, only with one window key. + */ +open class UIWindowApplicationDelegate: UIResponder, UIApplicationDelegate { + + /** + UIKitExtension: Ready-use window property. + + For correct configure, call `makeKeyAndVisible`. + */ + open var window: UIWindow? + + /** + UIKitExtension: Configure window and make it present. + + Method init window and configure with your tint and controller. + + - warning: + Use it only if none-scene mode. + + - parameter createViewControllerHandler: Handler for get root controller for window. + - parameter tint: Tint color for window. + */ + open func makeKeyAndVisible(createViewControllerHandler: () -> UIViewController, tint: UIColor) { + let frame = UIScreen.main.bounds + window = UIWindow(frame: frame) + window?.tintColor = tint + window?.rootViewController = createViewControllerHandler() + window?.makeKeyAndVisible() + } + + /** + UIKitExtension: Configure window and make it present. + + Method init window and configure with your tint and controller. + + - warning: + Use it only if none-scene mode. + + - parameter viewController: Root controller for window. + - parameter tint: Tint color for window. + */ + open func makeKeyAndVisible(viewController: UIViewController, tint: UIColor) { + makeKeyAndVisible(createViewControllerHandler: { + return viewController + }, tint: tint) + } +} +#endif diff --git a/Sources/UIKitExtension/CommonInit.swift b/Sources/UIKitExtension/CommonInit.swift new file mode 100644 index 0000000..4492d40 --- /dev/null +++ b/Sources/UIKitExtension/CommonInit.swift @@ -0,0 +1,225 @@ +#if canImport(UIKit) && (os(iOS) || os(tvOS)) +import UIKit + +public protocol UICommonInit { + + /** + UIKitExtension: Wrapper of init. + Called in each init and using for configuration. + + No need ovveride init. Using one function for configurate view. + */ + func commonInit() +} + +class UICommonView: UIView, UICommonInit { + + public init() { + super.init(frame: CGRect.zero) + commonInit() + } + + public override init(frame: CGRect) { + super.init(frame: frame) + commonInit() + } + + public required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + commonInit() + } + + open func commonInit() {} +} + +open class UICommonControl: UIControl, UICommonInit { + + public init() { + super.init(frame: CGRect.zero) + commonInit() + } + + public override init(frame: CGRect) { + super.init(frame: frame) + commonInit() + } + + public required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + commonInit() + } + + open func commonInit() {} +} + +open class UICommonButton: UIButton, UICommonInit { + + public init() { + super.init(frame: CGRect.zero) + commonInit() + } + + public override init(frame: CGRect) { + super.init(frame: frame) + commonInit() + } + + public required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + commonInit() + } + + open func commonInit() {} +} + +open class UICommonImageView: UIImageView, UICommonInit { + + public init() { + super.init(frame: .zero) + commonInit() + } + + public init(image: UIImage?, contentMode: UIImageView.ContentMode) { + super.init(image: image) + self.contentMode = contentMode + commonInit() + } + + public required init?(coder: NSCoder) { + super.init(coder: coder) + commonInit() + } + + open func commonInit() {} +} + +open class UICommonLabel: UILabel, UICommonInit { + + public init() { + super.init(frame: .zero) + commonInit() + } + + public required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + commonInit() + } + + open func commonInit() {} +} + +open class UICommonSlider: UISlider, UICommonInit { + + public init() { + super.init(frame: .zero) + commonInit() + } + + public required init?(coder: NSCoder) { + super.init(coder: coder) + commonInit() + } + + open func commonInit() {} +} + +open class UICommonTextField: UITextField, UICommonInit { + + public init() { + super.init(frame: CGRect.zero) + commonInit() + } + + public override init(frame: CGRect) { + super.init(frame: frame) + commonInit() + } + + public required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + commonInit() + } + + open func commonInit() {} +} + +public class UICommonInitTextView: UITextView, UICommonInit { + + public init() { + super.init(frame: .zero, textContainer: nil) + commonInit() + } + + public required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + commonInit() + } + + open func commonInit() {} +} + +open class UICommonCollectionView: UICollectionView, UICommonInit { + + public init(collectionViewLayout layout: UICollectionViewLayout) { + super.init(frame: .zero, collectionViewLayout: layout) + commonInit() + } + + public required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + commonInit() + } + + open func commonInit() {} +} + +open class UICommonCollectionViewCell: UICollectionViewCell, UICommonInit { + + public init() { + super.init(frame: .zero) + commonInit() + } + + public override init(frame: CGRect) { + super.init(frame: frame) + commonInit() + } + + public required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + commonInit() + } + + open func commonInit() {} +} + +open class UICommonTableView: UITableView, UICommonInit { + + public init(style: UITableView.Style) { + super.init(frame: .zero, style: style) + commonInit() + } + + public required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + commonInit() + } + + open func commonInit() {} +} + +open class UICommonTableViewCell: UITableViewCell, UICommonInit { + + public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + commonInit() + } + + public required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + commonInit() + } + + open func commonInit() {} +} +#endif diff --git a/Sources/UIKitExtension/Controllers/UICommonNavigationController.swift b/Sources/UIKitExtension/Controllers/UICommonNavigationController.swift new file mode 100644 index 0000000..a0770f5 --- /dev/null +++ b/Sources/UIKitExtension/Controllers/UICommonNavigationController.swift @@ -0,0 +1,76 @@ +import UIKit + +open class UICommonNavigationController: UINavigationController, UICommonInit { + + // MARK: - Init + + public override init(rootViewController: UIViewController) { + super.init(rootViewController: rootViewController) + commonInit() + } + + public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { + super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) + commonInit() + } + + public required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + } + + public override init(navigationBarClass: AnyClass?, toolbarClass: AnyClass?) { + super.init(navigationBarClass: navigationBarClass, toolbarClass: toolbarClass) + } + + open func commonInit() {} + + // MARK: - Layout + + /** + UIKitExtension: If set to `true`, navigation bar apply horizontal margins from view of navigation controller. + Default is `false`. + */ + open var inheritLayoutMarginsForNavigationBar: Bool = false + + /** + UIKitExtension: If set to `true`, child controllers apply horizontal margins from view of navigation controller. + Default is `false`. + */ + open var inheritLayoutMarginsForСhilds: Bool = false + + open override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + + // Inhert of Navigation Bar + + if inheritLayoutMarginsForNavigationBar { + + let leftMargin = view.layoutMargins.left + let rightMargin = view.layoutMargins.left + + if navigationBar.layoutMargins.left != leftMargin { + navigationBar.layoutMargins.left = leftMargin + } + if navigationBar.layoutMargins.right != rightMargin { + navigationBar.layoutMargins.right = rightMargin + } + } + + // Inhert Childs Controllers + + if inheritLayoutMarginsForСhilds { + + let leftMargin = view.layoutMargins.left + let rightMargin = view.layoutMargins.left + + for childController in viewControllers { + if childController.view.layoutMargins.left != leftMargin { + childController.view.layoutMargins.left = leftMargin + } + if childController.view.layoutMargins.right != rightMargin { + childController.view.layoutMargins.right = rightMargin + } + } + } + } +} diff --git a/Sources/UIKitExtension/Extensions/UIColorExtension.swift b/Sources/UIKitExtension/Extensions/UIColorExtension.swift new file mode 100644 index 0000000..3e779d2 --- /dev/null +++ b/Sources/UIKitExtension/Extensions/UIColorExtension.swift @@ -0,0 +1,45 @@ +#if canImport(UIKit) +import UIKit + +extension UIColor { + + /** + UIKitExtension: New color to system stack. + Its color for empty areas and it usually downed of main background color. + */ + public static var systemDownedBackground: UIColor { + let lightColor = UIColor.secondarySystemBackground.mixWithColor(.darkGray, amount: 0.09).mixWithColor(UIColor.systemBlue, amount: 0.01) + let darkColor = UIColor.secondarySystemBackground + return UIColor.init(light: lightColor, dark: darkColor) + } + + + /** + UIKitExtension: New color to system grouped stack. + Its color for empty aread and it usually downed of content background color. + */ + public static var systemDownedGroupedBackground: UIColor { + return UIColor.init(dynamicProvider: { trait in + if trait.userInterfaceStyle == .light { + return UIColor.systemGroupedBackground.mixWithColor(.darkGray, amount: 0.09).mixWithColor(UIColor.systemBlue, amount: 0.01) + } else { + return UIColor.secondarySystemGroupedBackground.alpha(0.7) + } + }) + } + + /** + UIKitExtension: Dimmed content color. + */ + public static var dimmedContent: UIColor { + return UIColor.secondaryLabel + } + + /** + UIKitExtension: Dimmed background color. + */ + public static var dimmedBackground: UIColor { + dimmedContent.alpha(0.1) + } +} +#endif diff --git a/Sources/UIKitExtension/Extensions/UIImageExtension.swift b/Sources/UIKitExtension/Extensions/UIImageExtension.swift new file mode 100644 index 0000000..d57979d --- /dev/null +++ b/Sources/UIKitExtension/Extensions/UIImageExtension.swift @@ -0,0 +1,25 @@ +#if canImport(UIKit) +import UIKit + +public extension UIImage { + + /** + UIKitExtension: For iOS version get fill image, for macOS get lines images. + */ + static func adaptive(_ name: String) -> UIImage { + var formattedName = name + let fillSuffix = ".fill" + if UIDevice.current.isMac { + if name.hasSuffix(fillSuffix) { + formattedName = name.removedSuffix(fillSuffix) + } + } else { + if !name.hasSuffix(fillSuffix) { + formattedName = name + fillSuffix + } + } + let image = UIImage.init(systemName: formattedName) + return image ?? UIImage.system(name) + } +} +#endif diff --git a/Sources/UIKitExtension/Extensions/UIViewControllerExtension.swift b/Sources/UIKitExtension/Extensions/UIViewControllerExtension.swift new file mode 100644 index 0000000..094ef16 --- /dev/null +++ b/Sources/UIKitExtension/Extensions/UIViewControllerExtension.swift @@ -0,0 +1,18 @@ +// +// File.swift +// +// +// Created by Ivan Vorobei on 19.07.22. +// + +import Foundation + +/* + @objc open func wrapToNavigationController(prefersLargeTitles: Bool) -> SPNavigationController { + let navigationController = SPNavigationController(rootViewController: self) + #if os(iOS) + navigationController.navigationBar.prefersLargeTitles = prefersLargeTitles + #endif + return navigationController + } + */ diff --git a/Sources/UIKitExtension/Import.swift b/Sources/UIKitExtension/Import.swift new file mode 100644 index 0000000..70e5cf2 --- /dev/null +++ b/Sources/UIKitExtension/Import.swift @@ -0,0 +1,2 @@ +import Foundation +import SwiftBoost From 9a5580091479f3a7301e81f4f79785b0f70f00f1 Mon Sep 17 00:00:00 2001 From: Ivan Vorobei Date: Mon, 8 Aug 2022 12:24:16 +0300 Subject: [PATCH 08/16] Added layout and interface. Added dimmed button and large button. --- Package.swift | 4 +- Sources/UIKitExtension/Appearance.swift | 11 + Sources/UIKitExtension/CommonInit.swift | 30 +- ...rTableController+HeaderContainerView.swift | 45 +++ .../Table/UIHeaderTableController.swift | 46 ++++ .../Controls/Buttons/UIDimmedButton.swift | 256 ++++++++++++++++++ .../Buttons/UILargeActionButton.swift | 80 ++++++ Sources/UIKitExtension/Layout.swift | 24 ++ 8 files changed, 494 insertions(+), 2 deletions(-) create mode 100644 Sources/UIKitExtension/Appearance.swift create mode 100644 Sources/UIKitExtension/Controllers/Table/UIHeaderTableController+HeaderContainerView.swift create mode 100644 Sources/UIKitExtension/Controllers/Table/UIHeaderTableController.swift create mode 100644 Sources/UIKitExtension/Controls/Buttons/UIDimmedButton.swift create mode 100644 Sources/UIKitExtension/Controls/Buttons/UILargeActionButton.swift create mode 100644 Sources/UIKitExtension/Layout.swift diff --git a/Package.swift b/Package.swift index 4e0efe1..31b7adf 100644 --- a/Package.swift +++ b/Package.swift @@ -16,12 +16,14 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/sparrowcode/SwiftBoost", .upToNextMajor(from: "4.0.0")), + .package(url: "https://github.com/sparrowcode/DiffableKit", .upToNextMajor(from: "5.0.0")), ], targets: [ .target( name: "UIKitExtension", dependencies: [ - .product(name: "SwiftBoost", package: "SwiftBoost") + .product(name: "SwiftBoost", package: "SwiftBoost"), + .product(name: "DiffableKit", package: "DiffableKit") ], swiftSettings: [ .define("UIKITEXTENSION_SPM") diff --git a/Sources/UIKitExtension/Appearance.swift b/Sources/UIKitExtension/Appearance.swift new file mode 100644 index 0000000..08a7259 --- /dev/null +++ b/Sources/UIKitExtension/Appearance.swift @@ -0,0 +1,11 @@ +#if canImport(UIKit) && (os(iOS)) +import UIKit + +enum Appearance { + + enum Corners { + + static var readable_area: CGFloat { 16 } + } +} +#endif diff --git a/Sources/UIKitExtension/CommonInit.swift b/Sources/UIKitExtension/CommonInit.swift index 4492d40..39c38eb 100644 --- a/Sources/UIKitExtension/CommonInit.swift +++ b/Sources/UIKitExtension/CommonInit.swift @@ -12,7 +12,7 @@ public protocol UICommonInit { func commonInit() } -class UICommonView: UIView, UICommonInit { +open class UICommonView: UIView, UICommonInit { public init() { super.init(frame: CGRect.zero) @@ -70,6 +70,34 @@ open class UICommonButton: UIButton, UICommonInit { } open func commonInit() {} + + // MARK: - Layout + + /** + UIKitExtension: Inset between image and title. + + No need any additional processing layout. + It work automatically in `layoutSubviews` method. + */ + open var titleImageInset: CGFloat? = nil + + open override func layoutSubviews() { + super.layoutSubviews() + if let inset = titleImageInset { + if imageView?.image == nil { + imageEdgeInsets.right = .zero + titleEdgeInsets.left = .zero + } else { + if ltr { + imageEdgeInsets.right = inset + titleEdgeInsets.left = inset + } else { + imageEdgeInsets.right = -inset + titleEdgeInsets.left = -inset + } + } + } + } } open class UICommonImageView: UIImageView, UICommonInit { diff --git a/Sources/UIKitExtension/Controllers/Table/UIHeaderTableController+HeaderContainerView.swift b/Sources/UIKitExtension/Controllers/Table/UIHeaderTableController+HeaderContainerView.swift new file mode 100644 index 0000000..90e241e --- /dev/null +++ b/Sources/UIKitExtension/Controllers/Table/UIHeaderTableController+HeaderContainerView.swift @@ -0,0 +1,45 @@ +import UIKit + +extension UIHeaderTableController { + + open class HeaderContainerView: UICommonView { + + // MARK: - Public + + public let contentView: UIView + + // MARK: - Init + + public init(contentView: UIView) { + self.contentView = contentView + super.init() + } + + public required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + public override func commonInit() { + super.commonInit() + insetsLayoutMarginsFromSafeArea = false + contentView.insetsLayoutMarginsFromSafeArea = false + layoutMargins = .zero + addSubview(contentView) + } + + // MARK: - Layout + + public override func layoutSubviews() { + super.layoutSubviews() + contentView.setWidthAndFit(width: layoutWidth) + contentView.frame.origin.x = layoutMargins.left + contentView.frame.origin.y = layoutMargins.top + } + + public override func sizeThatFits(_ size: CGSize) -> CGSize { + frame.setWidth(size.width) + layoutSubviews() + return .init(width: size.width, height: contentView.frame.maxY + layoutMargins.bottom) + } + } +} diff --git a/Sources/UIKitExtension/Controllers/Table/UIHeaderTableController.swift b/Sources/UIKitExtension/Controllers/Table/UIHeaderTableController.swift new file mode 100644 index 0000000..ce2c4db --- /dev/null +++ b/Sources/UIKitExtension/Controllers/Table/UIHeaderTableController.swift @@ -0,0 +1,46 @@ +import UIKit +import DiffableKit + +open class UIHeaderTableController: DiffableTableController { + + // MARK: - Public + + open func setHeaderView(_ view: UIView) { + let headerContainerView = HeaderContainerView(contentView: view) + headerContainerView.setWidthAndFit(width: self.view.frame.width) + tableView.tableHeaderView = headerContainerView + } + + open func setSpaceBetweenHeaderAndCells(_ value: CGFloat) { + tableView.tableHeaderView?.layoutMargins.bottom = value + } + + // MARK: - Layout + + open override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + + if let headerContainerView = tableView.tableHeaderView as? HeaderContainerView { + + if headerContainerView.contentView.layoutMargins.left != tableView.layoutMargins.left { + headerContainerView.contentView.layoutMargins.left = tableView.layoutMargins.left + } + + if headerContainerView.contentView.layoutMargins.right != tableView.layoutMargins.right { + headerContainerView.contentView.layoutMargins.right = tableView.layoutMargins.right + } + + headerContainerView.setWidthAndFit(width: view.frame.width) + + if cachedHeaderHeight != headerContainerView.frame.height { + cachedHeaderHeight = headerContainerView.frame.height + DispatchQueue.main.async { [weak self] in + guard let self = self else { return } + self.diffableDataSource?.updateLayout(animated: true, completion: nil) + } + } + } + } + + private var cachedHeaderHeight: CGFloat? = nil +} diff --git a/Sources/UIKitExtension/Controls/Buttons/UIDimmedButton.swift b/Sources/UIKitExtension/Controls/Buttons/UIDimmedButton.swift new file mode 100644 index 0000000..99c9838 --- /dev/null +++ b/Sources/UIKitExtension/Controls/Buttons/UIDimmedButton.swift @@ -0,0 +1,256 @@ +#if canImport(UIKit) && (os(iOS) || os(tvOS)) +import UIKit + +/** + UIKitExtension: Button with process dimmed colors. + + Button support color states like `basic`, `dimmed` and `disabled`. + For change it using `Colorise` object. + + Also available tinting for all modes. + */ +open class UIDimmedButton: UICommonButton { + + open override var isHighlighted: Bool { + didSet { + updateAppearance() + } + } + + open override var isEnabled: Bool { + didSet { + updateAppearance() + } + } + + open override func tintColorDidChange() { + super.tintColorDidChange() + updateAppearance() + } + + /** + UIKitExtension: Higlight style when button pressing. + + If set content, only label and image change opacity. + If choosed background, area of button will change opacity. + */ + open var higlightStyle = HiglightStyle.default + + // MARK: - Colorises + + private lazy var defaultColorise = Colorise(content: tintColor, background: .clear) + private lazy var disabledColorise = Colorise(content: .dimmedContent, background: .clear) + private lazy var dimmedColorise = Colorise(content: .dimmedContent, background: .clear) + + // MARK: - Data + + /** + UIKitExtension: Opacity of elements when button higlight. + */ + open var highlightOpacity: CGFloat = 0.7 + + // MARK: - Process + + open func setColorise(_ colorise: Colorise, for state: AppearanceState) { + switch state { + case .default: + defaultColorise = colorise + case .dimmed: + dimmedColorise = colorise + case .disabled: + disabledColorise = colorise + } + updateAppearance() + } + + open func getColorise(for state: AppearanceState) -> Colorise { + switch state { + case .default: + return defaultColorise + case .dimmed: + return dimmedColorise + case .disabled: + return disabledColorise + } + } + + /** + UIKitExtension: Set the color scheme for the default state. + `.dimmed` and `.disabled` states calculate automaticaly. + */ + open func applyDefaultAppearance(with colorise: Colorise? = nil) { + defaultColorise = colorise ?? Colorise(content: .tint, background: .custom(.clear)) + let defaultBackgroundColor = colorFromColoriseMode(defaultColorise.background) + let dimmedBackground = (defaultBackgroundColor == .clear) ? UIColor.clear : .dimmedBackground + dimmedColorise = Colorise(content: .dimmedContent, background: dimmedBackground) + disabledColorise = Colorise(content: .dimmedContent, background: dimmedBackground) + updateAppearance() + } + + private func updateAppearance() { + if tintAdjustmentMode == .dimmed { + applyColorise(dimmedColorise) + } else if isEnabled { + let colorise = self.defaultColorise + applyColorise(colorise) + + // Image Tint Color + var imageTintColor = colorFromColoriseMode(colorise.icon) + imageTintColor = imageTintColor.withAlphaComponent(isHighlighted ? imageTintColor.alpha * highlightOpacity : imageTintColor.alpha) + imageView?.tintColor = imageTintColor + + // Higlight + switch higlightStyle { + case .content: + break + case .background: + let color = colorFromColoriseMode(colorise.background) + backgroundColor = color.alpha(isHighlighted ? color.alpha * highlightOpacity : color.alpha) + } + } else { + applyColorise(disabledColorise) + } + } + + private func applyColorise(_ colorise: Colorise) { + let titleColor = colorFromColoriseMode(colorise.title) + setTitleColor(titleColor, for: .normal) + setTitleColor(titleColor.withAlphaComponent(highlightOpacity), for: .highlighted) + imageView?.tintColor = colorFromColoriseMode(colorise.icon) + backgroundColor = colorFromColoriseMode(colorise.background) + } + + /** + UIKitExtension: Get valid color by `Colorise.Mode` for this button. + + For example each call can return diffrent color for if changing tint. + If mode in custom always return this value. + + - parameter mode: Colorise mode from which need get color. + */ + public func colorFromColoriseMode(_ mode: Colorise.Mode) -> UIColor { + switch mode { + case .tint: return self.tintColor + case .secondaryTint: return self.tintColor.secondary + case .custom(let color): return color + } + } + + // MARK: - Models + + /** + UIKitExtension: Represent colors for state of button for elements. + */ + public struct Colorise { + + public var title: Mode + public var icon: Mode + public var background: Mode + + public init(content: Mode, background: Mode) { + self.title = content + self.icon = content + self.background = background + } + + public init(content: UIColor, background: UIColor) { + self.title = .custom(content) + self.icon = .custom(content) + self.background = .custom(background) + } + + public init(content: UIColor, icon: UIColor, background: UIColor) { + self.title = .custom(content) + self.icon = .custom(icon) + self.background = .custom(background) + } + + /** + UIKitExtension: Represent of rendering colors in button. + + It need for have dynamic tint color and fixed custom. + */ + public enum Mode { + + /** + UIKitExtension: Always specific color. + */ + case custom(UIColor) + + /** + UIKitExtension: Observe and apply tint color of button. + */ + case tint + + /** + UIKitExtension: Observe and apply secondary tint color of button. + */ + case secondaryTint + } + + // MARK: - Ready Use + + public static var tintedContent: Colorise { + return .init(content: .tint, background: .custom(.clear)) + } + + #if os(iOS) + @available(iOS 13.0, *) + public static var tintedContentSecondaryBackground : Colorise { + return .init(content: .tint, background: .custom(.secondarySystemBackground)) + } + + @available(iOS 13.0, *) + public static var tintedContentTertiaryBackground : Colorise { + return .init(content: .tint, background: .custom(.tertiarySystemBackground)) + } + + @available(iOS 13.0, *) + public static var tintedContentSecondaryGroupBackground : Colorise { + return .init(content: .tint, background: .custom(.secondarySystemGroupedBackground)) + } + + @available(iOS 13.0, *) + public static var tintedContentTertiaryGroupBackground : Colorise { + return .init(content: .tint, background: .custom(.tertiarySystemGroupedBackground)) + } + #endif + + public static var tintedColorful: Colorise { + return .init(content: .custom(.white), background: .tint) + } + + public static var tinted: Colorise { + return .init(content: .tint, background: .secondaryTint) + } + } + + public enum HiglightStyle { + + case content + case background + + static var `default`: HiglightStyle { + return .background + } + } + + public enum AppearanceState { + + /** + UIKitExtension: The button is in the normal state. + */ + case `default` + + /** + UIKitExtension: The button is in a dimmed state, for example when another `UIViewController` is presented. + */ + case dimmed + + /** + UIKitExtension: The button is disabled. Controlled through the `isDisabled` property. + */ + case disabled + } +} +#endif diff --git a/Sources/UIKitExtension/Controls/Buttons/UILargeActionButton.swift b/Sources/UIKitExtension/Controls/Buttons/UILargeActionButton.swift new file mode 100644 index 0000000..c5a1cf7 --- /dev/null +++ b/Sources/UIKitExtension/Controls/Buttons/UILargeActionButton.swift @@ -0,0 +1,80 @@ +#if canImport(UIKit) && (os(iOS)) +import UIKit + +/** + UIKitExtension: Large action button. + Usually using at bottom of screen. + */ +open class UILargeActionButton: UIDimmedButton { + + // MARK: - Init + + open override func commonInit() { + super.commonInit() + insetsLayoutMarginsFromSafeArea = false + preservesSuperviewLayoutMargins = false + titleLabel?.font = UIFont.preferredFont(forTextStyle: .headline, addPoints: 1) + titleLabel?.numberOfLines = 1 + titleImageInset = 6 + contentEdgeInsets = .init(horizontal: 10, vertical: 12) + roundCorners(radius: Appearance.Corners.readable_area) + } + + // MARK: - Layout + + /** + NativeUIKit: Layout wrapper. Native way for layout button. + */ + open func layout(y: CGFloat) { + guard let superview = self.superview else { return } + sizeToFit() + let width = superview.readableWidth + frame.setWidth(width) + setXCenter() + frame.origin.y = y + } + + /** + NativeUIKit: Layout wrapper. Native way for layout button. + */ + open func layout(maxY: CGFloat) { + guard let superview = self.superview else { return } + sizeToFit() + let width = superview.readableWidth + frame.setWidth(width) + setXCenter() + frame.setMaxY(maxY) + } + + open override func sizeThatFits(_ size: CGSize) -> CGSize { + let superSize = super.sizeThatFits(size) + let width = superSize.width + + // When set image, height may change. + // It's allow save height of button. + var height = superSize.height + if let titleLabel = titleLabel, let imageView = imageView, let _ = imageView.image { + if titleLabel.frame.height > .zero && imageView.frame.height > .zero { + let imageCorrection = imageView.frame.height - titleLabel.frame.height + height -= imageCorrection + } + } + + return CGSize(width: width, height: height) + } + + // MARK: - Wrappers + + public static var defaultCornerRadius: CGFloat { + let button = UILargeActionButton() + return button.layer.cornerRadius + } + + public static var defaultHeight: CGFloat { + let button = UILargeActionButton() + button.setTitle(.space) + button.sizeToFit() + return button.frame.height + } +} +#endif diff --git a/Sources/UIKitExtension/Layout.swift b/Sources/UIKitExtension/Layout.swift new file mode 100644 index 0000000..6ec1098 --- /dev/null +++ b/Sources/UIKitExtension/Layout.swift @@ -0,0 +1,24 @@ +#if canImport(UIKit) && (os(iOS)) +import UIKit + +public enum Layout { + + public enum Spaces { + + public static var step: CGFloat = 4 + + public static var default_less: CGFloat { step * 3 } + public static var `default`: CGFloat { step * 4 } + public static var default_more: CGFloat { step * 5 } + + public static var default_half: CGFloat { self.default / 2 } + public static var default_double: CGFloat { self.default * 2 } + + public enum Margins { + + public static var full_screen_horizontal: CGFloat { Spaces.default } + public static var modal_screen_horizontal: CGFloat { step * 5 } + } + } +} +#endif From 3e47d92637de1b73e4bcaf89520f89092ad166e3 Mon Sep 17 00:00:00 2001 From: Ivan Vorobei Date: Mon, 15 Aug 2022 14:40:31 +0300 Subject: [PATCH 09/16] Added large header and empty areas. --- Sources/UIKitExtension/CommonInit.swift | 15 +++ .../UIKitExtension/Labels/UIFooterView.swift | 47 +++++++ Sources/UIKitExtension/Layout.swift | 33 +++-- .../Table/UIEmptyTableViewCell.swift | 106 +++++++++++++++ ...rTableController+HeaderContainerView.swift | 0 .../Table/UIHeaderTableController.swift | 0 .../Table/UILargeHeaderTableView.swift | 59 +++++++++ .../Views/UILargeHeaderView.swift | 63 +++++++++ .../Views/UIPlaceholderView.swift | 123 ++++++++++++++++++ 9 files changed, 428 insertions(+), 18 deletions(-) create mode 100644 Sources/UIKitExtension/Labels/UIFooterView.swift create mode 100644 Sources/UIKitExtension/Table/UIEmptyTableViewCell.swift rename Sources/UIKitExtension/{Controllers => }/Table/UIHeaderTableController+HeaderContainerView.swift (100%) rename Sources/UIKitExtension/{Controllers => }/Table/UIHeaderTableController.swift (100%) create mode 100644 Sources/UIKitExtension/Table/UILargeHeaderTableView.swift create mode 100644 Sources/UIKitExtension/Views/UILargeHeaderView.swift create mode 100644 Sources/UIKitExtension/Views/UIPlaceholderView.swift diff --git a/Sources/UIKitExtension/CommonInit.swift b/Sources/UIKitExtension/CommonInit.swift index 39c38eb..641bbd2 100644 --- a/Sources/UIKitExtension/CommonInit.swift +++ b/Sources/UIKitExtension/CommonInit.swift @@ -250,4 +250,19 @@ open class UICommonTableViewCell: UITableViewCell, UICommonInit { open func commonInit() {} } + +open class UICommonTableViewHeaderFooterView: UITableViewHeaderFooterView { + + public override init(reuseIdentifier: String?) { + super.init(reuseIdentifier: reuseIdentifier) + commonInit() + } + + public required init?(coder: NSCoder) { + super.init(coder: coder) + commonInit() + } + + open func commonInit() {} +} #endif diff --git a/Sources/UIKitExtension/Labels/UIFooterView.swift b/Sources/UIKitExtension/Labels/UIFooterView.swift new file mode 100644 index 0000000..de0b6b6 --- /dev/null +++ b/Sources/UIKitExtension/Labels/UIFooterView.swift @@ -0,0 +1,47 @@ +#if canImport(UIKit) && (os(iOS)) +import UIKit + +open class UIFooterView: UICommonView { + + // MARK: - Views + + public let label = UICommonLabel().do { + $0.font = .preferredFont(forTextStyle: .footnote) + $0.numberOfLines = .zero + if #available(iOS 13.0, *) { + $0.textColor = .secondaryLabel + } else { + $0.textColor = .gray + } + } + + public override init() { + super.init() + } + + public init(text: String) { + super.init() + label.text = text + } + + required public init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + open override func commonInit() { + super.commonInit() + layoutMargins = .init(top: Spaces.default_half, left: 16, bottom: Spaces.default_half, right: .zero) + addSubview(label) + } + + open override func layoutSubviews() { + super.layoutSubviews() + label.layoutDynamicHeight(x: layoutMargins.left, y: layoutMargins.top, width: layoutWidth) + } + + open override func sizeThatFits(_ size: CGSize) -> CGSize { + layoutSubviews() + return .init(width: size.width, height: label.frame.maxY + layoutMargins.bottom) + } +} +#endif diff --git a/Sources/UIKitExtension/Layout.swift b/Sources/UIKitExtension/Layout.swift index 6ec1098..fe69e62 100644 --- a/Sources/UIKitExtension/Layout.swift +++ b/Sources/UIKitExtension/Layout.swift @@ -1,24 +1,21 @@ #if canImport(UIKit) && (os(iOS)) import UIKit -public enum Layout { +public enum Spaces { - public enum Spaces { - - public static var step: CGFloat = 4 - - public static var default_less: CGFloat { step * 3 } - public static var `default`: CGFloat { step * 4 } - public static var default_more: CGFloat { step * 5 } - - public static var default_half: CGFloat { self.default / 2 } - public static var default_double: CGFloat { self.default * 2 } - - public enum Margins { - - public static var full_screen_horizontal: CGFloat { Spaces.default } - public static var modal_screen_horizontal: CGFloat { step * 5 } - } - } + public static var step: CGFloat = 4 + + public static var default_less: CGFloat { step * 3 } + public static var `default`: CGFloat { step * 4 } + public static var default_more: CGFloat { step * 5 } + + public static var default_half: CGFloat { self.default / 2 } + public static var default_double: CGFloat { self.default * 2 } +} + +public enum Margins { + + public static var full_screen_horizontal: CGFloat { Spaces.default } + public static var modal_screen_horizontal: CGFloat { Spaces.step * 5 } } #endif diff --git a/Sources/UIKitExtension/Table/UIEmptyTableViewCell.swift b/Sources/UIKitExtension/Table/UIEmptyTableViewCell.swift new file mode 100644 index 0000000..f1136b5 --- /dev/null +++ b/Sources/UIKitExtension/Table/UIEmptyTableViewCell.swift @@ -0,0 +1,106 @@ +#if canImport(UIKit) && (os(iOS)) +import UIKit +import DiffableKit + +open class UIEmptyTableViewCell: UICommonTableViewCell { + + // MARK: - Data + + open var verticalMargins: Margins = .medium { + didSet { + contentView.layoutMargins.top = self.verticalMargins.value + contentView.layoutMargins.bottom = self.verticalMargins.value + } + } + + // MARK: - Views + + public let placeholderView = UIPlaceholderView().do { + $0.isUserInteractionEnabled = false + } + + // MARK: - Init + + open override func commonInit() { + super.commonInit() + selectionStyle = .none + contentView.addSubview(placeholderView) + updateBackgroundColorByParent() + } + + // MARK: - Lifecycle + + open override func tintColorDidChange() { + super.tintColorDidChange() + updateBackgroundColorByParent() + } + + // MARK: - Layout + + open override func layoutSubviews() { + super.layoutSubviews() + placeholderView.frame.origin.y = contentView.layoutMargins.top + placeholderView.setWidthAndFit(width: contentView.layoutWidth) + placeholderView.setXCenter() + } + + open override func sizeThatFits(_ size: CGSize) -> CGSize { + let superSize = super.sizeThatFits(size) + layoutSubviews() + let calculatedHeight = placeholderView.frame.maxY + contentView.layoutMargins.bottom + let bottomInset = calculatedHeight * (1 - 0.94) + return .init(width: superSize.width, height: calculatedHeight + bottomInset) + } + + // MARK: - Actions + + private func updateBackgroundColorByParent() { + if #available(iOS 13.0, *) { + backgroundColor = UIColor.systemDownedGroupedBackground + } + } + + // MARK: - Models + + public enum Margins { + + case small + case medium + case large + + var value: CGFloat { + switch self { + case .small: return Spaces.default + case .medium: return Spaces.default * 2 + case .large: return Spaces.default * 3 + } + } + } +} + +// MARK: - Diffable + +open class DiffableEmptyRowItem: DiffableTableRow { + + var verticalMargins: UIEmptyTableViewCell.Margins + + public init(id: String, verticalMargins: UIEmptyTableViewCell.Margins, text: String, detail: String?) { + self.verticalMargins = verticalMargins + super.init(id: id, text: text, detail: detail) + } +} + +extension DiffableTableDataSource.CellProvider { + + public static var empty: DiffableTableDataSource.CellProvider { + return DiffableTableDataSource.CellProvider() { (tableView, indexPath, item) -> UITableViewCell? in + guard let item = item as? DiffableEmptyRowItem else { return nil } + let cell = tableView.dequeueReusableCell(withClass: UIEmptyTableViewCell.self, for: indexPath) + cell.placeholderView.headerLabel.text = item.text + cell.placeholderView.descriptionLabel.text = item.detail + cell.verticalMargins = item.verticalMargins + return cell + } + } +} +#endif diff --git a/Sources/UIKitExtension/Controllers/Table/UIHeaderTableController+HeaderContainerView.swift b/Sources/UIKitExtension/Table/UIHeaderTableController+HeaderContainerView.swift similarity index 100% rename from Sources/UIKitExtension/Controllers/Table/UIHeaderTableController+HeaderContainerView.swift rename to Sources/UIKitExtension/Table/UIHeaderTableController+HeaderContainerView.swift diff --git a/Sources/UIKitExtension/Controllers/Table/UIHeaderTableController.swift b/Sources/UIKitExtension/Table/UIHeaderTableController.swift similarity index 100% rename from Sources/UIKitExtension/Controllers/Table/UIHeaderTableController.swift rename to Sources/UIKitExtension/Table/UIHeaderTableController.swift diff --git a/Sources/UIKitExtension/Table/UILargeHeaderTableView.swift b/Sources/UIKitExtension/Table/UILargeHeaderTableView.swift new file mode 100644 index 0000000..da0be5b --- /dev/null +++ b/Sources/UIKitExtension/Table/UILargeHeaderTableView.swift @@ -0,0 +1,59 @@ +#if canImport(UIKit) && (os(iOS)) +import UIKit +import DiffableKit + +open class UILargeHeaderTableView: UICommonTableViewHeaderFooterView { + + public let headerView = UILargeHeaderView() + + open override func commonInit() { + super.commonInit() + insetsLayoutMarginsFromSafeArea = false + contentView.layoutMargins = .zero + contentView.addSubview(headerView) + } + + open override func layoutSubviews() { + super.layoutSubviews() + headerView.setWidthAndFit(width: contentView.layoutWidth) + headerView.frame.origin = .init(x: contentView.layoutMargins.left, y: layoutMargins.top) + } + + open override func sizeThatFits(_ size: CGSize) -> CGSize { + let superSize = super.sizeThatFits(size) + return .init(width: superSize.width, height: headerView.frame.height + layoutMargins.bottom) + } +} + +// MARK: - Diffable + +open class DiffableLargeHeaderItem: DiffableActionableItem { + + open var title: String + open var actionTitle: String? + + public init(id: String? = nil, title: String, actionTitle: String? = nil, action: Action? = nil) { + self.title = title + self.actionTitle = actionTitle + super.init(id: id ?? title, action: action) + } +} + +extension DiffableTableDataSource.HeaderFooterProvider { + + public static var largeHeader: DiffableTableDataSource.HeaderFooterProvider { + return DiffableTableDataSource.HeaderFooterProvider() { (tableView, section, item) -> UIView? in + guard let header = item as? DiffableLargeHeaderItem else { return nil } + let view = tableView.dequeueReusableHeaderFooterView(withClass: UILargeHeaderTableView.self) + view.headerView.titleLabel.text = header.title + if let actionTitle = header.actionTitle { + view.headerView.button.setTitle(actionTitle) + view.headerView.diffableButtonAction = { + header.action?(item, .init(row: .zero, section: section)) + } + } + return view + } + } +} +#endif diff --git a/Sources/UIKitExtension/Views/UILargeHeaderView.swift b/Sources/UIKitExtension/Views/UILargeHeaderView.swift new file mode 100644 index 0000000..84fd2ae --- /dev/null +++ b/Sources/UIKitExtension/Views/UILargeHeaderView.swift @@ -0,0 +1,63 @@ +#if canImport(UIKit) && (os(iOS)) +import UIKit + +open class UILargeHeaderView: UICommonView { + + // MARK: - Views + + public let titleLabel = UICommonLabel().do { + $0.font = UIFont.preferredFont(forTextStyle: .title2, weight: .semibold) + $0.textColor = .label + } + + public let button = UIDimmedButton().do { + $0.applyDefaultAppearance(with: .tintedContent) + } + + // MARK: - Init + + open override func commonInit() { + super.commonInit() + layoutMargins = .init(top: Spaces.default_double, left: .zero, bottom: 14, right: .zero) + insetsLayoutMarginsFromSafeArea = false + preservesSuperviewLayoutMargins = false + addSubviews(titleLabel, button) + } + + // MARK: - Layout + + open override func layoutSubviews() { + super.layoutSubviews() + titleLabel.layoutDynamicHeight( + x: layoutMargins.left, + y: layoutMargins.top, + width: layoutWidth + ) + button.sizeToFit() + button.setMaxXToSuperviewRightMargin() + button.center.y = titleLabel.center.y + } + + open override func sizeThatFits(_ size: CGSize) -> CGSize { + let superSize = super.sizeThatFits(size) + layoutSubviews() + return .init(width: superSize.width, height: titleLabel.frame.maxY + layoutMargins.bottom) + } + + // MARK: - Private + + var diffableButtonAction: (()->Void)? = nil { + didSet { + if let _ = diffableButtonAction { + self.button.addTarget(self, action: #selector(self.diffableButtonTargetAction), for: .touchUpInside) + } else { + self.button.removeTargetsAndActions() + } + } + } + + @objc func diffableButtonTargetAction() { + self.diffableButtonAction?() + } +} +#endif diff --git a/Sources/UIKitExtension/Views/UIPlaceholderView.swift b/Sources/UIKitExtension/Views/UIPlaceholderView.swift new file mode 100644 index 0000000..4983fa8 --- /dev/null +++ b/Sources/UIKitExtension/Views/UIPlaceholderView.swift @@ -0,0 +1,123 @@ +#if canImport(UIKit) && (os(iOS)) +import UIKit + +open class UIPlaceholderView: UICommonButton { + + // MARK: - Views + + public let iconImageView = UICommonImageView().do { + $0.contentMode = .scaleAspectFit + } + + public let headerLabel = UICommonLabel().do { + $0.numberOfLines = .zero + $0.textAlignment = .center + $0.font = UIFont.preferredFont(forTextStyle: .title1, weight: .bold) + } + + public let descriptionLabel = UICommonLabel().do { + $0.numberOfLines = .zero + $0.textAlignment = .center + $0.font = UIFont.preferredFont(forTextStyle: .body, weight: .regular) + } + + // MARK: - Init + + public override init() { + super.init() + } + + public init(icon: UIImage, title: String, subtitle: String) { + super.init() + iconImageView.image = icon.alwaysTemplate + headerLabel.text = title + descriptionLabel.text = subtitle + } + + public required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + open override func commonInit() { + super.commonInit() + layoutMargins = .zero + tintColor = .tertiaryLabel + backgroundColor = .clear + iconImageView.tintColor = tintColor + headerLabel.textColor = tintColor + descriptionLabel.textColor = tintColor + addSubviews(iconImageView, headerLabel, descriptionLabel) + } + + // MARK: - Ovveride + + open override var isHighlighted: Bool { + didSet { + UIView.animate(withDuration: 0.1, delay: 0, options: [.beginFromCurrentState, .allowUserInteraction], animations: { + self.alpha = self.isHighlighted ? 0.5 : 1 + }, completion: nil) + } + } + + // MARK: - Actions + + open func setVisible(_ state: Bool, animated: Bool) { + let work = { [weak self] in + guard let self = self else { return } + self.alpha = state ? 1 : .zero + } + if animated { + UIView.animate(withDuration: 0.45, delay: .zero, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: [.allowUserInteraction, .beginFromCurrentState], animations: { + work() + }, completion: nil) + } else { + work() + } + } + + // MARK: - Layout + + open func layout(y: CGFloat) { + guard let superview = self.superview else { return } + sizeToFit() + let width = superview.readableWidth + frame.setWidth(width) + setXCenter() + frame.origin.y = y + } + + open func layoutCenter() { + guard let superview = self.superview else { return } + let width = superview.readableWidth + setWidthAndFit(width: width) + setXCenter() + switch superview { + case _ as UICollectionView: + center.y = (superview.layoutHeight / 2) * 0.94 + case _ as UITableView: + center.y = (superview.layoutHeight / 2) * 0.94 + default: + center.y = (superview.frame.height / 2) * 0.94 + } + + } + + open override func layoutSubviews() { + super.layoutSubviews() + iconImageView.sizeToFit() + iconImageView.setXCenter() + iconImageView.frame.origin.y = layoutMargins.top + headerLabel.layoutDynamicHeight(width: layoutWidth) + headerLabel.frame.origin.y = iconImageView.frame.maxY + 12 + headerLabel.setXCenter() + descriptionLabel.layoutDynamicHeight(width: layoutWidth) + descriptionLabel.frame.origin.y = headerLabel.frame.maxY + 4 + descriptionLabel.setXCenter() + } + + open override func sizeThatFits(_ size: CGSize) -> CGSize { + layoutSubviews() + return .init(width: size.width, height: descriptionLabel.frame.maxY + layoutMargins.bottom) + } +} +#endif From 915ed2e39b2780e4961a8742f5bd857e5ea8b4ae Mon Sep 17 00:00:00 2001 From: Ivan Vorobei Date: Thu, 13 Apr 2023 19:54:11 +0300 Subject: [PATCH 10/16] Updated diffable extension and avatar. --- .../UILargeHeaderCollectionView.swift | 62 +++ .../UIPagingCollectionViewLayout.swift | 48 ++ Sources/UIKitExtension/CommonInit.swift | 15 + .../UICommonCollectionViewController.swift | 23 + .../UICommonNavigationController.swift | 10 +- .../UICommonScrollViewController.swift | 21 + .../Controllers/UICommonViewContoller.swift | 28 ++ .../UIPageContainerController.swift | 76 ++++ .../UIPageControllerInterface.swift | 8 + .../UIPageScrollSystemController.swift | 115 +++++ .../UIProfileNavigationController.swift | 90 ++++ .../Buttons/UILargeActionButton.swift | 11 +- .../Controls/UIAvatarControl.swift | 425 ++++++++++++++++++ .../Diffable/DiffableLargeHeaderItem.swift | 14 + .../Extensions/UIScreenExtension.swift | 11 + .../UIViewControllerExtension.swift | 18 - .../UIKitExtension/SettingIconGenerator.swift | 41 ++ ...rTableController+HeaderContainerView.swift | 2 +- .../Table/UIHeaderTableController.swift | 59 ++- .../Table/UILargeHeaderTableView.swift | 33 +- .../UIKitExtension/Views/UIBorderedView.swift | 83 ++++ .../UIKitExtension/Views/UIGradientView.swift | 100 +++++ .../Views/UILargeHeaderView.swift | 68 ++- 23 files changed, 1278 insertions(+), 83 deletions(-) create mode 100644 Sources/UIKitExtension/Collection/UILargeHeaderCollectionView.swift create mode 100644 Sources/UIKitExtension/Collection/UIPagingCollectionViewLayout.swift create mode 100644 Sources/UIKitExtension/Controllers/UICommonCollectionViewController.swift create mode 100644 Sources/UIKitExtension/Controllers/UICommonScrollViewController.swift create mode 100644 Sources/UIKitExtension/Controllers/UICommonViewContoller.swift create mode 100644 Sources/UIKitExtension/Controllers/UIPageContainerController/UIPageContainerController.swift create mode 100644 Sources/UIKitExtension/Controllers/UIPageContainerController/UIPageControllerInterface.swift create mode 100644 Sources/UIKitExtension/Controllers/UIPageContainerController/UIPageScrollSystemController.swift create mode 100644 Sources/UIKitExtension/Controllers/UIProfileNavigationController.swift create mode 100644 Sources/UIKitExtension/Controls/UIAvatarControl.swift create mode 100644 Sources/UIKitExtension/Diffable/DiffableLargeHeaderItem.swift create mode 100644 Sources/UIKitExtension/Extensions/UIScreenExtension.swift delete mode 100644 Sources/UIKitExtension/Extensions/UIViewControllerExtension.swift create mode 100644 Sources/UIKitExtension/SettingIconGenerator.swift create mode 100644 Sources/UIKitExtension/Views/UIBorderedView.swift create mode 100644 Sources/UIKitExtension/Views/UIGradientView.swift diff --git a/Sources/UIKitExtension/Collection/UILargeHeaderCollectionView.swift b/Sources/UIKitExtension/Collection/UILargeHeaderCollectionView.swift new file mode 100644 index 0000000..bb9c9f7 --- /dev/null +++ b/Sources/UIKitExtension/Collection/UILargeHeaderCollectionView.swift @@ -0,0 +1,62 @@ +#if canImport(UIKit) && (os(iOS)) +import UIKit +import DiffableKit +import SwiftBoost + +open class UILargeHeaderCollectionView: UICommonCollectionReusableView { + + public let headerView = UILargeHeaderView() + + open override func commonInit() { + super.commonInit() + layoutMargins = .zero + layoutMargins.left = Spaces.default_half + preservesSuperviewLayoutMargins = false + insetsLayoutMarginsFromSafeArea = false + addSubview(headerView) + } + + open override func prepareForReuse() { + super.prepareForReuse() + headerView.button.removeTargetsAndActions() + } + + open override func layoutSubviews() { + super.layoutSubviews() + headerView.setWidthAndFit(width: layoutWidth) + headerView.frame.origin = .init(x: layoutMargins.left, y: layoutMargins.top) + } + + open override func sizeThatFits(_ size: CGSize) -> CGSize { + let superSize = super.sizeThatFits(size) + layoutSubviews() + return .init(width: superSize.width, height: headerView.frame.maxY + layoutMargins.bottom) + } +} + +extension DiffableCollectionDataSource.HeaderFooterProvider { + + public static var largeHeader: DiffableCollectionDataSource.HeaderFooterProvider { + return DiffableCollectionDataSource.HeaderFooterProvider() { (collectionView, section, item) -> UICollectionReusableView? in + guard let header = item as? DiffableLargeHeaderItem else { return nil } + let view = collectionView.dequeueReusableSupplementaryView(withCalss: UILargeHeaderCollectionView.self, kind: UICollectionView.elementKindSectionHeader, for: .init(row: .zero, section: section)) + view.headerView.button.setTitle(header.title) + if #available(iOS 14.0, *) { + if let action = header.action { + view.headerView.showChevron = true + view.headerView.button.addAction(.init(handler: { _ in + action(item, .init(row: .zero, section: section)) + }), for: .touchUpInside) + } else { + view.headerView.showChevron = false + } + } else { + #warning("add for ios 13") + view.headerView.showChevron = false + } + view.layoutSubviews() + return view + } + } +} +#endif diff --git a/Sources/UIKitExtension/Collection/UIPagingCollectionViewLayout.swift b/Sources/UIKitExtension/Collection/UIPagingCollectionViewLayout.swift new file mode 100644 index 0000000..0e68d65 --- /dev/null +++ b/Sources/UIKitExtension/Collection/UIPagingCollectionViewLayout.swift @@ -0,0 +1,48 @@ +import UIKit + +/** + - warning: Sometimes scroll working not well. Recommended to use composition layout. + */ +open class UIPagingCollectionViewFlowLayout: UICollectionViewFlowLayout { + + public enum Behavior { + + case soft + case aggresive + } + + open var behavior: Behavior = .soft + + open override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint { + + switch behavior { + case .soft: + guard let collectionView = self.collectionView else { + let latestOffset = super.targetContentOffset(forProposedContentOffset: proposedContentOffset, withScrollingVelocity: velocity) + return latestOffset + } + var offsetAdjustment = CGFloat.greatestFiniteMagnitude + let horizontalOffset = proposedContentOffset.x + let targetRect = CGRect(origin: CGPoint(x: proposedContentOffset.x, y: 0), size: collectionView.bounds.size) + for layoutAttributes in super.layoutAttributesForElements(in: targetRect)! { + let itemOffset = layoutAttributes.frame.origin.x + if (abs(itemOffset - horizontalOffset) < abs(offsetAdjustment)) { + offsetAdjustment = itemOffset - horizontalOffset + } + } + return CGPoint(x: proposedContentOffset.x + offsetAdjustment - collectionView.layoutMargins.left, y: proposedContentOffset.y) + case .aggresive: + guard let collectionView = self.collectionView else { + let latestOffset = super.targetContentOffset(forProposedContentOffset: proposedContentOffset, withScrollingVelocity: velocity) + return latestOffset + } + let pageWidth = self.itemSize.width + self.minimumLineSpacing + let approximatePage = collectionView.contentOffset.x / pageWidth + let currentPage = velocity.x == 0 ? round(approximatePage) : (velocity.x < 0.0 ? floor(approximatePage) : ceil(approximatePage)) + let flickVelocity = velocity.x * 0.3 + let flickedPages = (abs(round(flickVelocity)) <= 1) ? 0 : round(flickVelocity) + let newHorizontalOffset = ((currentPage + flickedPages) * pageWidth) - collectionView.contentInset.left + return CGPoint(x: newHorizontalOffset, y: proposedContentOffset.y) + } + } +} diff --git a/Sources/UIKitExtension/CommonInit.swift b/Sources/UIKitExtension/CommonInit.swift index 641bbd2..eb1dcbb 100644 --- a/Sources/UIKitExtension/CommonInit.swift +++ b/Sources/UIKitExtension/CommonInit.swift @@ -265,4 +265,19 @@ open class UICommonTableViewHeaderFooterView: UITableViewHeaderFooterView { open func commonInit() {} } + +open class UICommonCollectionReusableView: UICollectionReusableView { + + public override init(frame: CGRect) { + super.init(frame: frame) + commonInit() + } + + public required init?(coder: NSCoder) { + super.init(coder: coder) + commonInit() + } + + open func commonInit() {} +} #endif diff --git a/Sources/UIKitExtension/Controllers/UICommonCollectionViewController.swift b/Sources/UIKitExtension/Controllers/UICommonCollectionViewController.swift new file mode 100644 index 0000000..f970d93 --- /dev/null +++ b/Sources/UIKitExtension/Controllers/UICommonCollectionViewController.swift @@ -0,0 +1,23 @@ +import UIKit + +open class UICommonCollectionViewContoller: UICollectionViewController, UICommonInit { + + // MARK: - Init + + public convenience init() { + self.init(collectionViewLayout: UICollectionViewFlowLayout()) + commonInit() + } + + public override init(collectionViewLayout layout: UICollectionViewLayout) { + super.init(collectionViewLayout: layout) + commonInit() + } + + public required init?(coder: NSCoder) { + super.init(coder: coder) + commonInit() + } + + open func commonInit() {} +} diff --git a/Sources/UIKitExtension/Controllers/UICommonNavigationController.swift b/Sources/UIKitExtension/Controllers/UICommonNavigationController.swift index a0770f5..9392a00 100644 --- a/Sources/UIKitExtension/Controllers/UICommonNavigationController.swift +++ b/Sources/UIKitExtension/Controllers/UICommonNavigationController.swift @@ -1,6 +1,6 @@ import UIKit -open class UICommonNavigationController: UINavigationController, UICommonInit { +open class UICommonNavigationController: UINavigationController, UICommonInit, UINavigationControllerDelegate { // MARK: - Init @@ -22,7 +22,9 @@ open class UICommonNavigationController: UINavigationController, UICommonInit { super.init(navigationBarClass: navigationBarClass, toolbarClass: toolbarClass) } - open func commonInit() {} + open func commonInit() { + delegate = self + } // MARK: - Layout @@ -46,7 +48,7 @@ open class UICommonNavigationController: UINavigationController, UICommonInit { if inheritLayoutMarginsForNavigationBar { let leftMargin = view.layoutMargins.left - let rightMargin = view.layoutMargins.left + let rightMargin = view.layoutMargins.right if navigationBar.layoutMargins.left != leftMargin { navigationBar.layoutMargins.left = leftMargin @@ -61,7 +63,7 @@ open class UICommonNavigationController: UINavigationController, UICommonInit { if inheritLayoutMarginsForСhilds { let leftMargin = view.layoutMargins.left - let rightMargin = view.layoutMargins.left + let rightMargin = view.layoutMargins.right for childController in viewControllers { if childController.view.layoutMargins.left != leftMargin { diff --git a/Sources/UIKitExtension/Controllers/UICommonScrollViewController.swift b/Sources/UIKitExtension/Controllers/UICommonScrollViewController.swift new file mode 100644 index 0000000..3826baf --- /dev/null +++ b/Sources/UIKitExtension/Controllers/UICommonScrollViewController.swift @@ -0,0 +1,21 @@ +import UIKit + +open class UICommonScrollViewController: UICommonViewContoller { + + public lazy var scrollView = UIScrollView() + + open override func viewDidLoad() { + super.viewDidLoad() + + scrollView.delaysContentTouches = false + view.addSubview(scrollView) + + scrollView.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + scrollView.topAnchor.constraint(equalTo: view.topAnchor), + scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor), + scrollView.leftAnchor.constraint(equalTo: view.leftAnchor), + scrollView.rightAnchor.constraint(equalTo: view.rightAnchor) + ]) + } +} diff --git a/Sources/UIKitExtension/Controllers/UICommonViewContoller.swift b/Sources/UIKitExtension/Controllers/UICommonViewContoller.swift new file mode 100644 index 0000000..14bbedf --- /dev/null +++ b/Sources/UIKitExtension/Controllers/UICommonViewContoller.swift @@ -0,0 +1,28 @@ +import UIKit + +open class UICommonViewContoller: UIViewController, UICommonInit { + + // MARK: - Init + + public init() { + super.init(nibName: nil, bundle: nil) + commonInit() + } + + public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { + super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) + commonInit() + } + + public required init?(coder: NSCoder) { + super.init(coder: coder) + commonInit() + } + + open func commonInit() {} + + // MARK: - Toolbar + + //open var extendedToolbarContainerView: UIToolbarContainerView.ContainerView? = nil + +} diff --git a/Sources/UIKitExtension/Controllers/UIPageContainerController/UIPageContainerController.swift b/Sources/UIKitExtension/Controllers/UIPageContainerController/UIPageContainerController.swift new file mode 100644 index 0000000..7699d40 --- /dev/null +++ b/Sources/UIKitExtension/Controllers/UIPageContainerController/UIPageContainerController.swift @@ -0,0 +1,76 @@ +import UIKit + +open class UIPageContainerController: UIViewController { + + public var containerController: UIViewController + + // todo clean to + + public func scrollToPage(index: Int) { + if let pageController = containerController as? UIPageScrollSystemController { + pageController.safeScrollTo(index: index, animated: true) + } + } + + public init(data: UIPageContainerControllerDataSystem, scrollSystem: UIPageContainerControllerScrollSystem) { + switch data { + case .dataSource(let dataSource): + switch scrollSystem { + case .page: + containerController = UIPageScrollSystemController(dataSource: dataSource) + case .scroll: + fatalError() + } + case .array(let viewControllers): + switch scrollSystem { + case .page: + containerController = UIPageScrollSystemController(viewControllers: viewControllers) + case .scroll: + fatalError() + } + } + super.init(nibName: nil, bundle: nil) + } + + public required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - Lifecycle + + open override func viewDidLoad() { + super.viewDidLoad() + //presentationController?.delegate = self + + containerController.view.backgroundColor = .clear + addChild(containerController) + view.addSubview(containerController.view) + containerController.didMove(toParent: self) + + containerController.view.preservesSuperviewLayoutMargins = true + containerController.view.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + containerController.view.topAnchor.constraint(equalTo: view.topAnchor), + containerController.view.leftAnchor.constraint(equalTo: view.leftAnchor), + containerController.view.rightAnchor.constraint(equalTo: view.rightAnchor), + containerController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor) + ]) + } +} + +public enum UIPageContainerControllerScrollSystem { + + case page + case scroll +} + +public protocol UIPageContainerControllerDataSource { + + func pageContainerViewController(for index: Int) -> UIViewController? +} + +public enum UIPageContainerControllerDataSystem { + + case dataSource(dataSource: UIPageContainerControllerDataSource) + case array(viewControllers: [UIViewController]) +} diff --git a/Sources/UIKitExtension/Controllers/UIPageContainerController/UIPageControllerInterface.swift b/Sources/UIKitExtension/Controllers/UIPageContainerController/UIPageControllerInterface.swift new file mode 100644 index 0000000..de624f1 --- /dev/null +++ b/Sources/UIKitExtension/Controllers/UIPageContainerController/UIPageControllerInterface.swift @@ -0,0 +1,8 @@ +import UIKit + +protocol UIPageContainerControllerInterface: AnyObject { + + var allowScroll: Bool { get set } + + func safeScrollTo(index: Int, animated: Bool) +} diff --git a/Sources/UIKitExtension/Controllers/UIPageContainerController/UIPageScrollSystemController.swift b/Sources/UIKitExtension/Controllers/UIPageContainerController/UIPageScrollSystemController.swift new file mode 100644 index 0000000..aa8fcf3 --- /dev/null +++ b/Sources/UIKitExtension/Controllers/UIPageContainerController/UIPageScrollSystemController.swift @@ -0,0 +1,115 @@ +import UIKit + +extension UIPageContainerController { + + class UIPageScrollSystemController: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate, UIPageContainerControllerInterface { + + private var childControllers: [UIViewController]? = nil + private var controllersDataSource: UIPageContainerControllerDataSource? + + // MARK: - Init + + init(dataSource: UIPageContainerControllerDataSource) { + self.controllersDataSource = dataSource + super.init(transitionStyle: .scroll, navigationOrientation: .horizontal, options: [:]) + guard let firstController = getViewController(for: .zero) else { return } + setViewControllers([firstController], direction: .forward, animated: false) + } + + init(viewControllers: [UIViewController]) { + self.childControllers = viewControllers + super.init(transitionStyle: .scroll, navigationOrientation: .horizontal, options: [:]) + guard let firstController = self.childControllers?.first else { return } + setViewControllers([firstController], direction: .forward, animated: false) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + dataSource = self + delegate = self + + view.layoutMargins = .zero + view.preservesSuperviewLayoutMargins = true + view.insetsLayoutMarginsFromSafeArea = false + + for view in view.subviews { + if let scrollView = view as? UIScrollView { + scrollView.delaysContentTouches = false + scrollView.preservesSuperviewLayoutMargins = true + } + } + + if #available(iOS 13.0, *) { + view.backgroundColor = .systemBackground + } + } + + // MARK: - Layout + + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + // todo add observing margins for controllers + /*let margins = view.layoutMargins + childControllers.forEach({ + if $0.view.preservesSuperviewLayoutMargins && $0.view.layoutMargins != margins { + $0.view.layoutMargins = margins + } + })*/ + } + + // MARK: - SPPageControllerInterface + + // todo complete + var allowScroll: Bool = false + + // todo complete + func safeScrollTo(index: Int, animated: Bool) { + guard let controller = getViewController(for: index) else { return } + setViewControllers([controller], direction: .forward, animated: true) + } + + // MARK: - UIPageViewControllerDataSource + + func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? { + if let childControllers = self.childControllers { + guard let index = childControllers.firstIndex(of: viewController) else { return nil } + let newIndex = index - 1 + if newIndex < 0 { return nil } + return childControllers[newIndex] + } else { + guard let indexAfter = (viewController as? UIPageContainerControllerIndexProtocol)?.pageControllerIndex else { fatalError() } + return getViewController(for: indexAfter - 1) + } + } + + func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? { + if let childControllers = self.childControllers { + guard let index = childControllers.firstIndex(of: viewController) else { return nil } + let newIndex = index + 1 + if newIndex > childControllers.count - 1 { return nil } + return childControllers[newIndex] + } else { + guard let indexBefore = (viewController as? UIPageContainerControllerIndexProtocol)?.pageControllerIndex else { fatalError() } + return getViewController(for: indexBefore + 1) + } + } + + // MARK: - Internal + + private func getViewController(for index: Int) -> UIViewController? { + guard let controller = controllersDataSource?.pageContainerViewController(for: index) else { return nil } + guard var indexController = controller as? UIPageContainerControllerIndexProtocol else { fatalError() } + indexController.pageControllerIndex = index + return indexController as? UIViewController + } + } +} + +public protocol UIPageContainerControllerIndexProtocol { + + var pageControllerIndex: Int? { get set } +} diff --git a/Sources/UIKitExtension/Controllers/UIProfileNavigationController.swift b/Sources/UIKitExtension/Controllers/UIProfileNavigationController.swift new file mode 100644 index 0000000..324eeee --- /dev/null +++ b/Sources/UIKitExtension/Controllers/UIProfileNavigationController.swift @@ -0,0 +1,90 @@ +import UIKit +import SwiftBoost + +open class UIProfileNavigationController: UICommonNavigationController { + + open lazy var avatarControl = UIAvatarControl() + + open lazy var navigationSubtitleLabel = UILabel().do { + $0.font = .preferredFont(forTextStyle: .footnote) + $0.textColor = .secondaryLabel + $0.numberOfLines = 1 + $0.text = nil + } + + // MARK: - Init + + open override func commonInit() { + super.commonInit() + } + + // MARK: - UINavigationControllerDelegate + + open func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) { + if !isShowProfile(for: viewController) { + removeAvatarControl() + } + } + + open func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) { + if isShowProfile(for: viewController) { + addAvatarControl() + } + } + + // MARK: - Profile + + private var profileAnimationDuration: TimeInterval { 0.12 } + + open func isShowProfile(for viewController: UIViewController) -> Bool { + return (viewController as? UIProfileNavigationControllerDelegate)?.isShowProfile ?? false + } + + internal func addAvatarControl() { + + let navBar = navigationBar + navBar.subviews.forEach { subview in + + let stringFromClass = NSStringFromClass(subview.classForCoder) +#if targetEnvironment(macCatalyst) + guard stringFromClass.contains("UINavigationBarContentView") else { return } +#else + guard stringFromClass.contains("UINavigationBarLargeTitleView") else { return } +#endif + + if avatarControl.superview != subview { + avatarControl.removeFromSuperview() + subview.addSubview(avatarControl) + } + + avatarControl.alpha = .zero + avatarControl.isUserInteractionEnabled = true + UIView.animate(withDuration: profileAnimationDuration, delay: .zero, options: [.beginFromCurrentState, .allowUserInteraction], animations: { + self.avatarControl.alpha = 1 + }) + + avatarControl.translatesAutoresizingMaskIntoConstraints = false + avatarControl.removeConstraints(avatarControl.constraints) + avatarControl.widthAnchor.constraint(equalToConstant: 36).isActive = true + avatarControl.heightAnchor.constraint(equalToConstant: 36).isActive = true + avatarControl.bottomAnchor.constraint(equalTo: avatarControl.superview!.bottomAnchor, constant: -10).isActive = true + avatarControl.trailingAnchor.constraint(equalTo: subview.safeAreaLayoutGuide.trailingAnchor, constant: -16).isActive = true + } + } + + internal func removeAvatarControl() { + self.navigationSubtitleLabel.removeFromSuperview() + UIView.animate(withDuration: profileAnimationDuration, delay: .zero, options: [.beginFromCurrentState, .allowUserInteraction, .curveEaseInOut], animations: { + self.avatarControl.alpha = .zero + }, completion: { finished in + if finished { + self.avatarControl.removeFromSuperview() + } + }) + } +} + +public protocol UIProfileNavigationControllerDelegate { + + var isShowProfile: Bool { get } +} diff --git a/Sources/UIKitExtension/Controls/Buttons/UILargeActionButton.swift b/Sources/UIKitExtension/Controls/Buttons/UILargeActionButton.swift index c5a1cf7..655dc48 100644 --- a/Sources/UIKitExtension/Controls/Buttons/UILargeActionButton.swift +++ b/Sources/UIKitExtension/Controls/Buttons/UILargeActionButton.swift @@ -17,7 +17,7 @@ open class UILargeActionButton: UIDimmedButton { titleLabel?.numberOfLines = 1 titleImageInset = 6 contentEdgeInsets = .init(horizontal: 10, vertical: 12) - roundCorners(radius: Appearance.Corners.readable_area) + roundCorners(curve: .continuous, radius: Appearance.Corners.readable_area) } // MARK: - Layout @@ -50,16 +50,19 @@ open class UILargeActionButton: UIDimmedButton { let superSize = super.sizeThatFits(size) let width = superSize.width + let height = superSize.height + /* Disabled image correction becouse + returned other values and its confused lauout system. + For now disabled. + // When set image, height may change. // It's allow save height of button. - var height = superSize.height if let titleLabel = titleLabel, let imageView = imageView, let _ = imageView.image { if titleLabel.frame.height > .zero && imageView.frame.height > .zero { let imageCorrection = imageView.frame.height - titleLabel.frame.height height -= imageCorrection } - } - + }*/ return CGSize(width: width, height: height) } diff --git a/Sources/UIKitExtension/Controls/UIAvatarControl.swift b/Sources/UIKitExtension/Controls/UIAvatarControl.swift new file mode 100644 index 0000000..1fac753 --- /dev/null +++ b/Sources/UIKitExtension/Controls/UIAvatarControl.swift @@ -0,0 +1,425 @@ + +#if canImport(UIKit) && (os(iOS)) +import UIKit + +/* + // edit buttons + open var indicatorAddImage = UIImage.system("plus", font: .preferredFont(forTextStyle: .title3, weight: .bold)) { + didSet { + updateEditAppearance() + } + } + + open var indicatorAddColorise = UIDimmedButton.Colorise.init(content: .white, background: .systemGreen) { + didSet { + updateEditAppearance() + } + } + + open var indicatorEditImage = UIImage.system("pencil", font: .preferredFont(forTextStyle: .title3, weight: .bold)) { + didSet { + updateEditAppearance() + } + } + + open var indicatorEditColorise = UIDimmedButton.Colorise.init(content: .white, background: .systemBlue) { + didSet { + updateEditAppearance() + } + } + */ + +open class UIAvatarControl: UICommonControl { + + // MARK: - Views + + public let activityIndicatorView = UIActivityIndicatorView().do { + $0.stopAnimating() + } + + public let avatarView = UICommonImageView().do { + $0.contentMode = .scaleAspectFill + $0.layer.masksToBounds = true + } + + public let placeholderView = UICommonImageView().do { + $0.image = UIImage.system("person.crop.circle.fill") + $0.contentMode = .scaleAspectFit + $0.tintColor = .init(light: .systemGray3, dark: .systemGray2) + } + + public let indicatorButton = UIDimmedButton().do { + $0.contentEdgeInsets = .init(side: 4) + } + + // MARK: - Public + + open var hasAvatar: Bool { + switch avatarAppearance { + case .avatar(_): return true + default: return false + } + } + + open var avatarAppearance: AvatarAppearance = .placeholder { + didSet { + updateAvatarAppearance() + updateEditAppearance() + } + } + + open var isEditable: Bool = false { + didSet { + updateEditAppearance() + } + } + + @objc func didTapAction(sender: UITapGestureRecognizer) { + self.sendActions(for: .touchUpInside) + } + + open override var isHighlighted: Bool { + didSet { + placeholderView.alpha = isHighlighted ? 0.8 : 1 + } + } + + // MARK: - Init + + open override func commonInit() { + super.commonInit() + layoutMargins = .zero + addSubview(placeholderView) + addSubview(avatarView) + addSubview(activityIndicatorView) + + updateAvatarAppearance() + updateEditAppearance() + + addGestureRecognizer(tapGestureRecognizer) + tapGestureRecognizer.addTarget(self, action: #selector(didTapAction(sender: ))) + + addSubview(indicatorButton) + } + + // MARK: - Layout + + open override func layoutSubviews() { + super.layoutSubviews() + + placeholderView.setEqualSuperviewMarginsWithFrames() + avatarView.setEqualSuperviewMarginsWithFrames() + + indicatorButton.sizeToFit() + indicatorButton.roundMinimumSide() + let sqrt2 = CGFloat(sqrt(2)) + if rtl { + let indicatorCenter = ((avatarView.frame.width * (sqrt2 + 1)) / (2 * sqrt2)) * 1.02 + indicatorButton.center = .init(x: frame.width - indicatorCenter, y: indicatorCenter) + } else { + let indicatorCenter = ((avatarView.frame.width * (sqrt2 + 1)) / (2 * sqrt2)) * 1.02 + indicatorButton.center = .init(x: indicatorCenter, y: indicatorCenter) + } + + activityIndicatorView.setToCenter() + } + + // MARK: - Internal + + let tapGestureRecognizer = UITapGestureRecognizer() + + internal func updateAvatarAppearance() { + switch avatarAppearance { + case .loading: + activityIndicatorView.startAnimating() + avatarView.isHidden = true + placeholderView.isHidden = true + case .placeholder: + activityIndicatorView.stopAnimating() + avatarView.isHidden = true + placeholderView.isHidden = false + case .avatar(let image): + activityIndicatorView.stopAnimating() + avatarView.isHidden = false + avatarView.image = image + placeholderView.isHidden = true + } + } + + internal func updateEditAppearance() { + if isEditable { + switch avatarAppearance { + case .loading: + indicatorButton.isHidden = true + case .placeholder: + indicatorButton.isHidden = false + //indicatorButton.setImage(indicatorAddImage) + //indicatorButton.applyDefaultAppearance(with: indicatorAddColorise) + case .avatar(_): + indicatorButton.isHidden = false + //indicatorButton.setImage(indicatorEditImage) + //indicatorButton.applyDefaultAppearance(with: indicatorEditColorise) + } + } else { + indicatorButton.isHidden = true + } + + [placeholderView, avatarView, indicatorButton].forEach({ $0.isUserInteractionEnabled = isEditable }) + } + + // MARK: - Models + + public enum AvatarAppearance { + + case loading + case placeholder + case avatar(_ image: UIImage) + } + + public enum EditAppearance { + + case none + case edit + case add + } +} + +/* +/** + NativeUIKit: Native avatar view. + + Using SFSymbols for content. + You shoud update avatar by set `avatarAppearance` to valid state. + For change colors or content check images and colorises properties. + For change show edit button set `isEditable` to true. + */ +#warning("todo redo it") +@available(iOS 13, *) +open class NativeAvatarView22: SPView { + + // MARK: - Views + + public lazy var activityIndicatorView = UIActivityIndicatorView().do { + $0.stopAnimating() + } + + public lazy var avatarButton = SPButton().do { + $0.imageView?.contentMode = .scaleAspectFill + $0.layer.masksToBounds = true + $0.contentVerticalAlignment = .fill + $0.contentHorizontalAlignment = .fill + } + + public lazy var placeholderButton = SPButton().do { + $0.imageView?.contentMode = .scaleAspectFit + $0.setImage(placeholderImage.alwaysTemplate) + $0.tintColor = .init(light: .systemGray3, dark: .systemGray2) + } + + public lazy var indicatorButton = SPDimmedButton().do { + $0.contentEdgeInsets = .init(side: 4) + } + + // MARK: - Data + + /** + NativeUIKit: Indicate if current state has avatar. + */ + open var hasAvatar: Bool { + switch avatarAppearance { + case .avatar(_): return true + default: return false + } + } + + /** + NativeUIKit: Appearance of avatar button. Can be placeholder or with content. + State loading show loading indicator and hide all other views. + */ + open var avatarAppearance: AvatarAppearance = .placeholder { + didSet { + updateAvatarAppearance() + updateEditAppearance() + } + } + + /** + NativeUIKit: Hidden or visible edit button. + */ + open var isEditable: Bool = false { + didSet { + updateEditAppearance() + } + } + + open var placeholderImage = generatePlaceholderImage(fontSize: 80, fontWeight: .medium) { + didSet { + placeholderButton.setImage(placeholderImage) + } + } + + open var placeholderColorise = SPDimmedButton.Colorise.init(content: .init(light: .systemGray3, dark: .systemGray2), background: .clear) { + didSet { + //placeholderButton.applyDefaultAppearance(with: placeholderColorise) + } + } + + open var indicatorAddImage = UIImage.system("plus", font: .preferredFont(forTextStyle: .title3, weight: .bold)) { + didSet { + updateEditAppearance() + } + } + + open var indicatorAddColorise = SPDimmedButton.Colorise.init(content: .white, background: .systemGreen) { + didSet { + updateEditAppearance() + } + } + + open var indicatorEditImage = UIImage.system("pencil", font: .preferredFont(forTextStyle: .title3, weight: .bold)) { + didSet { + updateEditAppearance() + } + } + + open var indicatorEditColorise = SPDimmedButton.Colorise.init(content: .white, background: .systemBlue) { + didSet { + updateEditAppearance() + } + } + + // MARK: - Init + + open override func commonInit() { + super.commonInit() + layoutMargins = .zero + addSubview(placeholderButton) + addSubview(avatarButton) + addSubview(indicatorButton) + addSubview(activityIndicatorView) + updateAvatarAppearance() + updateEditAppearance() + } + + // MARK: - Layout + + open override func layoutSubviews() { + super.layoutSubviews() + + placeholderButton.frame = .init(side: layoutWidth) + placeholderButton.frame.origin.y = layoutMargins.top + placeholderButton.center.x = frame.width / 2 + + let avatarSideSize = min(placeholderButton.frame.width, placeholderButton.frame.height) * 0.93 + avatarButton.frame = .init(side: avatarSideSize) + avatarButton.center = placeholderButton.center + avatarButton.roundMinimumSide() + indicatorButton.sizeToFit() + indicatorButton.roundMinimumSide() + let sqrt2 = CGFloat(sqrt(2)) + + + if rtl { + let indicatorCenter = ((avatarButton.frame.width * (sqrt2 + 1)) / (2 * sqrt2)) * 1.02 + indicatorButton.center = .init(x: frame.width - indicatorCenter, y: indicatorCenter) + } else { + let indicatorCenter = ((avatarButton.frame.width * (sqrt2 + 1)) / (2 * sqrt2)) * 1.02 + indicatorButton.center = .init(x: indicatorCenter, y: indicatorCenter) + } + + activityIndicatorView.center = placeholderButton.center + } + + open override func sizeThatFits(_ size: CGSize) -> CGSize { + placeholderButton.sizeToFit() + frame.setWidth(placeholderButton.frame.width + layoutMargins.left + layoutMargins.right) + layoutSubviews() + if isEditable { + return .init( + width: max(placeholderButton.frame.maxX, indicatorButton.frame.maxX) + layoutMargins.bottom, + height: max(placeholderButton.frame.maxY, indicatorButton.frame.maxY) + layoutMargins.bottom + ) + } else { + return .init( + width: placeholderButton.frame.maxX + layoutMargins.bottom, + height: placeholderButton.frame.maxY + layoutMargins.bottom + ) + } + } + + // MARK: - Public + + static public func generatePlaceholderImage(fontSize: CGFloat, fontWeight: UIFont.Weight) -> UIImage { + return UIImage.system("person.crop.circle.fill", font: .systemFont(ofSize: fontSize, weight: fontWeight)) + } + + // MARK: - Internal + + /** + NativeUIKit: Call for update to current avatar appearance. + */ + internal func updateAvatarAppearance() { + switch avatarAppearance { + case .loading: + activityIndicatorView.startAnimating() + avatarButton.isHidden = true + placeholderButton.isHidden = true + case .placeholder: + activityIndicatorView.stopAnimating() + avatarButton.isHidden = true + placeholderButton.isHidden = false + case .avatar(let image): + activityIndicatorView.stopAnimating() + avatarButton.isHidden = false + avatarButton.setImage(image) + placeholderButton.isHidden = true + } + } + + /** + NativeUIKit: Call for update to current edit appearance. + */ + internal func updateEditAppearance() { + if isEditable { + switch avatarAppearance { + case .loading: + indicatorButton.isHidden = true + case .placeholder: + indicatorButton.isHidden = false + indicatorButton.setImage(indicatorAddImage) + indicatorButton.applyDefaultAppearance(with: indicatorAddColorise) + case .avatar(_): + indicatorButton.isHidden = false + indicatorButton.setImage(indicatorEditImage) + indicatorButton.applyDefaultAppearance(with: indicatorEditColorise) + } + } else { + indicatorButton.isHidden = true + } + + [placeholderButton, avatarButton, indicatorButton].forEach({ $0.isUserInteractionEnabled = isEditable }) + } + + // MARK: - Models + + /** + NativeUIKit: Appearance states for avatar view. + */ + public enum AvatarAppearance { + + case loading + case placeholder + case avatar(_ image: UIImage) + } + + /** + NativeUIKit: Appearance states for edit button. + */ + public enum EditAppearance { + + case none + case edit + case add + } +}*/ +#endif diff --git a/Sources/UIKitExtension/Diffable/DiffableLargeHeaderItem.swift b/Sources/UIKitExtension/Diffable/DiffableLargeHeaderItem.swift new file mode 100644 index 0000000..54744b7 --- /dev/null +++ b/Sources/UIKitExtension/Diffable/DiffableLargeHeaderItem.swift @@ -0,0 +1,14 @@ +#if canImport(UIKit) && (os(iOS)) +import UIKit +import DiffableKit + +open class DiffableLargeHeaderItem: DiffableActionableItem { + + open var title: String + + public init(id: String? = nil, title: String, action: Action? = nil) { + self.title = title + super.init(id: id ?? title, action: action) + } +} +#endif diff --git a/Sources/UIKitExtension/Extensions/UIScreenExtension.swift b/Sources/UIKitExtension/Extensions/UIScreenExtension.swift new file mode 100644 index 0000000..429fc6a --- /dev/null +++ b/Sources/UIKitExtension/Extensions/UIScreenExtension.swift @@ -0,0 +1,11 @@ +import UIKit + +public extension UIScreen { + + var displayCornerRadius: CGFloat { + guard let cornerRadius = self.value(forKey:"_displayCornerRadius") as? CGFloat else { + return .zero + } + return cornerRadius + } +} diff --git a/Sources/UIKitExtension/Extensions/UIViewControllerExtension.swift b/Sources/UIKitExtension/Extensions/UIViewControllerExtension.swift deleted file mode 100644 index 094ef16..0000000 --- a/Sources/UIKitExtension/Extensions/UIViewControllerExtension.swift +++ /dev/null @@ -1,18 +0,0 @@ -// -// File.swift -// -// -// Created by Ivan Vorobei on 19.07.22. -// - -import Foundation - -/* - @objc open func wrapToNavigationController(prefersLargeTitles: Bool) -> SPNavigationController { - let navigationController = SPNavigationController(rootViewController: self) - #if os(iOS) - navigationController.navigationBar.prefersLargeTitles = prefersLargeTitles - #endif - return navigationController - } - */ diff --git a/Sources/UIKitExtension/SettingIconGenerator.swift b/Sources/UIKitExtension/SettingIconGenerator.swift new file mode 100644 index 0000000..abfaddd --- /dev/null +++ b/Sources/UIKitExtension/SettingIconGenerator.swift @@ -0,0 +1,41 @@ +#if canImport(UIKit) +import UIKit + +extension UIImage { + + static var iconFontSize: Int { 16 } + static var backgroundFontSize: Int { 32 } + static var backgroundSystemName: String { "app.fill" } + + @available(iOS 13, tvOS 13, *) + public static func generateSettingIcon(_ systemName: String, backgroundColor: UIColor) -> UIImage? { + + let iconConfiguration = UIImage.SymbolConfiguration(pointSize: CGFloat(iconFontSize), weight: .regular) + let iconImage = UIImage(systemName: systemName, withConfiguration: iconConfiguration)?.withTintColor(.white, renderingMode: .alwaysOriginal) + + #warning("bg area symol has border, must drop it") + let backgroundConfiguration = UIImage.SymbolConfiguration(pointSize: CGFloat(backgroundFontSize), weight: .regular) + let backgroundImage = UIImage(systemName: backgroundSystemName, withConfiguration: backgroundConfiguration)!.withTintColor(backgroundColor, renderingMode: .alwaysOriginal) + + let size = backgroundImage.size + UIGraphicsBeginImageContextWithOptions(size, false, .zero) + + backgroundImage.draw(in: CGRect(origin: .zero, size: size)) + + if let iconImage = iconImage { + let iconSize = iconImage.size + iconImage.draw(in: CGRect( + origin: .init( + x: (size.width - iconSize.width) / 2, + y: (size.height - iconSize.height) / 2 + ), + size: iconSize + )) + } + + let settingsImage = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + return settingsImage + } +} +#endif diff --git a/Sources/UIKitExtension/Table/UIHeaderTableController+HeaderContainerView.swift b/Sources/UIKitExtension/Table/UIHeaderTableController+HeaderContainerView.swift index 90e241e..c554090 100644 --- a/Sources/UIKitExtension/Table/UIHeaderTableController+HeaderContainerView.swift +++ b/Sources/UIKitExtension/Table/UIHeaderTableController+HeaderContainerView.swift @@ -22,7 +22,6 @@ extension UIHeaderTableController { public override func commonInit() { super.commonInit() insetsLayoutMarginsFromSafeArea = false - contentView.insetsLayoutMarginsFromSafeArea = false layoutMargins = .zero addSubview(contentView) } @@ -39,6 +38,7 @@ extension UIHeaderTableController { public override func sizeThatFits(_ size: CGSize) -> CGSize { frame.setWidth(size.width) layoutSubviews() + //print(size.width, contentView.frame.maxY, layoutMargins.bottom) return .init(width: size.width, height: contentView.frame.maxY + layoutMargins.bottom) } } diff --git a/Sources/UIKitExtension/Table/UIHeaderTableController.swift b/Sources/UIKitExtension/Table/UIHeaderTableController.swift index ce2c4db..ebf68eb 100644 --- a/Sources/UIKitExtension/Table/UIHeaderTableController.swift +++ b/Sources/UIKitExtension/Table/UIHeaderTableController.swift @@ -6,9 +6,15 @@ open class UIHeaderTableController: DiffableTableController { // MARK: - Public open func setHeaderView(_ view: UIView) { - let headerContainerView = HeaderContainerView(contentView: view) - headerContainerView.setWidthAndFit(width: self.view.frame.width) - tableView.tableHeaderView = headerContainerView + let containerView = HeaderContainerView(contentView: view) + //headerContainerView.setWidthAndFit(width: self.view.frame.width) + tableView.tableHeaderView = containerView + } + + open func setFooterView(_ view: UIView) { + let containerView = HeaderContainerView(contentView: view) + //headerContainerView.setWidthAndFit(width: self.view.frame.width) + tableView.tableFooterView = containerView } open func setSpaceBetweenHeaderAndCells(_ value: CGFloat) { @@ -20,27 +26,40 @@ open class UIHeaderTableController: DiffableTableController { open override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() - if let headerContainerView = tableView.tableHeaderView as? HeaderContainerView { - - if headerContainerView.contentView.layoutMargins.left != tableView.layoutMargins.left { - headerContainerView.contentView.layoutMargins.left = tableView.layoutMargins.left - } - - if headerContainerView.contentView.layoutMargins.right != tableView.layoutMargins.right { - headerContainerView.contentView.layoutMargins.right = tableView.layoutMargins.right - } - - headerContainerView.setWidthAndFit(width: view.frame.width) - - if cachedHeaderHeight != headerContainerView.frame.height { - cachedHeaderHeight = headerContainerView.frame.height - DispatchQueue.main.async { [weak self] in - guard let self = self else { return } - self.diffableDataSource?.updateLayout(animated: true, completion: nil) + for view in [tableView.tableHeaderView, tableView.tableFooterView] { + if let containerView = view as? HeaderContainerView { + if containerView.contentView.layoutMargins.left != tableView.layoutMargins.left { + containerView.contentView.layoutMargins.left = tableView.layoutMargins.left + } + + if containerView.contentView.layoutMargins.right != tableView.layoutMargins.right { + containerView.contentView.layoutMargins.right = tableView.layoutMargins.right + } + + containerView.setWidthAndFit(width: self.view.frame.width) + + if view == tableView.tableHeaderView && cachedHeaderHeight != containerView.frame.height { + let animated = (cachedHeaderHeight == nil) ? false : true + cachedHeaderHeight = containerView.frame.height + DispatchQueue.main.async { [weak self] in + guard let self = self else { return } + self.diffableDataSource?.updateLayout(animated: animated, completion: nil) + } + } + + if view == tableView.tableFooterView && cachedFooterHeight != containerView.frame.height { + let animated = (cachedHeaderHeight == nil) ? false : true + cachedFooterHeight = containerView.frame.height + DispatchQueue.main.async { [weak self] in + guard let self = self else { return } + self.diffableDataSource?.updateLayout(animated: animated, completion: nil) + } } } } + } private var cachedHeaderHeight: CGFloat? = nil + private var cachedFooterHeight: CGFloat? = nil } diff --git a/Sources/UIKitExtension/Table/UILargeHeaderTableView.swift b/Sources/UIKitExtension/Table/UILargeHeaderTableView.swift index da0be5b..22c4426 100644 --- a/Sources/UIKitExtension/Table/UILargeHeaderTableView.swift +++ b/Sources/UIKitExtension/Table/UILargeHeaderTableView.swift @@ -10,32 +10,21 @@ open class UILargeHeaderTableView: UICommonTableViewHeaderFooterView { super.commonInit() insetsLayoutMarginsFromSafeArea = false contentView.layoutMargins = .zero + contentView.layoutMargins.left = Spaces.default_half + contentView.preservesSuperviewLayoutMargins = false + contentView.insetsLayoutMarginsFromSafeArea = false contentView.addSubview(headerView) } open override func layoutSubviews() { super.layoutSubviews() headerView.setWidthAndFit(width: contentView.layoutWidth) - headerView.frame.origin = .init(x: contentView.layoutMargins.left, y: layoutMargins.top) + headerView.frame.origin = .init(x: contentView.layoutMargins.left, y: contentView.layoutMargins.top) } open override func sizeThatFits(_ size: CGSize) -> CGSize { let superSize = super.sizeThatFits(size) - return .init(width: superSize.width, height: headerView.frame.height + layoutMargins.bottom) - } -} - -// MARK: - Diffable - -open class DiffableLargeHeaderItem: DiffableActionableItem { - - open var title: String - open var actionTitle: String? - - public init(id: String? = nil, title: String, actionTitle: String? = nil, action: Action? = nil) { - self.title = title - self.actionTitle = actionTitle - super.init(id: id ?? title, action: action) + return .init(width: superSize.width, height: headerView.frame.maxY + contentView.layoutMargins.bottom) } } @@ -45,12 +34,14 @@ extension DiffableTableDataSource.HeaderFooterProvider { return DiffableTableDataSource.HeaderFooterProvider() { (tableView, section, item) -> UIView? in guard let header = item as? DiffableLargeHeaderItem else { return nil } let view = tableView.dequeueReusableHeaderFooterView(withClass: UILargeHeaderTableView.self) - view.headerView.titleLabel.text = header.title - if let actionTitle = header.actionTitle { - view.headerView.button.setTitle(actionTitle) - view.headerView.diffableButtonAction = { + view.headerView.button.setTitle(header.title) + if #available(iOS 14.0, *) { + view.headerView.button.addAction(.init(handler: { _ in header.action?(item, .init(row: .zero, section: section)) - } + }), for: .touchUpInside) + } else { + #warning("add for ios 13") + } return view } diff --git a/Sources/UIKitExtension/Views/UIBorderedView.swift b/Sources/UIKitExtension/Views/UIBorderedView.swift new file mode 100644 index 0000000..511aad1 --- /dev/null +++ b/Sources/UIKitExtension/Views/UIBorderedView.swift @@ -0,0 +1,83 @@ +#if canImport(UIKit) && (os(iOS)) +import UIKit +import SwiftBoost + +open class UIBorderedView: UICommonView { + + // MARK: - Data + + open var position: Position = .top { + didSet { + layoutSubviews() + } + } + + // MARK: - Views + + private let borderView = UICommonView().do { + if #available(iOS 13.0, *) { + $0.backgroundColor = .separator + } else { + $0.backgroundColor = .systemGray + } + } + + // MARK: - Init + + public init(position: Position) { + self.position = position + super.init() + } + + public required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + } + + open override func commonInit() { + super.commonInit() + layoutMargins = .zero + addSubview(borderView) + } + + // MARK: - Actions + + public func setBorderVisible(_ visible: Bool, animated: Bool) { + let work = { [weak self] in + guard let self = self else { return } + self.borderView.alpha = visible ? 1 : 0 + } + if animated { + UIView.animate(withDuration: 0.3, delay: 0, options: [.beginFromCurrentState, .allowUserInteraction], animations: { + work() + }) + } else { + work() + } + } + + // MARK: - Layout + + open override func layoutSubviews() { + super.layoutSubviews() + let leftMargin = self.layoutMargins.left + let rightMargin = self.layoutMargins.right + borderView.frame.setWidth(frame.width - leftMargin - rightMargin) + borderView.frame.setHeight(0.7) + borderView.frame.origin.x = leftMargin + switch position { + case .top: + borderView.frame.origin.y = 0 + case .bottom: + borderView.frame.setMaxY(frame.height) + } + } + + // MARK: - Models + + public enum Position { + + case top + case bottom + } +} +#endif diff --git a/Sources/UIKitExtension/Views/UIGradientView.swift b/Sources/UIKitExtension/Views/UIGradientView.swift new file mode 100644 index 0000000..c73e096 --- /dev/null +++ b/Sources/UIKitExtension/Views/UIGradientView.swift @@ -0,0 +1,100 @@ +#if canImport(UIKit) && (os(iOS)) +import UIKit + +open class UIGradientView: UICommonView { + + open var gradientLayer = CAGradientLayer() + + open var startColor = UIColor.white { + didSet { + updateGradient() + } + } + + open var endColor = UIColor.black { + didSet { + updateGradient() + } + } + + open var startColorPosition = Position.topLeft { + didSet { + updateGradient() + } + } + + open var endColorPosition = Position.bottomRight { + didSet { + updateGradient() + } + } + + // MARK: - Init + + open override func commonInit() { + super.commonInit() + layer.addSublayer(gradientLayer) + } + + // MARK: - Lifecycle + + open override func tintColorDidChange() { + super.tintColorDidChange() + updateGradient() + } + + // MARK: - Layout + + open override func layoutSublayers(of layer: CALayer) { + gradientLayer.frame = self.bounds + super.layoutSublayers(of: layer) + } + + // MARK: - Helpers + + private func updateGradient() { + gradientLayer.colors = [startColor.cgColor, endColor.cgColor] + gradientLayer.locations = [0.0, 1.0] + gradientLayer.startPoint = startColorPosition.point + gradientLayer.endPoint = endColorPosition.point + } + + // MARK: - Models + + public enum Position { + + case topLeft + case topCenter + case topRight + case bottomLeft + case bottomCenter + case bottomRight + case mediumLeft + case mediumRight + case mediumCenter + + var point: CGPoint { + switch self { + case .topLeft: + return CGPoint.init(x: 0, y: 0) + case .topCenter: + return CGPoint.init(x: 0.5, y: 0) + case .topRight: + return CGPoint.init(x: 1, y: 0) + case .bottomLeft: + return CGPoint.init(x: 0, y: 1) + case .bottomCenter: + return CGPoint.init(x: 0.5, y: 1) + case .bottomRight: + return CGPoint.init(x: 1, y: 1) + case .mediumLeft: + return CGPoint.init(x: 0, y: 0.5) + case .mediumRight: + return CGPoint.init(x: 1, y: 0.5) + case .mediumCenter: + return CGPoint.init(x: 0.5, y: 0.5) + } + } + } +} +#endif diff --git a/Sources/UIKitExtension/Views/UILargeHeaderView.swift b/Sources/UIKitExtension/Views/UILargeHeaderView.swift index 84fd2ae..2b80c13 100644 --- a/Sources/UIKitExtension/Views/UILargeHeaderView.swift +++ b/Sources/UIKitExtension/Views/UILargeHeaderView.swift @@ -1,47 +1,85 @@ #if canImport(UIKit) && (os(iOS)) import UIKit +import SwiftBoost open class UILargeHeaderView: UICommonView { // MARK: - Views - public let titleLabel = UICommonLabel().do { - $0.font = UIFont.preferredFont(forTextStyle: .title2, weight: .semibold) - $0.textColor = .label + public let button = UIDimmedButton().do { + $0.applyDefaultAppearance(with: .init(content: .label, background: .clear)) + $0.higlightStyle = .content + $0.titleLabel?.font = UIFont.preferredFont(forTextStyle: .title2, weight: .bold) } - public let button = UIDimmedButton().do { - $0.applyDefaultAppearance(with: .tintedContent) + public var showChevron: Bool = false { + didSet { + if showChevron { + var image = UIImage(systemName: "chevron.right") + let fontConfig = UIImage.SymbolConfiguration(font: UIFont.preferredFont(forTextStyle: .body, weight: .bold)) + image = image?.applyingSymbolConfiguration(fontConfig) + image = image?.withTintColor(.secondaryLabel, renderingMode: .alwaysOriginal) + button.setImage(image) + } else { + button.setImage(nil) + } + } } // MARK: - Init open override func commonInit() { super.commonInit() - layoutMargins = .init(top: Spaces.default_double, left: .zero, bottom: 14, right: .zero) insetsLayoutMarginsFromSafeArea = false preservesSuperviewLayoutMargins = false - addSubviews(titleLabel, button) + layoutMargins = .init(top: Spaces.default_double, left: .zero, bottom: 14, right: .zero) + addSubviews(button) + + showChevron = true } // MARK: - Layout + public func layout(y: CGFloat) { + guard let superview = self.superview else { return } + setWidthAndFit(width: superview.layoutWidth) + frame.origin = .init(x: superview.layoutMargins.left, y: y) + } + open override func layoutSubviews() { super.layoutSubviews() - titleLabel.layoutDynamicHeight( - x: layoutMargins.left, - y: layoutMargins.top, - width: layoutWidth - ) + + let spacing: CGFloat = Spaces.step + button.contentEdgeInsets.left = spacing + button.contentEdgeInsets.right = spacing + button.sizeToFit() - button.setMaxXToSuperviewRightMargin() - button.center.y = titleLabel.center.y + button.frame.origin.x = layoutMargins.left + button.frame.origin.y = layoutMargins.top + + let buttonWidth = button.frame.width + let imageWidth = button.imageView?.frame.width ?? .zero + + + button.imageEdgeInsets = UIEdgeInsets( + top: 1, + left: buttonWidth - spacing - imageWidth, + bottom: -1, + right: -spacing + ) + + button.titleEdgeInsets = UIEdgeInsets( + top: 0, + left: -(imageWidth + spacing + spacing + imageWidth), + bottom: 0, + right: 0 + ) } open override func sizeThatFits(_ size: CGSize) -> CGSize { let superSize = super.sizeThatFits(size) layoutSubviews() - return .init(width: superSize.width, height: titleLabel.frame.maxY + layoutMargins.bottom) + return .init(width: superSize.width, height: button.frame.maxY + layoutMargins.bottom) } // MARK: - Private From 4acece29143317d04624a65f1adda77eccfb5a78 Mon Sep 17 00:00:00 2001 From: Ivan Vorobei Date: Sun, 16 Apr 2023 22:49:07 +0300 Subject: [PATCH 11/16] Added rtl for collection large header. --- Sources/UIKitExtension/Views/UILargeHeaderView.swift | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Sources/UIKitExtension/Views/UILargeHeaderView.swift b/Sources/UIKitExtension/Views/UILargeHeaderView.swift index 2b80c13..6d19df5 100644 --- a/Sources/UIKitExtension/Views/UILargeHeaderView.swift +++ b/Sources/UIKitExtension/Views/UILargeHeaderView.swift @@ -54,8 +54,13 @@ open class UILargeHeaderView: UICommonView { button.contentEdgeInsets.right = spacing button.sizeToFit() - button.frame.origin.x = layoutMargins.left + button.frame.origin.y = layoutMargins.top + if ltr { + button.frame.origin.x = layoutMargins.left + } else { + button.frame.setMaxX(frame.width - layoutMargins.right) + } let buttonWidth = button.frame.width let imageWidth = button.imageView?.frame.width ?? .zero From 7992ab02891d19ec0fe84e2c365f52f6236fe6a8 Mon Sep 17 00:00:00 2001 From: Ivan Vorobei Date: Tue, 29 Aug 2023 19:59:30 +0300 Subject: [PATCH 12/16] Update text field and classes. --- .../UILargeHeaderCollectionView.swift | 2 +- Sources/UIKitExtension/CommonInit.swift | 20 ++++++ .../UICommonScrollViewController.swift | 6 +- .../UICommonSplitViewController.swift | 22 +++++++ .../UIPageContainerController.swift | 11 +++- .../UIPageControllerInterface.swift | 2 + .../UIPageScrollSystemController.swift | 18 ++++-- .../UIPopoverNavigationController.swift | 35 +++++++++++ .../Buttons/UILargeActionButton.swift | 3 +- .../Diffable/DiffableEmptyRowItem.swift | 28 +++++++++ .../Diffable/DiffableTableTextField.swift | 39 ++++++++++++ .../Diffable/DiffableTintTableRow.swift | 22 +++++++ .../UIFooterCollectionFooterView.swift | 30 +++++++++ .../FooterView}/UIFooterView.swift | 0 .../UILargeButtonFooterView.swift | 34 ++++++++++ ...LargeButtonFooterViewTableHeaderView.swift | 26 ++++++++ ...oterView\320\241ollectionHeaderView.swift" | 31 ++++++++++ .../Table/UIEmptyTableViewCell.swift | 26 -------- .../Table/UIHeaderTableController.swift | 13 ++-- .../Table/UITextFieldTableViewCell.swift | 62 +++++++++++++++++++ .../Table/UITintedTableViewCell.swift | 44 +++++++++++++ .../Views/{ => Gradient}/UIGradientView.swift | 0 22 files changed, 433 insertions(+), 41 deletions(-) create mode 100644 Sources/UIKitExtension/Controllers/UICommonSplitViewController.swift create mode 100644 Sources/UIKitExtension/Controllers/UIPopoverNavigationController.swift create mode 100644 Sources/UIKitExtension/Diffable/DiffableEmptyRowItem.swift create mode 100644 Sources/UIKitExtension/Diffable/DiffableTableTextField.swift create mode 100644 Sources/UIKitExtension/Diffable/DiffableTintTableRow.swift create mode 100644 Sources/UIKitExtension/Ready Use/FooterView/UIFooterCollectionFooterView.swift rename Sources/UIKitExtension/{Labels => Ready Use/FooterView}/UIFooterView.swift (100%) create mode 100644 Sources/UIKitExtension/Ready Use/LargeButtonFooterView/UILargeButtonFooterView.swift create mode 100644 Sources/UIKitExtension/Ready Use/LargeButtonFooterView/UILargeButtonFooterViewTableHeaderView.swift create mode 100644 "Sources/UIKitExtension/Ready Use/LargeButtonFooterView/UILargeButtonFooterView\320\241ollectionHeaderView.swift" create mode 100644 Sources/UIKitExtension/Table/UITextFieldTableViewCell.swift create mode 100644 Sources/UIKitExtension/Table/UITintedTableViewCell.swift rename Sources/UIKitExtension/Views/{ => Gradient}/UIGradientView.swift (100%) diff --git a/Sources/UIKitExtension/Collection/UILargeHeaderCollectionView.swift b/Sources/UIKitExtension/Collection/UILargeHeaderCollectionView.swift index bb9c9f7..efa418e 100644 --- a/Sources/UIKitExtension/Collection/UILargeHeaderCollectionView.swift +++ b/Sources/UIKitExtension/Collection/UILargeHeaderCollectionView.swift @@ -48,7 +48,7 @@ extension DiffableCollectionDataSource.HeaderFooterProvider { action(item, .init(row: .zero, section: section)) }), for: .touchUpInside) } else { - view.headerView.showChevron = false + view.headerView.showChevron = true } } else { #warning("add for ios 13") diff --git a/Sources/UIKitExtension/CommonInit.swift b/Sources/UIKitExtension/CommonInit.swift index eb1dcbb..990ba98 100644 --- a/Sources/UIKitExtension/CommonInit.swift +++ b/Sources/UIKitExtension/CommonInit.swift @@ -32,6 +32,26 @@ open class UICommonView: UIView, UICommonInit { open func commonInit() {} } +open class UICommonScrollView: UIScrollView, UICommonInit { + + public init() { + super.init(frame: CGRect.zero) + commonInit() + } + + public override init(frame: CGRect) { + super.init(frame: frame) + commonInit() + } + + public required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + commonInit() + } + + open func commonInit() {} +} + open class UICommonControl: UIControl, UICommonInit { public init() { diff --git a/Sources/UIKitExtension/Controllers/UICommonScrollViewController.swift b/Sources/UIKitExtension/Controllers/UICommonScrollViewController.swift index 3826baf..2cc2e06 100644 --- a/Sources/UIKitExtension/Controllers/UICommonScrollViewController.swift +++ b/Sources/UIKitExtension/Controllers/UICommonScrollViewController.swift @@ -2,7 +2,7 @@ import UIKit open class UICommonScrollViewController: UICommonViewContoller { - public lazy var scrollView = UIScrollView() + public lazy var scrollView = UICommonScrollView() open override func viewDidLoad() { super.viewDidLoad() @@ -10,6 +10,10 @@ open class UICommonScrollViewController: UICommonViewContoller { scrollView.delaysContentTouches = false view.addSubview(scrollView) + if #available(iOS 15.0, *) { + setContentScrollView(scrollView) + } + scrollView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ scrollView.topAnchor.constraint(equalTo: view.topAnchor), diff --git a/Sources/UIKitExtension/Controllers/UICommonSplitViewController.swift b/Sources/UIKitExtension/Controllers/UICommonSplitViewController.swift new file mode 100644 index 0000000..d77eb69 --- /dev/null +++ b/Sources/UIKitExtension/Controllers/UICommonSplitViewController.swift @@ -0,0 +1,22 @@ +import UIKit + +open class UICommonSplitViewController: UISplitViewController, UISplitViewControllerDelegate { + + public init() { + super.init(nibName: nil, bundle: nil) + commonInit() + } + + @available(iOS 14.0, *) + public override init(style: UISplitViewController.Style) { + super.init(style: style) + commonInit() + } + + public required init?(coder: NSCoder) { + super.init(coder: coder) + commonInit() + } + + open func commonInit() {} +} diff --git a/Sources/UIKitExtension/Controllers/UIPageContainerController/UIPageContainerController.swift b/Sources/UIKitExtension/Controllers/UIPageContainerController/UIPageContainerController.swift index 7699d40..22bca28 100644 --- a/Sources/UIKitExtension/Controllers/UIPageContainerController/UIPageContainerController.swift +++ b/Sources/UIKitExtension/Controllers/UIPageContainerController/UIPageContainerController.swift @@ -6,12 +6,19 @@ open class UIPageContainerController: UIViewController { // todo clean to - public func scrollToPage(index: Int) { + public func scrollToPage(index: Int, animated: Bool) { if let pageController = containerController as? UIPageScrollSystemController { - pageController.safeScrollTo(index: index, animated: true) + pageController.safeScrollTo(index: index, animated: animated) } } + public func getCurrentController() -> UIViewController? { + if let pageController = containerController as? UIPageScrollSystemController { + return pageController.getCurrentController() + } + return nil + } + public init(data: UIPageContainerControllerDataSystem, scrollSystem: UIPageContainerControllerScrollSystem) { switch data { case .dataSource(let dataSource): diff --git a/Sources/UIKitExtension/Controllers/UIPageContainerController/UIPageControllerInterface.swift b/Sources/UIKitExtension/Controllers/UIPageContainerController/UIPageControllerInterface.swift index de624f1..d247452 100644 --- a/Sources/UIKitExtension/Controllers/UIPageContainerController/UIPageControllerInterface.swift +++ b/Sources/UIKitExtension/Controllers/UIPageContainerController/UIPageControllerInterface.swift @@ -5,4 +5,6 @@ protocol UIPageContainerControllerInterface: AnyObject { var allowScroll: Bool { get set } func safeScrollTo(index: Int, animated: Bool) + + func getCurrentController() -> UIViewController? } diff --git a/Sources/UIKitExtension/Controllers/UIPageContainerController/UIPageScrollSystemController.swift b/Sources/UIKitExtension/Controllers/UIPageContainerController/UIPageScrollSystemController.swift index aa8fcf3..c1ae66f 100644 --- a/Sources/UIKitExtension/Controllers/UIPageContainerController/UIPageScrollSystemController.swift +++ b/Sources/UIKitExtension/Controllers/UIPageContainerController/UIPageScrollSystemController.swift @@ -9,10 +9,10 @@ extension UIPageContainerController { // MARK: - Init - init(dataSource: UIPageContainerControllerDataSource) { + init(dataSource: UIPageContainerControllerDataSource, initialIndex: Int = .zero) { self.controllersDataSource = dataSource super.init(transitionStyle: .scroll, navigationOrientation: .horizontal, options: [:]) - guard let firstController = getViewController(for: .zero) else { return } + guard let firstController = getViewController(for: initialIndex) else { return } setViewControllers([firstController], direction: .forward, animated: false) } @@ -69,7 +69,12 @@ extension UIPageContainerController { // todo complete func safeScrollTo(index: Int, animated: Bool) { guard let controller = getViewController(for: index) else { return } - setViewControllers([controller], direction: .forward, animated: true) + setViewControllers([controller], direction: .forward, animated: animated) + + } + + func getCurrentController() -> UIViewController? { + return viewControllers?.first } // MARK: - UIPageViewControllerDataSource @@ -81,8 +86,9 @@ extension UIPageContainerController { if newIndex < 0 { return nil } return childControllers[newIndex] } else { - guard let indexAfter = (viewController as? UIPageContainerControllerIndexProtocol)?.pageControllerIndex else { fatalError() } - return getViewController(for: indexAfter - 1) + print("vv \(viewController)") + guard let currentIndex = (viewController as? UIPageContainerControllerIndexProtocol)?.pageControllerIndex else { fatalError() } + return getViewController(for: currentIndex - 1) } } @@ -94,6 +100,7 @@ extension UIPageContainerController { return childControllers[newIndex] } else { guard let indexBefore = (viewController as? UIPageContainerControllerIndexProtocol)?.pageControllerIndex else { fatalError() } + print("currentIndex before \(indexBefore)") return getViewController(for: indexBefore + 1) } } @@ -101,6 +108,7 @@ extension UIPageContainerController { // MARK: - Internal private func getViewController(for index: Int) -> UIViewController? { + print("ask controller for index \(index) in page") guard let controller = controllersDataSource?.pageContainerViewController(for: index) else { return nil } guard var indexController = controller as? UIPageContainerControllerIndexProtocol else { fatalError() } indexController.pageControllerIndex = index diff --git a/Sources/UIKitExtension/Controllers/UIPopoverNavigationController.swift b/Sources/UIKitExtension/Controllers/UIPopoverNavigationController.swift new file mode 100644 index 0000000..460eebe --- /dev/null +++ b/Sources/UIKitExtension/Controllers/UIPopoverNavigationController.swift @@ -0,0 +1,35 @@ +import UIKit + +open class UIPopoverNavigationController: UICommonNavigationController, UIPopoverPresentationControllerDelegate { + + public init(rootController: UIViewController, preferredContentSize size: CGSize, sourceView: UIView, direction: UIPopoverArrowDirection) { + super.init(rootViewController: rootController) + + modalPresentationStyle = .popover + preferredContentSize = size + popoverPresentationController?.permittedArrowDirections = [direction] + popoverPresentationController?.sourceView = sourceView + popoverPresentationController?.sourceRect = sourceView.frame + popoverPresentationController?.delegate = self + } + + public init(rootController: UIViewController, preferredContentSize size: CGSize, sourceBarButtonItem: UIBarButtonItem, direction: UIPopoverArrowDirection) { + super.init(rootViewController: rootController) + + modalPresentationStyle = .popover + preferredContentSize = size + popoverPresentationController?.permittedArrowDirections = [direction] + popoverPresentationController?.barButtonItem = sourceBarButtonItem + popoverPresentationController?.delegate = self + } + + required public init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - UIPopoverPresentationControllerDelegate + + public func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle { + return .none + } +} diff --git a/Sources/UIKitExtension/Controls/Buttons/UILargeActionButton.swift b/Sources/UIKitExtension/Controls/Buttons/UILargeActionButton.swift index 655dc48..a85c758 100644 --- a/Sources/UIKitExtension/Controls/Buttons/UILargeActionButton.swift +++ b/Sources/UIKitExtension/Controls/Buttons/UILargeActionButton.swift @@ -16,7 +16,8 @@ open class UILargeActionButton: UIDimmedButton { titleLabel?.font = UIFont.preferredFont(forTextStyle: .headline, addPoints: 1) titleLabel?.numberOfLines = 1 titleImageInset = 6 - contentEdgeInsets = .init(horizontal: 10, vertical: 12) + // before was 12, 14 looks little better + contentEdgeInsets = .init(horizontal: 10, vertical: 14) roundCorners(curve: .continuous, radius: Appearance.Corners.readable_area) } diff --git a/Sources/UIKitExtension/Diffable/DiffableEmptyRowItem.swift b/Sources/UIKitExtension/Diffable/DiffableEmptyRowItem.swift new file mode 100644 index 0000000..1c7b901 --- /dev/null +++ b/Sources/UIKitExtension/Diffable/DiffableEmptyRowItem.swift @@ -0,0 +1,28 @@ +#if canImport(UIKit) && (os(iOS)) +import UIKit +import DiffableKit + +open class DiffableEmptyRowItem: DiffableTableRow { + + var verticalMargins: UIEmptyTableViewCell.Margins + + public init(id: String, verticalMargins: UIEmptyTableViewCell.Margins, text: String, detail: String?) { + self.verticalMargins = verticalMargins + super.init(id: id, text: text, detail: detail) + } +} + +extension DiffableTableDataSource.CellProvider { + + public static var empty: DiffableTableDataSource.CellProvider { + return DiffableTableDataSource.CellProvider() { (tableView, indexPath, item) -> UITableViewCell? in + guard let item = item as? DiffableEmptyRowItem else { return nil } + let cell = tableView.dequeueReusableCell(withClass: UIEmptyTableViewCell.self, for: indexPath) + cell.placeholderView.headerLabel.text = item.text + cell.placeholderView.descriptionLabel.text = item.detail + cell.verticalMargins = item.verticalMargins + return cell + } + } +} +#endif diff --git a/Sources/UIKitExtension/Diffable/DiffableTableTextField.swift b/Sources/UIKitExtension/Diffable/DiffableTableTextField.swift new file mode 100644 index 0000000..f34d698 --- /dev/null +++ b/Sources/UIKitExtension/Diffable/DiffableTableTextField.swift @@ -0,0 +1,39 @@ +#if canImport(UIKit) && (os(iOS)) +import UIKit +import DiffableKit + +open class DiffableTableTextField: DiffableItem { + + open var title: String? = nil + open var getTextFieldText: (() -> String?) = { return nil } + open var textFieldplaceholder: String? = nil + open var action: Action + + public init( + id: String, + title: String?, + getTextFieldText: @escaping (() -> String?), + textFieldplaceholder: String?, + action: @escaping Action + ) { + self.title = title + self.getTextFieldText = getTextFieldText + self.textFieldplaceholder = textFieldplaceholder + self.action = action + super.init(id: id) + } + + open override var hash: Int { + var hasher = Hasher() + hasher.combine(id) + return hasher.finalize() + } + + open override func isEqual(_ object: Any?) -> Bool { + guard let object = object as? DiffableItem else { return false } + return id == object.id + } + + public typealias Action = (_ item: DiffableItem, _ indexPath: IndexPath, _ value: String?) -> Void +} +#endif diff --git a/Sources/UIKitExtension/Diffable/DiffableTintTableRow.swift b/Sources/UIKitExtension/Diffable/DiffableTintTableRow.swift new file mode 100644 index 0000000..f45540d --- /dev/null +++ b/Sources/UIKitExtension/Diffable/DiffableTintTableRow.swift @@ -0,0 +1,22 @@ +#if canImport(UIKit) && (os(iOS)) +import UIKit +import DiffableKit + +open class DiffableTintedTableRow: DiffableTableRow { + + open var color: UIColor + + public init( + id: String? = nil, + text: String, + detail: String? = nil, + icon: UIImage? = nil, + color: UIColor, + accessoryType: UITableViewCell.AccessoryType = .none, + action: Action? = nil + ) { + self.color = color + super.init(id: id ?? text, text: text, detail: detail, icon: icon, accessoryType: accessoryType, selectionStyle: .none, action: action) + } +} +#endif diff --git a/Sources/UIKitExtension/Ready Use/FooterView/UIFooterCollectionFooterView.swift b/Sources/UIKitExtension/Ready Use/FooterView/UIFooterCollectionFooterView.swift new file mode 100644 index 0000000..cccabd6 --- /dev/null +++ b/Sources/UIKitExtension/Ready Use/FooterView/UIFooterCollectionFooterView.swift @@ -0,0 +1,30 @@ +import UIKit + +open class UIFooterCollectionFooterView: UICommonCollectionReusableView { + + public let contentView = UIFooterView() + + open override func commonInit() { + super.commonInit() + layoutMargins = .zero + preservesSuperviewLayoutMargins = false + insetsLayoutMarginsFromSafeArea = false + addSubview(contentView) + } + + open override func prepareForReuse() { + super.prepareForReuse() + } + + open override func layoutSubviews() { + super.layoutSubviews() + contentView.setWidthAndFit(width: layoutWidth) + contentView.frame.origin = .init(x: layoutMargins.left, y: layoutMargins.top) + } + + open override func sizeThatFits(_ size: CGSize) -> CGSize { + let superSize = super.sizeThatFits(size) + layoutSubviews() + return .init(width: superSize.width, height: contentView.frame.maxY + layoutMargins.bottom) + } +} diff --git a/Sources/UIKitExtension/Labels/UIFooterView.swift b/Sources/UIKitExtension/Ready Use/FooterView/UIFooterView.swift similarity index 100% rename from Sources/UIKitExtension/Labels/UIFooterView.swift rename to Sources/UIKitExtension/Ready Use/FooterView/UIFooterView.swift diff --git a/Sources/UIKitExtension/Ready Use/LargeButtonFooterView/UILargeButtonFooterView.swift b/Sources/UIKitExtension/Ready Use/LargeButtonFooterView/UILargeButtonFooterView.swift new file mode 100644 index 0000000..9fac4a6 --- /dev/null +++ b/Sources/UIKitExtension/Ready Use/LargeButtonFooterView/UILargeButtonFooterView.swift @@ -0,0 +1,34 @@ +import UIKit + +open class UILargeButtonFooterView: UICommonView { + + public let button = UILargeActionButton().do { + $0.applyDefaultAppearance(with: .tinted) + } + + public let footerView = UIFooterView() + + // MARK: - Init + + open override func commonInit() { + super.commonInit() + addSubviews([button, footerView]) + } + + // MARK: - Layout + + open override func layoutSubviews() { + super.layoutSubviews() + button.sizeToFit() + button.frame.setWidth(frame.width) + + footerView.setWidthAndFit(width: button.frame.width) + footerView.frame.origin.x = button.frame.origin.x + footerView.frame.origin.y = button.frame.maxY + } + + open override func sizeThatFits(_ size: CGSize) -> CGSize { + layoutSubviews() + return .init(width: size.width, height: footerView.frame.maxY + layoutMargins.bottom) + } +} diff --git a/Sources/UIKitExtension/Ready Use/LargeButtonFooterView/UILargeButtonFooterViewTableHeaderView.swift b/Sources/UIKitExtension/Ready Use/LargeButtonFooterView/UILargeButtonFooterViewTableHeaderView.swift new file mode 100644 index 0000000..029c044 --- /dev/null +++ b/Sources/UIKitExtension/Ready Use/LargeButtonFooterView/UILargeButtonFooterViewTableHeaderView.swift @@ -0,0 +1,26 @@ +import UIKit + +open class UILargeButtonFooterViewTableHeaderView: UICommonView { + + public let contentView = UILargeButtonFooterView() + + open override func commonInit() { + super.commonInit() + addSubview(contentView) + layoutMargins = .zero + layoutMargins.top = Spaces.step + } + + open override func layoutSubviews() { + super.layoutSubviews() + contentView.setWidthAndFit(width: readableWidth) + contentView.frame.origin.x = .zero + contentView.frame.origin.y = layoutMargins.top + } + + open override func sizeThatFits(_ size: CGSize) -> CGSize { + let superSize = super.sizeThatFits(size) + layoutIfNeeded() + return .init(width: superSize.width, height: contentView.frame.maxY + layoutMargins.bottom) + } +} diff --git "a/Sources/UIKitExtension/Ready Use/LargeButtonFooterView/UILargeButtonFooterView\320\241ollectionHeaderView.swift" "b/Sources/UIKitExtension/Ready Use/LargeButtonFooterView/UILargeButtonFooterView\320\241ollectionHeaderView.swift" new file mode 100644 index 0000000..704b3f5 --- /dev/null +++ "b/Sources/UIKitExtension/Ready Use/LargeButtonFooterView/UILargeButtonFooterView\320\241ollectionHeaderView.swift" @@ -0,0 +1,31 @@ +import UIKit + +open class UILargeButtonFooterViewСollectionHeaderView: UICommonCollectionReusableView { + + public let contentView = UILargeButtonFooterView() + + open override func commonInit() { + super.commonInit() + layoutMargins = .zero + preservesSuperviewLayoutMargins = false + insetsLayoutMarginsFromSafeArea = false + addSubview(contentView) + } + + open override func prepareForReuse() { + super.prepareForReuse() + contentView.button.removeTargetsAndActions() + } + + open override func layoutSubviews() { + super.layoutSubviews() + contentView.setWidthAndFit(width: layoutWidth) + contentView.frame.origin = .init(x: layoutMargins.left, y: layoutMargins.top) + } + + open override func sizeThatFits(_ size: CGSize) -> CGSize { + let superSize = super.sizeThatFits(size) + layoutSubviews() + return .init(width: superSize.width, height: contentView.frame.maxY + layoutMargins.bottom) + } +} diff --git a/Sources/UIKitExtension/Table/UIEmptyTableViewCell.swift b/Sources/UIKitExtension/Table/UIEmptyTableViewCell.swift index f1136b5..fc8cfbb 100644 --- a/Sources/UIKitExtension/Table/UIEmptyTableViewCell.swift +++ b/Sources/UIKitExtension/Table/UIEmptyTableViewCell.swift @@ -77,30 +77,4 @@ open class UIEmptyTableViewCell: UICommonTableViewCell { } } } - -// MARK: - Diffable - -open class DiffableEmptyRowItem: DiffableTableRow { - - var verticalMargins: UIEmptyTableViewCell.Margins - - public init(id: String, verticalMargins: UIEmptyTableViewCell.Margins, text: String, detail: String?) { - self.verticalMargins = verticalMargins - super.init(id: id, text: text, detail: detail) - } -} - -extension DiffableTableDataSource.CellProvider { - - public static var empty: DiffableTableDataSource.CellProvider { - return DiffableTableDataSource.CellProvider() { (tableView, indexPath, item) -> UITableViewCell? in - guard let item = item as? DiffableEmptyRowItem else { return nil } - let cell = tableView.dequeueReusableCell(withClass: UIEmptyTableViewCell.self, for: indexPath) - cell.placeholderView.headerLabel.text = item.text - cell.placeholderView.descriptionLabel.text = item.detail - cell.verticalMargins = item.verticalMargins - return cell - } - } -} #endif diff --git a/Sources/UIKitExtension/Table/UIHeaderTableController.swift b/Sources/UIKitExtension/Table/UIHeaderTableController.swift index ebf68eb..827efc3 100644 --- a/Sources/UIKitExtension/Table/UIHeaderTableController.swift +++ b/Sources/UIKitExtension/Table/UIHeaderTableController.swift @@ -21,19 +21,22 @@ open class UIHeaderTableController: DiffableTableController { tableView.tableHeaderView?.layoutMargins.bottom = value } + open func setSpaceBetweenFooterAndCells(_ value: CGFloat) { + tableView.tableFooterView?.layoutMargins.top = value + } + // MARK: - Layout open override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() - for view in [tableView.tableHeaderView, tableView.tableFooterView] { if let containerView = view as? HeaderContainerView { - if containerView.contentView.layoutMargins.left != tableView.layoutMargins.left { - containerView.contentView.layoutMargins.left = tableView.layoutMargins.left + if containerView.layoutMargins.left != tableView.layoutMargins.left { + containerView.layoutMargins.left = tableView.layoutMargins.left } - if containerView.contentView.layoutMargins.right != tableView.layoutMargins.right { - containerView.contentView.layoutMargins.right = tableView.layoutMargins.right + if containerView.layoutMargins.right != tableView.layoutMargins.right { + containerView.layoutMargins.right = tableView.layoutMargins.right } containerView.setWidthAndFit(width: self.view.frame.width) diff --git a/Sources/UIKitExtension/Table/UITextFieldTableViewCell.swift b/Sources/UIKitExtension/Table/UITextFieldTableViewCell.swift new file mode 100644 index 0000000..ca454f4 --- /dev/null +++ b/Sources/UIKitExtension/Table/UITextFieldTableViewCell.swift @@ -0,0 +1,62 @@ +import UIKit +import DiffableKit + +open class UITextFieldTableViewCell: UICommonTableViewCell { + + public static var reuseIdentifier: String { "UITextFieldTableViewCell" } + + public let titleLabel = UICommonLabel() + public let textField = UICommonTextField() + + open override func commonInit() { + super.commonInit() + contentView.addSubviews(titleLabel, textField) + selectionStyle = .none + } + + open override func layoutSubviews() { + super.layoutSubviews() + if titleLabel.text == nil { + textField.setEqualSuperviewMarginsWithFrames() + } else { + titleLabel.sizeToFit() + titleLabel.frame.origin.x = contentView.layoutMargins.left + titleLabel.setYCenter() + + textField.sizeToFit() + textField.setYCenter() + let space: CGFloat = Spaces.default_half + textField.frame.setWidth(contentView.layoutWidth - titleLabel.frame.maxX - space) + textField.frame.origin.x = titleLabel.frame.maxX + space + } + } + + open override func prepareForReuse() { + super.prepareForReuse() + titleLabel.text = nil + textField.text = nil + textField.placeholder = nil + textField.removeTargetsAndActions() + accessoryView = nil + textLabel?.text = nil + detailTextLabel?.text = nil + } +} + +extension DiffableTableDataSource.CellProvider { + + @available(iOS 14.0, *) + public static var textField: DiffableTableDataSource.CellProvider { + return DiffableTableDataSource.CellProvider() { (tableView, indexPath, item) -> UITableViewCell? in + guard let item = item as? DiffableTableTextField else { return nil } + let cell = tableView.dequeueReusableCell(withClass: UITextFieldTableViewCell.self, for: indexPath) + cell.titleLabel.text = item.title + cell.textField.text = item.getTextFieldText() + cell.textField.placeholder = item.textFieldplaceholder + cell.textField.addAction(.init(handler: { _ in + item.action(item, indexPath, cell.textField.text) + }), for: .editingChanged) + return cell + } + } +} diff --git a/Sources/UIKitExtension/Table/UITintedTableViewCell.swift b/Sources/UIKitExtension/Table/UITintedTableViewCell.swift new file mode 100644 index 0000000..2efd9aa --- /dev/null +++ b/Sources/UIKitExtension/Table/UITintedTableViewCell.swift @@ -0,0 +1,44 @@ +import UIKit +import DiffableKit + +open class UITintedTableViewCell: DiffableTableViewCell { + + public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: .value1, reuseIdentifier: reuseIdentifier) + textLabel?.font = UIFont.preferredFont(forTextStyle: .body, weight: .medium) + selectionStyle = .none + } + + public required init?(coder: NSCoder) { + super.init(coder: coder) + } + + open override func setHighlighted(_ highlighted: Bool, animated: Bool) { + for view in [imageView, textLabel] { + #warning("to constant higloight 0.7") + view?.alpha = highlighted ? 0.7 : 1 + } + } + + open override func sizeThatFits(_ size: CGSize) -> CGSize { + let superSize = super.sizeThatFits(size) + return .init(width: superSize.width, height: superSize.height + 4) + } +} + +extension DiffableTableDataSource.CellProvider { + + public static var rowTinted: DiffableTableDataSource.CellProvider { + return DiffableTableDataSource.CellProvider() { (tableView, indexPath, item) -> UITableViewCell? in + guard let item = item as? DiffableTintedTableRow else { return nil } + let cell = tableView.dequeueReusableCell(withClass: UITintedTableViewCell.self, for: indexPath) + cell.textLabel?.text = item.text + cell.detailTextLabel?.text = item.detail + cell.imageView?.image = item.icon + cell.accessoryType = item.accessoryType + cell.imageView?.tintColor = item.color + cell.textLabel?.textColor = item.color + return cell + } + } +} diff --git a/Sources/UIKitExtension/Views/UIGradientView.swift b/Sources/UIKitExtension/Views/Gradient/UIGradientView.swift similarity index 100% rename from Sources/UIKitExtension/Views/UIGradientView.swift rename to Sources/UIKitExtension/Views/Gradient/UIGradientView.swift From 1f59b9b02be89f0a6b38584b48ef3899614a7ea7 Mon Sep 17 00:00:00 2001 From: Ivan Vorobei Date: Wed, 27 Sep 2023 20:44:25 +0300 Subject: [PATCH 13/16] Added secondarySystemGroupedBorder. --- Sources/UIKitExtension/Extensions/UIColorExtension.swift | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Sources/UIKitExtension/Extensions/UIColorExtension.swift b/Sources/UIKitExtension/Extensions/UIColorExtension.swift index 3e779d2..3184fd9 100644 --- a/Sources/UIKitExtension/Extensions/UIColorExtension.swift +++ b/Sources/UIKitExtension/Extensions/UIColorExtension.swift @@ -3,6 +3,13 @@ import UIKit extension UIColor { + public static var secondarySystemGroupedBorder: UIColor { + return .init( + light: secondarySystemGroupedBackground.darker(by: 0.1), + dark: secondarySystemGroupedBackground.lighter(by: 0.1) + ) + } + /** UIKitExtension: New color to system stack. Its color for empty areas and it usually downed of main background color. From 265342c6f99fec6e0ce0b90808a8718e13f1766b Mon Sep 17 00:00:00 2001 From: Ivan Vorobei Date: Tue, 17 Oct 2023 22:24:43 +0300 Subject: [PATCH 14/16] Drop delegate. --- .../Controllers/UICommonSplitViewController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/UIKitExtension/Controllers/UICommonSplitViewController.swift b/Sources/UIKitExtension/Controllers/UICommonSplitViewController.swift index d77eb69..27871cc 100644 --- a/Sources/UIKitExtension/Controllers/UICommonSplitViewController.swift +++ b/Sources/UIKitExtension/Controllers/UICommonSplitViewController.swift @@ -1,6 +1,6 @@ import UIKit -open class UICommonSplitViewController: UISplitViewController, UISplitViewControllerDelegate { +open class UICommonSplitViewController: UISplitViewController { public init() { super.init(nibName: nil, bundle: nil) From 2019ff884c80c001334e43f65debe858ed5f084f Mon Sep 17 00:00:00 2001 From: Ivan Vorobei Date: Sat, 24 Feb 2024 00:06:00 +0300 Subject: [PATCH 15/16] Updated version. --- Package.swift | 4 ++-- .../UILargeHeaderCollectionView.swift | 17 ++++++----------- .../Table/UILargeHeaderTableView.swift | 11 +++-------- .../Table/UITextFieldTableViewCell.swift | 1 + 4 files changed, 12 insertions(+), 21 deletions(-) diff --git a/Package.swift b/Package.swift index 31b7adf..77caa25 100644 --- a/Package.swift +++ b/Package.swift @@ -5,8 +5,8 @@ import PackageDescription let package = Package( name: "UIKitExtension", platforms: [ - .iOS(.v13), - .tvOS(.v13) + .iOS(.v14), + .tvOS(.v14) ], products: [ .library( diff --git a/Sources/UIKitExtension/Collection/UILargeHeaderCollectionView.swift b/Sources/UIKitExtension/Collection/UILargeHeaderCollectionView.swift index efa418e..2337369 100644 --- a/Sources/UIKitExtension/Collection/UILargeHeaderCollectionView.swift +++ b/Sources/UIKitExtension/Collection/UILargeHeaderCollectionView.swift @@ -41,18 +41,13 @@ extension DiffableCollectionDataSource.HeaderFooterProvider { guard let header = item as? DiffableLargeHeaderItem else { return nil } let view = collectionView.dequeueReusableSupplementaryView(withCalss: UILargeHeaderCollectionView.self, kind: UICollectionView.elementKindSectionHeader, for: .init(row: .zero, section: section)) view.headerView.button.setTitle(header.title) - if #available(iOS 14.0, *) { - if let action = header.action { - view.headerView.showChevron = true - view.headerView.button.addAction(.init(handler: { _ in - action(item, .init(row: .zero, section: section)) - }), for: .touchUpInside) - } else { - view.headerView.showChevron = true - } + if let action = header.action { + view.headerView.showChevron = true + view.headerView.button.addAction(.init(handler: { _ in + action(item, .init(row: .zero, section: section)) + }), for: .touchUpInside) } else { - #warning("add for ios 13") - view.headerView.showChevron = false + view.headerView.showChevron = true } view.layoutSubviews() return view diff --git a/Sources/UIKitExtension/Table/UILargeHeaderTableView.swift b/Sources/UIKitExtension/Table/UILargeHeaderTableView.swift index 22c4426..087ee50 100644 --- a/Sources/UIKitExtension/Table/UILargeHeaderTableView.swift +++ b/Sources/UIKitExtension/Table/UILargeHeaderTableView.swift @@ -35,14 +35,9 @@ extension DiffableTableDataSource.HeaderFooterProvider { guard let header = item as? DiffableLargeHeaderItem else { return nil } let view = tableView.dequeueReusableHeaderFooterView(withClass: UILargeHeaderTableView.self) view.headerView.button.setTitle(header.title) - if #available(iOS 14.0, *) { - view.headerView.button.addAction(.init(handler: { _ in - header.action?(item, .init(row: .zero, section: section)) - }), for: .touchUpInside) - } else { - #warning("add for ios 13") - - } + view.headerView.button.addAction(.init(handler: { _ in + header.action?(item, .init(row: .zero, section: section)) + }), for: .touchUpInside) return view } } diff --git a/Sources/UIKitExtension/Table/UITextFieldTableViewCell.swift b/Sources/UIKitExtension/Table/UITextFieldTableViewCell.swift index ca454f4..a33a14b 100644 --- a/Sources/UIKitExtension/Table/UITextFieldTableViewCell.swift +++ b/Sources/UIKitExtension/Table/UITextFieldTableViewCell.swift @@ -51,6 +51,7 @@ extension DiffableTableDataSource.CellProvider { guard let item = item as? DiffableTableTextField else { return nil } let cell = tableView.dequeueReusableCell(withClass: UITextFieldTableViewCell.self, for: indexPath) cell.titleLabel.text = item.title + cell.titleLabel.textColor = .secondaryLabel cell.textField.text = item.getTextFieldText() cell.textField.placeholder = item.textFieldplaceholder cell.textField.addAction(.init(handler: { _ in From 89dfc018c4ca6e39f0c326289a72220b7a422f5d Mon Sep 17 00:00:00 2001 From: Ivan Vorobei Date: Mon, 25 Nov 2024 00:14:58 +0300 Subject: [PATCH 16/16] Update UIScreenExtension.swift --- Sources/UIKitExtension/Extensions/UIScreenExtension.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/UIKitExtension/Extensions/UIScreenExtension.swift b/Sources/UIKitExtension/Extensions/UIScreenExtension.swift index 429fc6a..ecf1c23 100644 --- a/Sources/UIKitExtension/Extensions/UIScreenExtension.swift +++ b/Sources/UIKitExtension/Extensions/UIScreenExtension.swift @@ -3,7 +3,7 @@ import UIKit public extension UIScreen { var displayCornerRadius: CGFloat { - guard let cornerRadius = self.value(forKey:"_displayCornerRadius") as? CGFloat else { + guard let cornerRadius = self.value(forKey: "_displayCornerRadius") as? CGFloat else { return .zero } return cornerRadius