├── Assets └── Readme │ ├── spm-install-preview.png │ └── Elements │ ├── NativePlayPauseCompactButton.svg │ ├── NativeSmallActionButton.svg │ ├── NativeAvatarView.svg │ └── NativeLargeActionButton.svg ├── Example App ├── tvOS Example │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── Contents.json │ │ └── App Icon & Top Shelf Image.brandassets │ │ │ ├── App Icon.imagestack │ │ │ ├── Back.imagestacklayer │ │ │ │ ├── Contents.json │ │ │ │ └── Content.imageset │ │ │ │ │ └── Contents.json │ │ │ ├── Front.imagestacklayer │ │ │ │ ├── Contents.json │ │ │ │ └── Content.imageset │ │ │ │ │ └── Contents.json │ │ │ ├── Middle.imagestacklayer │ │ │ │ ├── Contents.json │ │ │ │ └── Content.imageset │ │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ │ ├── App Icon - App Store.imagestack │ │ │ ├── Back.imagestacklayer │ │ │ │ ├── Contents.json │ │ │ │ └── Content.imageset │ │ │ │ │ └── Contents.json │ │ │ ├── Front.imagestacklayer │ │ │ │ ├── Contents.json │ │ │ │ └── Content.imageset │ │ │ │ │ └── Contents.json │ │ │ ├── Middle.imagestacklayer │ │ │ │ ├── Contents.json │ │ │ │ └── Content.imageset │ │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ │ ├── Top Shelf Image.imageset │ │ │ └── Contents.json │ │ │ ├── Top Shelf Image Wide.imageset │ │ │ └── Contents.json │ │ │ └── Contents.json │ ├── Info.plist │ └── LaunchScreen.storyboard ├── iOS Example │ ├── App │ │ ├── Assets.xcassets │ │ │ ├── Contents.json │ │ │ └── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ ├── Base.lproj │ │ │ └── LaunchScreen.storyboard │ │ └── AppDelegate.swift │ ├── Scenes │ │ └── RootController.swift │ └── Info.plist ├── watchOS Example Extension │ ├── ExtensionDelegate.swift │ ├── Assets.xcassets │ │ ├── Contents.json │ │ └── Complication.complicationset │ │ │ ├── Graphic Bezel.imageset │ │ │ └── Contents.json │ │ │ ├── Graphic Corner.imageset │ │ │ └── Contents.json │ │ │ ├── Graphic Circular.imageset │ │ │ └── Contents.json │ │ │ ├── Graphic Large Rectangular.imageset │ │ │ └── Contents.json │ │ │ ├── Circular.imageset │ │ │ └── Contents.json │ │ │ ├── Modular.imageset │ │ │ └── Contents.json │ │ │ ├── Extra Large.imageset │ │ │ └── Contents.json │ │ │ ├── Utilitarian.imageset │ │ │ └── Contents.json │ │ │ ├── Graphic Extra Large.imageset │ │ │ └── Contents.json │ │ │ └── Contents.json │ └── Info.plist ├── watchOS Example │ ├── Assets.xcassets │ │ ├── Contents.json │ │ ├── AccentColor.colorset │ │ │ └── Contents.json │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Base.lproj │ │ └── Interface.storyboard │ └── Info.plist └── NativeUIKit.xcodeproj │ ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcuserdata │ │ └── ivanvorobei.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── swiftpm │ │ └── Package.resolved │ ├── xcuserdata │ └── ivanvorobei.xcuserdatad │ │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ │ └── xcschemes │ │ └── xcschememanagement.plist │ └── xcshareddata │ └── xcschemes │ ├── iOS Example.xcscheme │ └── watchOS Example.xcscheme ├── .gitignore ├── TODO.md ├── .github ├── ISSUE_TEMPLATE │ ├── feature_request.md │ ├── question.md │ └── bug_report.md └── PULL_REQUEST_TEMPLATE.md ├── CONTRIBUTING.md ├── LICENSE ├── Package.swift ├── Sources └── NativeUIKit │ ├── NativeAppearance.swift │ ├── Bars │ ├── NativeMimicrateNavigationBarView.swift │ ├── ToolBar │ │ ├── NativeMimicrateNavigationToolBarView.swift │ │ ├── NativeSmallActionToolBarView.swift │ │ ├── LargeAction │ │ │ ├── NativeSkipableLargeActionToolBarView.swift │ │ │ ├── NativeLargeSmallActionToolBarView.swift │ │ │ └── NativeLargeActionToolBarView.swift │ │ └── NativeAppleAuthToolBarView.swift │ ├── NativeMimicrateBarView.swift │ └── NativeBorderedView.swift │ ├── Table │ ├── LargeHeader │ │ ├── NativeLargeHeaderViewExtension.swift │ │ ├── NativeLargeHeaderItem.swift │ │ └── CellProvider+LargeHeader.swift │ ├── Empty │ │ ├── NativeEmptyRowItem.swift │ │ ├── CellProvider+Empty.swift │ │ └── NativeEmptyTableViewCell.swift │ └── Button │ │ ├── NativeLeftButtonTableViewCell.swift │ │ ├── CellProvider+Button.swift │ │ └── NativeDiffableLeftButton.swift │ ├── Controllers │ ├── Complex │ │ ├── Profile │ │ │ ├── NativeProfileController.swift │ │ │ ├── NativeAvatarHeaderView.swift │ │ │ └── NativeProfileHeaderView.swift │ │ └── Onboarding │ │ │ ├── Actinos │ │ │ ├── NativeOnboardingActionsController.swift │ │ │ └── NativeOnbiardingActionButton.swift │ │ │ ├── NativeOnboardingController.swift │ │ │ └── Features │ │ │ ├── NativeOnboardingFeaturesController.swift │ │ │ └── NativeOnboardingFeatureView.swift │ ├── Scroll │ │ ├── NativeHeaderController.swift │ │ ├── NativeHeaderTextFieldController.swift │ │ └── NativeHeaderFullWidthImageController.swift │ ├── Table │ │ └── Header │ │ │ ├── NativeHeaderTableController+HeaderContainerView.swift │ │ │ └── NativeHeaderTableController.swift │ └── Navigation │ │ └── NativeNavigationController.swift │ ├── Collection │ ├── LargeHeader │ │ ├── SPDiffableCollectionHeaderFooterProvider+LargeHeader.swift │ │ └── NativeLargeHeaderCollectionView.swift │ └── NativePromoCollectionViewCell.swift │ ├── Controls │ ├── NativePlayPauseCompactButton.swift │ ├── NativeLargeTextField.swift │ └── Buttons │ │ ├── NativeSmallActionButton.swift │ │ ├── NativeMediumActionButton.swift │ │ └── NativeLargeActionButton.swift │ ├── NativeLayout.swift │ ├── Labels │ ├── NativeFooterView.swift │ └── NativeModalHeaderView.swift │ └── Views │ ├── NativeLargeHeaderView.swift │ ├── NativePromoView.swift │ └── NativePlaceholderView.swift └── CODE_OF_CONDUCT.md /Assets/Readme/spm-install-preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sparrowcode/UIKitExtension/HEAD/Assets/Readme/spm-install-preview.png -------------------------------------------------------------------------------- /Example App/tvOS Example/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import SparrowKit 3 | 4 | @main 5 | class AppDelegate: SPAppWindowDelegate {} 6 | -------------------------------------------------------------------------------- /Example App/tvOS Example/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # osX files 2 | .DS_Store 3 | .Trashes 4 | 5 | # Swift Package Manager 6 | .swiftpm 7 | 8 | # Assets 9 | Elements.sketch 10 | -------------------------------------------------------------------------------- /Example App/iOS Example/App/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Example App/watchOS Example Extension/ExtensionDelegate.swift: -------------------------------------------------------------------------------- 1 | import WatchKit 2 | 3 | class ExtensionDelegate: NSObject, WKExtensionDelegate {} 4 | -------------------------------------------------------------------------------- /Example App/watchOS Example/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Example App/watchOS Example Extension/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # TODO 2 | 3 | Here provided ideas or features which will be implemented soon. 4 | 5 | - Readme. 6 | - Example app. 7 | - Add to list in opensource. 8 | - Add docs. 9 | - Added examples of controllers. 10 | -------------------------------------------------------------------------------- /Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Example App/NativeUIKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example App/watchOS Example/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Example App/NativeUIKit.xcodeproj/xcuserdata/ivanvorobei.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: ivanvorobei 7 | 8 | --- 9 | 10 | **Feature Description** 11 | Describe what functionality you want to see. 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Question 3 | about: Something is not clear with the project 4 | title: '' 5 | labels: question 6 | assignees: ivanvorobei 7 | 8 | --- 9 | 10 | **Describe the Problem** 11 | A clear and concise description of what you want to do. 12 | -------------------------------------------------------------------------------- /Example App/NativeUIKit.xcodeproj/project.xcworkspace/xcuserdata/ivanvorobei.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sparrowcode/UIKitExtension/HEAD/Example App/NativeUIKit.xcodeproj/project.xcworkspace/xcuserdata/ivanvorobei.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Goal 2 | 3 | 4 | ## Checklist 5 | 6 | - [] Testing in compability platforms 7 | - [] Installed correct via `Swift Package Manager` and `Cocoapods` 8 | -------------------------------------------------------------------------------- /Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Example App/NativeUIKit.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: ivanvorobei 7 | --- 8 | 9 | **Details** 10 | - iOS Version [e.g. 15.2] 11 | - Framework Version [e.g. 1.0.2] 12 | - Installed via [e.g. SPM, Cocoapods, Manually] 13 | 14 | **Describe the Bug** 15 | A clear and concise description of what the bug is. 16 | -------------------------------------------------------------------------------- /Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "tv", 9 | "scale" : "2x" 10 | } 11 | ], 12 | "info" : { 13 | "author" : "xcode", 14 | "version" : 1 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "tv", 9 | "scale" : "2x" 10 | } 11 | ], 12 | "info" : { 13 | "author" : "xcode", 14 | "version" : 1 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "tv", 9 | "scale" : "2x" 10 | } 11 | ], 12 | "info" : { 13 | "author" : "xcode", 14 | "version" : 1 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | }, 6 | "layers" : [ 7 | { 8 | "filename" : "Front.imagestacklayer" 9 | }, 10 | { 11 | "filename" : "Middle.imagestacklayer" 12 | }, 13 | { 14 | "filename" : "Back.imagestacklayer" 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | }, 6 | "layers" : [ 7 | { 8 | "filename" : "Front.imagestacklayer" 9 | }, 10 | { 11 | "filename" : "Middle.imagestacklayer" 12 | }, 13 | { 14 | "filename" : "Back.imagestacklayer" 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /Example App/watchOS Example Extension/Assets.xcassets/Complication.complicationset/Graphic Bezel.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "watch", 5 | "scale" : "2x", 6 | "screen-width" : ">161" 7 | }, 8 | { 9 | "idiom" : "watch", 10 | "scale" : "2x", 11 | "screen-width" : ">183" 12 | } 13 | ], 14 | "info" : { 15 | "author" : "xcode", 16 | "version" : 1 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Example App/watchOS Example Extension/Assets.xcassets/Complication.complicationset/Graphic Corner.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "watch", 5 | "scale" : "2x", 6 | "screen-width" : ">161" 7 | }, 8 | { 9 | "idiom" : "watch", 10 | "scale" : "2x", 11 | "screen-width" : ">183" 12 | } 13 | ], 14 | "info" : { 15 | "author" : "xcode", 16 | "version" : 1 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Example App/watchOS Example Extension/Assets.xcassets/Complication.complicationset/Graphic Circular.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "watch", 5 | "scale" : "2x", 6 | "screen-width" : ">161" 7 | }, 8 | { 9 | "idiom" : "watch", 10 | "scale" : "2x", 11 | "screen-width" : ">183" 12 | } 13 | ], 14 | "info" : { 15 | "author" : "xcode", 16 | "version" : 1 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Example App/watchOS Example Extension/Assets.xcassets/Complication.complicationset/Graphic Large Rectangular.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "watch", 5 | "scale" : "2x", 6 | "screen-width" : ">161" 7 | }, 8 | { 9 | "idiom" : "watch", 10 | "scale" : "2x", 11 | "screen-width" : ">183" 12 | } 13 | ], 14 | "info" : { 15 | "author" : "xcode", 16 | "version" : 1 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "tv", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "idiom" : "tv-marketing", 13 | "scale" : "1x" 14 | }, 15 | { 16 | "idiom" : "tv-marketing", 17 | "scale" : "2x" 18 | } 19 | ], 20 | "info" : { 21 | "author" : "xcode", 22 | "version" : 1 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "tv", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "idiom" : "tv-marketing", 13 | "scale" : "1x" 14 | }, 15 | { 16 | "idiom" : "tv-marketing", 17 | "scale" : "2x" 18 | } 19 | ], 20 | "info" : { 21 | "author" : "xcode", 22 | "version" : 1 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Example App/watchOS Example Extension/Assets.xcassets/Complication.complicationset/Circular.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "watch", 5 | "scale" : "2x", 6 | "screen-width" : "<=145" 7 | }, 8 | { 9 | "idiom" : "watch", 10 | "scale" : "2x", 11 | "screen-width" : ">161" 12 | }, 13 | { 14 | "idiom" : "watch", 15 | "scale" : "2x", 16 | "screen-width" : ">145" 17 | }, 18 | { 19 | "idiom" : "watch", 20 | "scale" : "2x", 21 | "screen-width" : ">183" 22 | } 23 | ], 24 | "info" : { 25 | "author" : "xcode", 26 | "version" : 1 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Example App/watchOS Example Extension/Assets.xcassets/Complication.complicationset/Modular.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "watch", 5 | "scale" : "2x", 6 | "screen-width" : "<=145" 7 | }, 8 | { 9 | "idiom" : "watch", 10 | "scale" : "2x", 11 | "screen-width" : ">161" 12 | }, 13 | { 14 | "idiom" : "watch", 15 | "scale" : "2x", 16 | "screen-width" : ">145" 17 | }, 18 | { 19 | "idiom" : "watch", 20 | "scale" : "2x", 21 | "screen-width" : ">183" 22 | } 23 | ], 24 | "info" : { 25 | "author" : "xcode", 26 | "version" : 1 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Example App/watchOS Example Extension/Assets.xcassets/Complication.complicationset/Extra Large.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "watch", 5 | "scale" : "2x", 6 | "screen-width" : "<=145" 7 | }, 8 | { 9 | "idiom" : "watch", 10 | "scale" : "2x", 11 | "screen-width" : ">161" 12 | }, 13 | { 14 | "idiom" : "watch", 15 | "scale" : "2x", 16 | "screen-width" : ">145" 17 | }, 18 | { 19 | "idiom" : "watch", 20 | "scale" : "2x", 21 | "screen-width" : ">183" 22 | } 23 | ], 24 | "info" : { 25 | "author" : "xcode", 26 | "version" : 1 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Example App/watchOS Example Extension/Assets.xcassets/Complication.complicationset/Utilitarian.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "watch", 5 | "scale" : "2x", 6 | "screen-width" : "<=145" 7 | }, 8 | { 9 | "idiom" : "watch", 10 | "scale" : "2x", 11 | "screen-width" : ">161" 12 | }, 13 | { 14 | "idiom" : "watch", 15 | "scale" : "2x", 16 | "screen-width" : ">145" 17 | }, 18 | { 19 | "idiom" : "watch", 20 | "scale" : "2x", 21 | "screen-width" : ">183" 22 | } 23 | ], 24 | "info" : { 25 | "author" : "xcode", 26 | "version" : 1 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Example App/watchOS Example Extension/Assets.xcassets/Complication.complicationset/Graphic Extra Large.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "watch", 5 | "scale" : "2x", 6 | "screen-width" : "<=145" 7 | }, 8 | { 9 | "idiom" : "watch", 10 | "scale" : "2x", 11 | "screen-width" : ">161" 12 | }, 13 | { 14 | "idiom" : "watch", 15 | "scale" : "2x", 16 | "screen-width" : ">145" 17 | }, 18 | { 19 | "idiom" : "watch", 20 | "scale" : "2x", 21 | "screen-width" : ">183" 22 | } 23 | ], 24 | "info" : { 25 | "author" : "xcode", 26 | "version" : 1 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Here provided more info about project, contribution process and recomended changes. 4 | Please, read it before pull request or create issue. 5 | 6 | ## Codestyle 7 | 8 | ### Marks 9 | 10 | For clean struct code good is using marks. 11 | 12 | ```swift 13 | class Example { 14 | 15 | // MARK: - Init 16 | 17 | init() {} 18 | } 19 | ``` 20 | 21 | Here you find all which using in project: 22 | 23 | - // MARK: - Init 24 | - // MARK: - Lifecycle 25 | - // MARK: - Layout 26 | - // MARK: - Public 27 | - // MARK: - Private 28 | - // MARK: - Internal 29 | - // MARK: - Models 30 | - // MARK: - Ovveride 31 | 32 | 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. 33 | -------------------------------------------------------------------------------- /Example App/tvOS Example/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "assets" : [ 3 | { 4 | "filename" : "App Icon - App Store.imagestack", 5 | "idiom" : "tv", 6 | "role" : "primary-app-icon", 7 | "size" : "1280x768" 8 | }, 9 | { 10 | "filename" : "App Icon.imagestack", 11 | "idiom" : "tv", 12 | "role" : "primary-app-icon", 13 | "size" : "400x240" 14 | }, 15 | { 16 | "filename" : "Top Shelf Image Wide.imageset", 17 | "idiom" : "tv", 18 | "role" : "top-shelf-image-wide", 19 | "size" : "2320x720" 20 | }, 21 | { 22 | "filename" : "Top Shelf Image.imageset", 23 | "idiom" : "tv", 24 | "role" : "top-shelf-image", 25 | "size" : "1920x720" 26 | } 27 | ], 28 | "info" : { 29 | "author" : "xcode", 30 | "version" : 1 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Example App/watchOS Example/Base.lproj/Interface.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Example App/NativeUIKit.xcodeproj/xcuserdata/ivanvorobei.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | iOS Example.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | tvOS Example.xcscheme_^#shared#^_ 13 | 14 | orderHint 15 | 3 16 | 17 | watchOS Example.xcscheme_^#shared#^_ 18 | 19 | orderHint 20 | 2 21 | 22 | 23 | SuppressBuildableAutocreation 24 | 25 | F4C33DD826C92DF8001A28B1 26 | 27 | primary 28 | 29 | 30 | F4C33E2F26C932C8001A28B1 31 | 32 | primary 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /Example App/NativeUIKit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "pins": [ 4 | { 5 | "package": "SparrowKit", 6 | "repositoryURL": "https://github.com/ivanvorobei/SparrowKit", 7 | "state": { 8 | "branch": null, 9 | "revision": "4a6283d0e5e010a99c038bd103a8c883245aafa5", 10 | "version": "3.5.4" 11 | } 12 | }, 13 | { 14 | "package": "SPDiffable", 15 | "repositoryURL": "https://github.com/ivanvorobei/SPDiffable", 16 | "state": { 17 | "branch": null, 18 | "revision": "49d6a86438d0e82efc8da1e0202bceee9b1f5666", 19 | "version": "4.0.6" 20 | } 21 | }, 22 | { 23 | "package": "SPPerspective", 24 | "repositoryURL": "https://github.com/ivanvorobei/SPPerspective", 25 | "state": { 26 | "branch": null, 27 | "revision": "580ed06a5daec94c5095379971570289b429fa3d", 28 | "version": "1.4.1" 29 | } 30 | } 31 | ] 32 | }, 33 | "version": 1 34 | } 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Ivan Vorobei 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Example App/watchOS Example/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | UISupportedInterfaceOrientations 22 | 23 | UIInterfaceOrientationPortrait 24 | UIInterfaceOrientationPortraitUpsideDown 25 | 26 | WKCompanionAppBundleIdentifier 27 | by.ivanvorobei.opensource.nativeuikit.ios 28 | WKWatchKitApp 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /Example App/tvOS Example/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | arm64 30 | 31 | UIUserInterfaceStyle 32 | Automatic 33 | 34 | 35 | -------------------------------------------------------------------------------- /Example App/watchOS Example Extension/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | CLKComplicationPrincipalClass 22 | $(PRODUCT_MODULE_NAME).ComplicationController 23 | NSExtension 24 | 25 | NSExtensionAttributes 26 | 27 | WKAppBundleIdentifier 28 | by.ivanvorobei.opensource.nativeuikit.ios.watchkitapp 29 | 30 | NSExtensionPointIdentifier 31 | com.apple.watchkit 32 | 33 | WKExtensionDelegateClassName 34 | $(PRODUCT_MODULE_NAME).ExtensionDelegate 35 | 36 | 37 | -------------------------------------------------------------------------------- /Example App/iOS Example/Scenes/RootController.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | import UIKit 23 | import SparrowKit 24 | import NativeUIKit 25 | 26 | class RootController: UIViewController { 27 | 28 | 29 | } 30 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.3 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "NativeUIKit", 7 | platforms: [ 8 | .iOS(.v12), 9 | .tvOS(.v12), 10 | .watchOS(.v6) 11 | ], 12 | products: [ 13 | .library( 14 | name: "NativeUIKit", targets: ["NativeUIKit"] 15 | ) 16 | ], 17 | dependencies: [ 18 | .package(url: "https://github.com/ivanvorobei/SparrowKit", .upToNextMajor(from: "3.6.0")), 19 | .package(url: "https://github.com/ivanvorobei/SPPerspective", .upToNextMajor(from: "1.4.1")), 20 | .package(url: "https://github.com/ivanvorobei/SPDiffable", .upToNextMajor(from: "4.1.0")), 21 | .package(url: "https://github.com/ivanvorobei/SPPageController", .upToNextMajor(from: "1.3.2")) 22 | ], 23 | targets: [ 24 | .target( 25 | name: "NativeUIKit", 26 | dependencies: [ 27 | .product(name: "SparrowKit", package: "SparrowKit"), 28 | .product(name: "SPPerspective", package: "SPPerspective"), 29 | .product(name: "SPDiffable", package: "SPDiffable"), 30 | .product(name: "SPPageController", package: "SPPageController") 31 | ], 32 | swiftSettings: [ 33 | .define("NATIVEUIKIT_SPM") 34 | ] 35 | ) 36 | ], 37 | swiftLanguageVersions: [.v5] 38 | ) 39 | -------------------------------------------------------------------------------- /Example App/watchOS Example Extension/Assets.xcassets/Complication.complicationset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "assets" : [ 3 | { 4 | "filename" : "Circular.imageset", 5 | "idiom" : "watch", 6 | "role" : "circular" 7 | }, 8 | { 9 | "filename" : "Extra Large.imageset", 10 | "idiom" : "watch", 11 | "role" : "extra-large" 12 | }, 13 | { 14 | "filename" : "Graphic Bezel.imageset", 15 | "idiom" : "watch", 16 | "role" : "graphic-bezel" 17 | }, 18 | { 19 | "filename" : "Graphic Circular.imageset", 20 | "idiom" : "watch", 21 | "role" : "graphic-circular" 22 | }, 23 | { 24 | "filename" : "Graphic Corner.imageset", 25 | "idiom" : "watch", 26 | "role" : "graphic-corner" 27 | }, 28 | { 29 | "filename" : "Graphic Extra Large.imageset", 30 | "idiom" : "watch", 31 | "role" : "graphic-extra-large" 32 | }, 33 | { 34 | "filename" : "Graphic Large Rectangular.imageset", 35 | "idiom" : "watch", 36 | "role" : "graphic-large-rectangular" 37 | }, 38 | { 39 | "filename" : "Modular.imageset", 40 | "idiom" : "watch", 41 | "role" : "modular" 42 | }, 43 | { 44 | "filename" : "Utilitarian.imageset", 45 | "idiom" : "watch", 46 | "role" : "utilitarian" 47 | } 48 | ], 49 | "info" : { 50 | "author" : "xcode", 51 | "version" : 1 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Sources/NativeUIKit/NativeAppearance.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #if canImport(UIKit) && (os(iOS)) 23 | import UIKit 24 | 25 | enum NativeAppearance { 26 | 27 | enum Corners { 28 | 29 | static var readable_area: CGFloat { 15 } 30 | } 31 | 32 | static var actionable_element_highlight_opacity: CGFloat { 0.6 } 33 | } 34 | #endif 35 | -------------------------------------------------------------------------------- /Assets/Readme/Elements/NativePlayPauseCompactButton.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | NativePlayPauseCompactButton 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example App/iOS Example/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UIApplicationSupportsIndirectInputEvents 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Assets/Readme/Elements/NativeSmallActionButton.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Sources/NativeUIKit/Bars/NativeMimicrateNavigationBarView.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #if canImport(UIKit) && (os(iOS)) 23 | import UIKit 24 | import SparrowKit 25 | 26 | open class NativeMimicrateNavigationBarView: NativeMimicrateBarView { 27 | 28 | // MARK: - Init 29 | 30 | public init() { 31 | super.init(borderPosition: .bottom) 32 | } 33 | 34 | public required init?(coder aDecoder: NSCoder) { 35 | fatalError("init(coder:) has not been implemented") 36 | } 37 | } 38 | #endif 39 | -------------------------------------------------------------------------------- /Sources/NativeUIKit/Table/LargeHeader/NativeLargeHeaderViewExtension.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #if canImport(UIKit) && (os(iOS)) 23 | import UIKit 24 | 25 | extension NativeLargeHeaderView { 26 | 27 | func configure(with item: NativeLargeHeaderItem, section: Int) { 28 | titleLabel.text = item.title 29 | if let actionTitle = item.actionTitle { 30 | button.setTitle(actionTitle) 31 | diffableButtonAction = { 32 | item.action?(item, .init(row: .zero, section: section)) 33 | } 34 | } 35 | } 36 | } 37 | #endif 38 | -------------------------------------------------------------------------------- /Example App/iOS Example/App/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Sources/NativeUIKit/Table/Empty/NativeEmptyRowItem.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #if canImport(UIKit) && (os(iOS)) 23 | import UIKit 24 | import SparrowKit 25 | import SPDiffable 26 | 27 | @available(iOS 13.0, *) 28 | open class NativeEmptyRowItem: SPDiffableTableRow { 29 | 30 | var verticalMargins: NativeEmptyTableViewCell.Margins 31 | 32 | public init(id: String, verticalMargins: NativeEmptyTableViewCell.Margins, text: String, detail: String?) { 33 | self.verticalMargins = verticalMargins 34 | super.init(id: id, text: text, detail: detail) 35 | } 36 | } 37 | #endif 38 | -------------------------------------------------------------------------------- /Example App/iOS Example/App/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | import UIKit 23 | import SparrowKit 24 | import NativeUIKit 25 | 26 | @UIApplicationMain 27 | class AppDelegate: SPAppWindowDelegate { 28 | 29 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 30 | let rootController = RootController() 31 | let navigationController = NativeNavigationController(rootViewController: rootController) 32 | makeKeyAndVisible(viewController: navigationController, tint: .systemBlue) 33 | return true 34 | } 35 | } 36 | 37 | -------------------------------------------------------------------------------- /Example App/tvOS Example/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /Sources/NativeUIKit/Table/LargeHeader/NativeLargeHeaderItem.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2020 Ivan Vorobei (hello@ivanvorobei.io) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #if canImport(UIKit) && (os(iOS) || os(tvOS)) 23 | import UIKit 24 | import SPDiffable 25 | 26 | /** 27 | SPDiffable: Native large header with button. 28 | */ 29 | open class NativeLargeHeaderItem: SPDiffableItem, SPDiffableItemActionable { 30 | 31 | open var title: String 32 | open var actionTitle: String? 33 | open var action: Action? 34 | 35 | public init(id: String? = nil, title: String, actionTitle: String? = nil, action: Action? = nil) { 36 | self.title = title 37 | self.actionTitle = actionTitle 38 | self.action = action 39 | super.init(id: id ?? title) 40 | } 41 | } 42 | #endif 43 | -------------------------------------------------------------------------------- /Sources/NativeUIKit/Table/LargeHeader/CellProvider+LargeHeader.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #if canImport(UIKit) && (os(iOS)) 23 | import UIKit 24 | import SPDiffable 25 | 26 | @available(iOS 13.0, *) 27 | extension SPDiffableTableDataSource.HeaderFooterProvider { 28 | 29 | public static var largeHeader: SPDiffableTableDataSource.HeaderFooterProvider { 30 | return SPDiffableTableDataSource.HeaderFooterProvider() { (tableView, section, item) -> UIView? in 31 | guard let header = item as? NativeLargeHeaderItem else { return nil } 32 | let view = NativeLargeHeaderView() 33 | view.configure(with: header, section: section) 34 | return view 35 | } 36 | } 37 | } 38 | #endif 39 | -------------------------------------------------------------------------------- /Example App/watchOS Example/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "watch", 5 | "role" : "notificationCenter", 6 | "scale" : "2x", 7 | "size" : "24x24", 8 | "subtype" : "38mm" 9 | }, 10 | { 11 | "idiom" : "watch", 12 | "role" : "notificationCenter", 13 | "scale" : "2x", 14 | "size" : "27.5x27.5", 15 | "subtype" : "42mm" 16 | }, 17 | { 18 | "idiom" : "watch", 19 | "role" : "companionSettings", 20 | "scale" : "2x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "watch", 25 | "role" : "companionSettings", 26 | "scale" : "3x", 27 | "size" : "29x29" 28 | }, 29 | { 30 | "idiom" : "watch", 31 | "role" : "appLauncher", 32 | "scale" : "2x", 33 | "size" : "40x40", 34 | "subtype" : "38mm" 35 | }, 36 | { 37 | "idiom" : "watch", 38 | "role" : "appLauncher", 39 | "scale" : "2x", 40 | "size" : "44x44", 41 | "subtype" : "40mm" 42 | }, 43 | { 44 | "idiom" : "watch", 45 | "role" : "appLauncher", 46 | "scale" : "2x", 47 | "size" : "50x50", 48 | "subtype" : "44mm" 49 | }, 50 | { 51 | "idiom" : "watch", 52 | "role" : "quickLook", 53 | "scale" : "2x", 54 | "size" : "86x86", 55 | "subtype" : "38mm" 56 | }, 57 | { 58 | "idiom" : "watch", 59 | "role" : "quickLook", 60 | "scale" : "2x", 61 | "size" : "98x98", 62 | "subtype" : "42mm" 63 | }, 64 | { 65 | "idiom" : "watch", 66 | "role" : "quickLook", 67 | "scale" : "2x", 68 | "size" : "108x108", 69 | "subtype" : "44mm" 70 | }, 71 | { 72 | "idiom" : "watch-marketing", 73 | "scale" : "1x", 74 | "size" : "1024x1024" 75 | } 76 | ], 77 | "info" : { 78 | "author" : "xcode", 79 | "version" : 1 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Example App/iOS Example/App/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Sources/NativeUIKit/Controllers/Complex/Profile/NativeProfileController.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #if canImport(UIKit) && (os(iOS)) 23 | import UIKit 24 | import SparrowKit 25 | import SPDiffable 26 | 27 | @available(iOS 13.0, *) 28 | open class NativeProfileController: NativeHeaderTableController { 29 | 30 | // MARK: - Views 31 | 32 | public let headerView = NativeAvatarHeaderView() 33 | 34 | // MARK: - Init 35 | 36 | public init(style: UITableView.Style = .insetGrouped) { 37 | super.init(style: style, headerView: headerView) 38 | } 39 | 40 | public required init?(coder: NSCoder) { 41 | fatalError("init(coder:) has not been implemented") 42 | } 43 | 44 | // MARK: - Lifecycle 45 | 46 | open override func viewDidLoad() { 47 | super.viewDidLoad() 48 | navigationItem.title = .empty 49 | } 50 | } 51 | #endif 52 | -------------------------------------------------------------------------------- /Sources/NativeUIKit/Table/Empty/CellProvider+Empty.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #if canImport(UIKit) && (os(iOS)) 23 | import UIKit 24 | import SPDiffable 25 | 26 | @available(iOS 13.0, *) 27 | extension SPDiffableTableDataSource.CellProvider { 28 | 29 | public static var empty: SPDiffableTableDataSource.CellProvider { 30 | return SPDiffableTableDataSource.CellProvider() { (tableView, indexPath, item) -> UITableViewCell? in 31 | guard let item = item as? NativeEmptyRowItem else { return nil } 32 | let cell = tableView.dequeueReusableCell(withClass: NativeEmptyTableViewCell.self, for: indexPath) 33 | cell.placeholderView.headerLabel.text = item.text 34 | cell.placeholderView.descriptionLabel.text = item.detail 35 | cell.verticalMargins = item.verticalMargins 36 | return cell 37 | } 38 | } 39 | } 40 | #endif 41 | -------------------------------------------------------------------------------- /Sources/NativeUIKit/Collection/LargeHeader/SPDiffableCollectionHeaderFooterProvider+LargeHeader.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | import UIKit 23 | import SPDiffable 24 | 25 | @available(iOS 13.0, tvOS 13.0, *) 26 | extension SPDiffableCollectionDataSource.HeaderFooterProvider { 27 | 28 | #if (os(iOS)) 29 | public static var largeHeader: SPDiffableCollectionDataSource.HeaderFooterProvider { 30 | return SPDiffableCollectionDataSource.HeaderFooterProvider.init { collectionView, kind, indexPath, item in 31 | guard let header = item as? NativeLargeHeaderItem else { return nil } 32 | let view = collectionView.dequeueReusableSupplementaryView(withCalss: NativeLargeHeaderCollectionView.self, kind: kind, for: indexPath) 33 | view.headerView.configure(with: header, section: indexPath.section) 34 | return view 35 | } 36 | } 37 | #endif 38 | } 39 | -------------------------------------------------------------------------------- /Sources/NativeUIKit/Table/Button/NativeLeftButtonTableViewCell.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #if canImport(UIKit) && (os(iOS)) 23 | import UIKit 24 | import SparrowKit 25 | 26 | open class NativeLeftButtonTableViewCell: SPTableViewCell { 27 | 28 | public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { 29 | super.init(style: .value1, reuseIdentifier: reuseIdentifier) 30 | } 31 | 32 | required public init?(coder aDecoder: NSCoder) { 33 | fatalError("init(coder:) has not been implemented") 34 | } 35 | 36 | open override func commonInit() { 37 | super.commonInit() 38 | textLabel?.font = .preferredFont(forTextStyle: .body, weight: .medium) 39 | higlightStyle = .content 40 | } 41 | 42 | open override func sizeThatFits(_ size: CGSize) -> CGSize { 43 | let superSize = super.sizeThatFits(size) 44 | return .init(width: superSize.width, height: superSize.height + 6) 45 | } 46 | } 47 | #endif 48 | -------------------------------------------------------------------------------- /Sources/NativeUIKit/Table/Button/CellProvider+Button.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #if canImport(UIKit) && (os(iOS)) 23 | import UIKit 24 | import SPDiffable 25 | 26 | @available(iOS 13.0, *) 27 | extension SPDiffableTableDataSource.CellProvider { 28 | 29 | public static var button: SPDiffableTableDataSource.CellProvider { 30 | return SPDiffableTableDataSource.CellProvider() { (tableView, indexPath, item) -> UITableViewCell? in 31 | guard let item = item as? NativeDiffableLeftButton else { return nil } 32 | let cell = tableView.dequeueReusableCell(withClass: NativeLeftButtonTableViewCell.self, for: indexPath) 33 | cell.textLabel?.text = item.text 34 | cell.textLabel?.textColor = item.textColor 35 | cell.detailTextLabel?.text = item.detail 36 | cell.detailTextLabel?.textColor = item.detailColor 37 | cell.imageView?.image = item.icon 38 | cell.accessoryType = item.accessoryType 39 | cell.higlightStyle = item.higlightStyle 40 | return cell 41 | } 42 | } 43 | } 44 | #endif 45 | -------------------------------------------------------------------------------- /Sources/NativeUIKit/Bars/ToolBar/NativeMimicrateNavigationToolBarView.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #if canImport(UIKit) && (os(iOS)) 23 | import UIKit 24 | import SparrowKit 25 | 26 | open class NativeMimicrateToolBarView: NativeMimicrateBarView { 27 | 28 | // MARK: - Init 29 | 30 | public init() { 31 | super.init(borderPosition: .top) 32 | } 33 | 34 | public required init?(coder aDecoder: NSCoder) { 35 | fatalError("init(coder:) has not been implemented") 36 | } 37 | 38 | open override func commonInit() { 39 | super.commonInit() 40 | insetsLayoutMarginsFromSafeArea = false 41 | layoutMargins.top = 16 42 | } 43 | 44 | // MARK: - Layout 45 | 46 | open override func layoutSubviews() { 47 | super.layoutSubviews() 48 | guard let superview = superview else { return } 49 | let contentWidth = min(440, superview.readableWidth) 50 | let horizontalMargin = (frame.width - contentWidth) / 2 51 | layoutMargins.left = horizontalMargin 52 | layoutMargins.right = horizontalMargin 53 | } 54 | } 55 | #endif 56 | -------------------------------------------------------------------------------- /Sources/NativeUIKit/Bars/ToolBar/NativeSmallActionToolBarView.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #if canImport(UIKit) && (os(iOS)) 23 | import UIKit 24 | import SparrowKit 25 | 26 | open class NativeSmallActionToolBarView: NativeMimicrateToolBarView { 27 | 28 | public let actionButton = SPDimmedButton().do { 29 | $0.applyDefaultAppearance(with: .tintedContent) 30 | $0.titleLabel?.font = UIFont.preferredFont(forTextStyle: .title3, weight: .semibold) 31 | } 32 | 33 | // MARK: - Init 34 | 35 | open override func commonInit() { 36 | super.commonInit() 37 | addSubview(actionButton) 38 | } 39 | 40 | // MARK: - Layout 41 | 42 | open override func layoutSubviews() { 43 | super.layoutSubviews() 44 | actionButton.setWidthAndFit(width: layoutWidth) 45 | actionButton.frame.origin.y = layoutMargins.top + 16 46 | actionButton.setXCenter() 47 | } 48 | 49 | open override func sizeThatFits(_ size: CGSize) -> CGSize { 50 | layoutSubviews() 51 | return .init(width: size.width, height: actionButton.frame.maxY + layoutMargins.bottom) 52 | } 53 | } 54 | #endif 55 | -------------------------------------------------------------------------------- /Sources/NativeUIKit/Collection/NativePromoCollectionViewCell.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #if canImport(UIKit) && (os(iOS)) 23 | import UIKit 24 | import SparrowKit 25 | import SPPerspective 26 | 27 | open class NativePromoCollectionViewCell: SPCollectionViewCell { 28 | 29 | // MARK: - Views 30 | 31 | public let promoView = NativePromoView() 32 | 33 | // MARK: - Init 34 | 35 | open override func commonInit() { 36 | super.commonInit() 37 | contentView.layoutMargins = .zero 38 | contentView.addSubview(promoView) 39 | } 40 | 41 | open override func prepareForReuse() { 42 | super.prepareForReuse() 43 | promoView.iconView.resetPerspective() 44 | } 45 | 46 | // MARK: - Layout 47 | 48 | open override func layoutSubviews() { 49 | super.layoutSubviews() 50 | promoView.layout(y: contentView.layoutMargins.top) 51 | } 52 | 53 | open override func sizeThatFits(_ size: CGSize) -> CGSize { 54 | let superSize = super.sizeThatFits(size) 55 | layoutSubviews() 56 | return .init(width: superSize.width, height: promoView.frame.maxY + contentView.layoutMargins.bottom) 57 | } 58 | } 59 | #endif 60 | -------------------------------------------------------------------------------- /Sources/NativeUIKit/Table/Button/NativeDiffableLeftButton.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #if canImport(UIKit) && (os(iOS)) 23 | import UIKit 24 | import SparrowKit 25 | import SPDiffable 26 | 27 | @available(iOS 13.0, *) 28 | open class NativeDiffableLeftButton: SPDiffableTableRow { 29 | 30 | open var textColor: UIColor 31 | open var detailColor: UIColor 32 | open var higlightStyle: SPTableViewCell.HiglightStyle 33 | 34 | public init( 35 | id: String? = nil, 36 | text: String, 37 | textColor: UIColor = .label, 38 | detail: String? = nil, 39 | detailColor: UIColor = .secondaryLabel, 40 | icon: UIImage? = nil, 41 | accessoryType: UITableViewCell.AccessoryType = .none, 42 | higlightStyle: SPTableViewCell.HiglightStyle = .content, 43 | action: SPDiffableTableRow.Action? = nil 44 | ) { 45 | self.textColor = textColor 46 | self.detailColor = detailColor 47 | self.higlightStyle = higlightStyle 48 | 49 | super.init( 50 | id: id, 51 | text: text, 52 | detail: detail, 53 | icon: icon, 54 | accessoryType: accessoryType, 55 | action: action 56 | ) 57 | } 58 | } 59 | #endif 60 | -------------------------------------------------------------------------------- /Sources/NativeUIKit/Controllers/Scroll/NativeHeaderController.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #if canImport(UIKit) && (os(iOS)) 23 | import UIKit 24 | import SparrowKit 25 | 26 | open class NativeHeaderController: SPScrollController { 27 | 28 | // MARK: - Views 29 | 30 | public let headerView: NativeModalHeaderView 31 | 32 | // MARK: - Init 33 | 34 | public init(image: UIImage?, title: String, subtitle: String) { 35 | self.headerView = NativeModalHeaderView(image: image, title: title, subtitle: subtitle) 36 | super.init() 37 | } 38 | 39 | public required init?(coder: NSCoder) { 40 | fatalError("init(coder:) has not been implemented") 41 | } 42 | 43 | // MARK: - Lifecycle 44 | 45 | open override func viewDidLoad() { 46 | super.viewDidLoad() 47 | scrollView.contentInset.bottom = NativeLayout.Spaces.Scroll.bottom_inset_reach_end 48 | scrollView.addSubview(headerView) 49 | } 50 | 51 | open override func viewDidLayoutSubviews() { 52 | super.viewDidLayoutSubviews() 53 | headerView.layout(y: NativeLayout.Spaces.default) 54 | scrollView.contentSize = .init(width: view.frame.width, height: headerView.frame.maxY) 55 | } 56 | } 57 | #endif 58 | -------------------------------------------------------------------------------- /Sources/NativeUIKit/Controls/NativePlayPauseCompactButton.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #if canImport(UIKit) && (os(iOS)) 23 | import UIKit 24 | import SparrowKit 25 | 26 | @available(iOS 13, *) 27 | open class NativePlayPauseCompactButton: SPDimmedButton { 28 | 29 | // MARK: - Data 30 | 31 | open var appearance: Appearance = .play { 32 | didSet { 33 | updateAppearance() 34 | } 35 | } 36 | open var playImage = UIImage.system("play.fill", font: .preferredFont(forTextStyle: .body)) 37 | open var pauseImage = UIImage.system("pause.fill", font: .preferredFont(forTextStyle: .body)) 38 | 39 | // MARK: - Init 40 | 41 | open override func commonInit() { 42 | super.commonInit() 43 | contentEdgeInsets = .init(side: 8) 44 | applyDefaultAppearance(with: .tintedContent) 45 | updateAppearance() 46 | } 47 | 48 | // MARK: - Internal 49 | 50 | internal func updateAppearance() { 51 | switch self.appearance { 52 | case .play: 53 | setImage(playImage) 54 | case .pause: 55 | setImage(pauseImage) 56 | } 57 | } 58 | 59 | // MARK: - Models 60 | 61 | public enum Appearance { 62 | 63 | case play 64 | case pause 65 | } 66 | } 67 | #endif 68 | -------------------------------------------------------------------------------- /Sources/NativeUIKit/Collection/LargeHeader/NativeLargeHeaderCollectionView.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #if canImport(UIKit) && (os(iOS)) 23 | import UIKit 24 | import SparrowKit 25 | 26 | open class NativeLargeHeaderCollectionView: SPCollectionReusableView { 27 | 28 | public let headerView = NativeLargeHeaderView() 29 | 30 | open override func commonInit() { 31 | super.commonInit() 32 | insetsLayoutMarginsFromSafeArea = false 33 | layoutMargins = .zero 34 | addSubview(headerView) 35 | } 36 | 37 | open override func layoutSubviews() { 38 | super.layoutSubviews() 39 | headerView.layout(y: layoutMargins.top) 40 | } 41 | 42 | open override func sizeThatFits(_ size: CGSize) -> CGSize { 43 | let superSize = super.sizeThatFits(size) 44 | return .init(width: superSize.width, height: headerView.frame.height + layoutMargins.bottom) 45 | } 46 | 47 | static public func size(for item: NativeLargeHeaderItem, in collectionView: UICollectionView) -> CGSize { 48 | let view = NativeLargeHeaderView() 49 | view.configure(with: item, section: .zero) 50 | view.setWidthAndFit(width: collectionView.layoutWidth) 51 | return .init(width: collectionView.frame.width, height: view.frame.height) 52 | } 53 | } 54 | #endif 55 | -------------------------------------------------------------------------------- /Assets/Readme/Elements/NativeAvatarView.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | NativeAvatarView 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Sources/NativeUIKit/Controllers/Scroll/NativeHeaderTextFieldController.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #if canImport(UIKit) && (os(iOS)) 23 | import UIKit 24 | import SparrowKit 25 | 26 | open class NativeHeaderTextFieldController: NativeHeaderController { 27 | 28 | // MARK: - Views 29 | 30 | public let textField = NativeLargeTextField().do { 31 | $0.returnKeyType = .done 32 | $0.autocapitalizationType = .words 33 | } 34 | 35 | public let footerView = NativeFooterView() 36 | 37 | // MARK: - Lifecycle 38 | 39 | open override func viewDidLoad() { 40 | super.viewDidLoad() 41 | if #available(iOS 13.0, *) { 42 | view.backgroundColor = .systemBackground 43 | } 44 | scrollView.addSubviews(textField, footerView) 45 | dismissKeyboardWhenTappedAround() 46 | } 47 | 48 | // MARK: - Layout 49 | 50 | open override func viewDidLayoutSubviews() { 51 | super.viewDidLayoutSubviews() 52 | textField.layout(y: headerView.frame.maxY + NativeLayout.Spaces.default + NativeLayout.Spaces.default_half) 53 | footerView.setWidthAndFit(width: textField.frame.width) 54 | footerView.frame.origin.x = textField.frame.origin.x 55 | footerView.frame.origin.y = textField.frame.maxY 56 | scrollView.contentSize = .init(width: view.frame.width, height: footerView.frame.maxY) 57 | } 58 | } 59 | #endif 60 | -------------------------------------------------------------------------------- /Sources/NativeUIKit/NativeLayout.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #if canImport(UIKit) && (os(iOS)) 23 | import UIKit 24 | 25 | public enum NativeLayout { 26 | 27 | public enum Sizes { 28 | 29 | public static var actionable_area_maximum_width: CGFloat { 408 } 30 | public static var not_actionable_area_maximum_width: CGFloat { 382 } 31 | } 32 | 33 | public enum Spaces { 34 | 35 | public static var step: CGFloat = 4 36 | 37 | public static var default_less: CGFloat { step * 3 } 38 | public static var `default`: CGFloat { step * 4 } 39 | public static var default_more: CGFloat { step * 5 } 40 | 41 | public static var default_half: CGFloat { self.default / 2 } 42 | public static var default_double: CGFloat { self.default * 2 } 43 | 44 | public enum Margins { 45 | 46 | public static var full_screen_horizontal: CGFloat { Spaces.default } 47 | public static var modal_screen_horizontal: CGFloat { step * 5 } 48 | } 49 | 50 | public enum Scroll { 51 | 52 | public static var top_inset_transparent_navigation: CGFloat { step * 2 } 53 | public static var bottom_inset_reach_end: CGFloat { step * 9 } 54 | public static var bottom_inset_when_keyboard_can_appear: CGFloat { step * 8 } 55 | } 56 | } 57 | } 58 | #endif 59 | -------------------------------------------------------------------------------- /Sources/NativeUIKit/Bars/ToolBar/LargeAction/NativeSkipableLargeActionToolBarView.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #if canImport(UIKit) && (os(iOS)) 23 | import UIKit 24 | import SparrowKit 25 | 26 | open class NativeSkipableLargeActionToolBarView: NativeLargeActionToolBarView { 27 | 28 | // MARK: - Views 29 | 30 | public let skipButton = SPDimmedButton().do { 31 | $0.applyDefaultAppearance(with: .init(content: .systemGray, background: .clear)) 32 | $0.titleLabel?.font = UIFont.preferredFont(forTextStyle: .body, weight: .semibold) 33 | } 34 | 35 | // MARK: - Init 36 | 37 | open override func commonInit() { 38 | super.commonInit() 39 | addSubview(skipButton) 40 | } 41 | 42 | // MARK: - Actions 43 | 44 | open override func setLoading(_ state: Bool) { 45 | super.setLoading(state) 46 | skipButton.isHidden = state 47 | } 48 | 49 | // MARK: - Layout 50 | 51 | open override func layoutSubviews() { 52 | super.layoutSubviews() 53 | skipButton.setWidthAndFit(width: layoutWidth) 54 | skipButton.frame.origin.y = actionButton.frame.maxY + 12 55 | skipButton.setXCenter() 56 | } 57 | 58 | open override func sizeThatFits(_ size: CGSize) -> CGSize { 59 | layoutSubviews() 60 | return .init(width: size.width, height: skipButton.frame.maxY + layoutMargins.bottom) 61 | } 62 | } 63 | #endif 64 | -------------------------------------------------------------------------------- /Sources/NativeUIKit/Bars/ToolBar/LargeAction/NativeLargeSmallActionToolBarView.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #if canImport(UIKit) && (os(iOS)) 23 | import UIKit 24 | import SparrowKit 25 | 26 | open class NativeLargeSmallActionToolBarView: NativeLargeActionToolBarView { 27 | 28 | // MARK: - Views 29 | 30 | public let secondActionButton = SPDimmedButton().do { 31 | $0.applyDefaultAppearance(with: .tintedContent) 32 | $0.titleLabel?.font = UIFont.preferredFont(forTextStyle: .headline, addPoints: -1) 33 | } 34 | 35 | // MARK: - Init 36 | 37 | open override func commonInit() { 38 | super.commonInit() 39 | addSubview(secondActionButton) 40 | } 41 | 42 | // MARK: - Actions 43 | 44 | open override func setLoading(_ state: Bool) { 45 | super.setLoading(state) 46 | secondActionButton.isHidden = state 47 | } 48 | 49 | // MARK: - Layout 50 | 51 | open override func layoutSubviews() { 52 | super.layoutSubviews() 53 | secondActionButton.setWidthAndFit(width: layoutWidth) 54 | secondActionButton.frame.origin.y = actionButton.frame.maxY + 12 55 | secondActionButton.setXCenter() 56 | } 57 | 58 | open override func sizeThatFits(_ size: CGSize) -> CGSize { 59 | layoutSubviews() 60 | return .init(width: size.width, height: secondActionButton.frame.maxY + layoutMargins.bottom) 61 | } 62 | } 63 | #endif 64 | -------------------------------------------------------------------------------- /Sources/NativeUIKit/Controllers/Scroll/NativeHeaderFullWidthImageController.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #if canImport(UIKit) && (os(iOS)) 23 | import UIKit 24 | import SparrowKit 25 | 26 | @available(iOS 13.0, *) 27 | open class NativeHeaderFullWidthImageController: SPScrollController { 28 | 29 | // MARK: - Views 30 | 31 | public let headerImageView = SPImageView().do { 32 | $0.backgroundColor = .systemGray.alpha(0.1) 33 | $0.contentMode = .scaleAspectFill 34 | } 35 | 36 | public let titlesView = NativeModalHeaderView() 37 | 38 | // MARK: - Lifecycle 39 | 40 | open override func viewDidLoad() { 41 | super.viewDidLoad() 42 | view.backgroundColor = .secondarySystemBackground 43 | scrollView.showsVerticalScrollIndicator = false 44 | scrollView.addSubviews([headerImageView, titlesView]) 45 | navigationController?.navigationBar.setAppearance(.transparentAlways) 46 | } 47 | 48 | // MARK: - Layout 49 | 50 | open override func viewDidLayoutSubviews() { 51 | super.viewDidLayoutSubviews() 52 | headerImageView.frame = .init(x: .zero, y: -scrollView.layoutMargins.top, width: view.frame.width, height: view.frame.height * 0.45) 53 | titlesView.layout(y: headerImageView.frame.maxY + NativeLayout.Spaces.default_double) 54 | scrollView.contentSize = .init( 55 | width: view.frame.width, 56 | height: titlesView.frame.maxY + NativeLayout.Spaces.default_double 57 | ) 58 | } 59 | } 60 | #endif 61 | -------------------------------------------------------------------------------- /Sources/NativeUIKit/Labels/NativeFooterView.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #if canImport(UIKit) && (os(iOS)) 23 | import UIKit 24 | import SparrowKit 25 | 26 | open class NativeFooterView: SPView { 27 | 28 | // MARK: - Views 29 | 30 | public let label = SPLabel().do { 31 | $0.font = .preferredFont(forTextStyle: .footnote, addPoints: 1) 32 | $0.numberOfLines = .zero 33 | if #available(iOS 13.0, *) { 34 | $0.textColor = .secondaryLabel 35 | } else { 36 | $0.textColor = .gray 37 | } 38 | } 39 | 40 | public override init() { 41 | super.init() 42 | } 43 | 44 | public init(text: String) { 45 | super.init() 46 | label.text = text 47 | } 48 | 49 | required public init?(coder aDecoder: NSCoder) { 50 | fatalError("init(coder:) has not been implemented") 51 | } 52 | 53 | open override func commonInit() { 54 | super.commonInit() 55 | layoutMargins = .init(top: NativeLayout.Spaces.default_half, left: 16, bottom: NativeLayout.Spaces.default_half, right: .zero) 56 | addSubview(label) 57 | } 58 | 59 | open override func layoutSubviews() { 60 | super.layoutSubviews() 61 | label.layoutDynamicHeight(x: layoutMargins.left, y: layoutMargins.top, width: layoutWidth) 62 | } 63 | 64 | open override func sizeThatFits(_ size: CGSize) -> CGSize { 65 | layoutSubviews() 66 | return .init(width: size.width, height: label.frame.maxY + layoutMargins.bottom) 67 | } 68 | } 69 | #endif 70 | -------------------------------------------------------------------------------- /Sources/NativeUIKit/Controllers/Table/Header/NativeHeaderTableController+HeaderContainerView.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #if canImport(UIKit) && (os(iOS)) 23 | import UIKit 24 | import SparrowKit 25 | import SPDiffable 26 | 27 | @available(iOS 13.0, *) 28 | extension NativeHeaderTableController { 29 | 30 | open class HeaderContainerView: SPView { 31 | 32 | // MARK: - Public 33 | 34 | public let contentView: UIView 35 | 36 | // MARK: - Init 37 | 38 | public init(contentView: UIView) { 39 | self.contentView = contentView 40 | super.init() 41 | } 42 | 43 | public required init?(coder aDecoder: NSCoder) { 44 | fatalError("init(coder:) has not been implemented") 45 | } 46 | 47 | public override func commonInit() { 48 | super.commonInit() 49 | insetsLayoutMarginsFromSafeArea = false 50 | contentView.insetsLayoutMarginsFromSafeArea = false 51 | layoutMargins = .zero 52 | addSubview(contentView) 53 | } 54 | 55 | // MARK: - Layout 56 | 57 | public override func layoutSubviews() { 58 | super.layoutSubviews() 59 | contentView.setWidthAndFit(width: layoutWidth) 60 | contentView.frame.origin.x = layoutMargins.left 61 | contentView.frame.origin.y = layoutMargins.top 62 | } 63 | 64 | public override func sizeThatFits(_ size: CGSize) -> CGSize { 65 | frame.setWidth(size.width) 66 | layoutSubviews() 67 | return .init(width: size.width, height: contentView.frame.maxY + layoutMargins.bottom) 68 | } 69 | } 70 | } 71 | #endif 72 | -------------------------------------------------------------------------------- /Sources/NativeUIKit/Bars/NativeMimicrateBarView.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #if canImport(UIKit) && (os(iOS)) 23 | import UIKit 24 | import SparrowKit 25 | 26 | /** 27 | NativeUIKit: View with mimicrate to navigation and toolbar views. 28 | */ 29 | open class NativeMimicrateBarView: SPView { 30 | 31 | // MARK: - Views 32 | 33 | public let borderedView: NativeBorderedView 34 | public let backgroundView: UIVisualEffectView 35 | 36 | // MARK: - Init 37 | 38 | public init(borderPosition: NativeBorderedView.Position) { 39 | self.borderedView = NativeBorderedView(position: borderPosition) 40 | self.backgroundView = UIVisualEffectView.init(style: .regular) 41 | super.init() 42 | } 43 | 44 | public required init?(coder aDecoder: NSCoder) { 45 | fatalError("init(coder:) has not been implemented") 46 | } 47 | 48 | open override func commonInit() { 49 | super.commonInit() 50 | addSubview(backgroundView) 51 | addSubview(borderedView) 52 | setVisible(progress: 1) 53 | } 54 | 55 | // MARK: - Actions 56 | 57 | /** 58 | NativeUIKit: Show or hide bar. 59 | 60 | - parameter progress: Value in range from `0` to `1` to visible or not view. 61 | */ 62 | open func setVisible(progress: CGFloat) { 63 | var alpha: CGFloat = progress 64 | if progress < 0 { alpha = 0 } 65 | if progress > 1 { alpha = 1 } 66 | self.borderedView.alpha = alpha 67 | self.backgroundView.alpha = alpha 68 | } 69 | 70 | // MARK: - Layout 71 | 72 | open override func layoutSubviews() { 73 | super.layoutSubviews() 74 | backgroundView.setEqualSuperviewBounds() 75 | borderedView.setEqualSuperviewBounds() 76 | } 77 | } 78 | #endif 79 | -------------------------------------------------------------------------------- /Sources/NativeUIKit/Controllers/Complex/Profile/NativeAvatarHeaderView.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #if canImport(UIKit) && (os(iOS)) 23 | import UIKit 24 | import SparrowKit 25 | 26 | @available(iOS 13.0, *) 27 | open class NativeAvatarHeaderView: SPView { 28 | 29 | // MARK: - Public 30 | 31 | public let avatarView = NativeAvatarView().do { 32 | $0.avatarAppearance = .placeholder 33 | $0.isEditable = true 34 | } 35 | 36 | // MARK: - Private 37 | 38 | private var extendView = SPView() 39 | 40 | // MARK: - Init 41 | 42 | open override func commonInit() { 43 | super.commonInit() 44 | backgroundColor = .clear 45 | addSubviews([extendView, avatarView]) 46 | 47 | layoutMargins = .init( 48 | top: NativeLayout.Spaces.default, 49 | left: NativeLayout.Spaces.default_double, 50 | bottom: NativeLayout.Spaces.default_more, 51 | right: NativeLayout.Spaces.default_double 52 | ) 53 | } 54 | 55 | // MARK: - Ovveride 56 | 57 | open override var backgroundColor: UIColor? { 58 | didSet { 59 | extendView.backgroundColor = backgroundColor 60 | } 61 | } 62 | 63 | // MARK: - Layout 64 | 65 | open override func layoutSubviews() { 66 | super.layoutSubviews() 67 | extendView.frame = .init(x: .zero, maxY: .zero, width: frame.width, height: 1000) 68 | 69 | avatarView.sizeToFit() 70 | avatarView.setXCenter() 71 | avatarView.frame.origin.y = layoutMargins.top 72 | } 73 | 74 | open override func sizeThatFits(_ size: CGSize) -> CGSize { 75 | layoutSubviews() 76 | return .init(width: frame.width, height: avatarView.frame.maxY + layoutMargins.bottom) 77 | } 78 | } 79 | #endif 80 | -------------------------------------------------------------------------------- /Sources/NativeUIKit/Bars/ToolBar/NativeAppleAuthToolBarView.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #if canImport(UIKit) && canImport(AuthenticationServices) && (os(iOS)) 23 | import UIKit 24 | import SparrowKit 25 | import AuthenticationServices 26 | 27 | @available(iOS 13.0, *) 28 | open class NativeAppleAuthToolBarView: NativeMimicrateToolBarView { 29 | 30 | // MARK: - Views 31 | 32 | public let authButton = ASAuthorizationAppleIDButton() 33 | 34 | public let footerLabel = SPLabel().do { 35 | $0.font = .preferredFont(forTextStyle: .footnote) 36 | $0.numberOfLines = .zero 37 | $0.textColor = .secondaryLabel 38 | } 39 | 40 | // MARK: - Init 41 | 42 | open override func commonInit() { 43 | super.commonInit() 44 | addSubview(authButton) 45 | addSubview(footerLabel) 46 | } 47 | 48 | // MARK: - Layout 49 | 50 | internal let footerLeftInset: CGFloat = 20 51 | 52 | open override func layoutSubviews() { 53 | super.layoutSubviews() 54 | 55 | let authButtonWidth = min(readableWidth, NativeLayout.Sizes.actionable_area_maximum_width) 56 | authButton.frame.setWidth(authButtonWidth) 57 | authButton.frame.setHeight(52) 58 | authButton.setXCenter() 59 | authButton.frame.origin.y = layoutMargins.top 60 | 61 | if footerLabel.text != nil { 62 | footerLabel.layoutDynamicHeight(x: layoutMargins.left + footerLeftInset, y: authButton.frame.maxY + 12, width: layoutWidth - footerLeftInset) 63 | } 64 | } 65 | 66 | open override func sizeThatFits(_ size: CGSize) -> CGSize { 67 | layoutSubviews() 68 | if footerLabel.text == nil { 69 | return .init(width: size.width, height: authButton.frame.maxY + layoutMargins.bottom) 70 | } else { 71 | return .init(width: size.width, height: footerLabel.frame.maxY + layoutMargins.bottom) 72 | } 73 | } 74 | } 75 | #endif 76 | -------------------------------------------------------------------------------- /Sources/NativeUIKit/Controls/NativeLargeTextField.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #if canImport(UIKit) && (os(iOS)) 23 | import UIKit 24 | import SparrowKit 25 | 26 | open class NativeLargeTextField: SPInsetsTextField { 27 | 28 | // MARK: - Init 29 | 30 | open override func commonInit() { 31 | super.commonInit() 32 | if #available(iOS 13.0, *) { 33 | backgroundColor = .secondarySystemBackground 34 | } else { 35 | backgroundColor = .white 36 | } 37 | layer.cornerRadius = NativeAppearance.Corners.readable_area 38 | textAlignment = .center 39 | clearButtonMode = .whileEditing 40 | contentMode = .scaleAspectFit 41 | font = .preferredFont(forTextStyle: .title2, weight: .bold, addPoints: .zero) 42 | adjustsFontSizeToFitWidth = true 43 | minimumFontSize = 16 44 | insets = .init(horizontal: 48, vertical: 14) 45 | } 46 | 47 | // MARK: - Lifecycle 48 | 49 | open override func tintColorDidChange() { 50 | super.tintColorDidChange() 51 | textColor = tintColor 52 | } 53 | 54 | // MARK: - Layout 55 | 56 | /** 57 | NativeUIKit: Layout wrapper. Native way for layout button. 58 | */ 59 | open func layout(y: CGFloat) { 60 | guard let superview = self.superview else { return } 61 | sizeToFit() 62 | let width = min(superview.readableWidth, NativeLayout.Sizes.actionable_area_maximum_width) 63 | frame.setWidth(width) 64 | setXCenter() 65 | frame.origin.y = y 66 | } 67 | 68 | /** 69 | NativeUIKit: Layout wrapper. Native way for layout button. 70 | */ 71 | open func layout(maxY: CGFloat) { 72 | guard let superview = self.superview else { return } 73 | sizeToFit() 74 | let width = min(superview.readableWidth, NativeLayout.Sizes.actionable_area_maximum_width) 75 | frame.setWidth(width) 76 | setXCenter() 77 | frame.setMaxY(maxY) 78 | } 79 | } 80 | #endif 81 | -------------------------------------------------------------------------------- /Example App/NativeUIKit.xcodeproj/xcshareddata/xcschemes/iOS Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 45 | 51 | 52 | 53 | 54 | 60 | 62 | 68 | 69 | 70 | 71 | 73 | 74 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /Sources/NativeUIKit/Controllers/Table/Header/NativeHeaderTableController.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #if canImport(UIKit) && (os(iOS)) 23 | import UIKit 24 | import SparrowKit 25 | import SPDiffable 26 | 27 | @available(iOS 13.0, *) 28 | open class NativeHeaderTableController: SPDiffableTableController { 29 | 30 | // MARK: - Public 31 | 32 | open var headerContainerView: HeaderContainerView 33 | 34 | // MARK: - Init 35 | 36 | public init(style: UITableView.Style, headerView: UIView) { 37 | self.headerContainerView = HeaderContainerView(contentView: headerView) 38 | super.init(style: style) 39 | } 40 | 41 | public required init?(coder: NSCoder) { 42 | fatalError("init(coder:) has not been implemented") 43 | } 44 | 45 | // MARK: - Lifecycle 46 | 47 | open override func viewDidLoad() { 48 | super.viewDidLoad() 49 | headerContainerView.setWidthAndFit(width: view.frame.width) 50 | tableView.tableHeaderView = headerContainerView 51 | } 52 | 53 | // MARK: - Layout 54 | 55 | open override func viewDidLayoutSubviews() { 56 | super.viewDidLayoutSubviews() 57 | headerContainerView.contentView.layoutMargins.left = tableView.layoutMargins.left 58 | headerContainerView.contentView.layoutMargins.right = tableView.layoutMargins.right 59 | headerContainerView.setWidthAndFit(width: view.frame.width) 60 | if cachedHeaderHeight != headerContainerView.frame.height { 61 | cachedHeaderHeight = headerContainerView.frame.height 62 | DispatchQueue.main.async { [weak self] in 63 | guard let self = self else { return } 64 | self.diffableDataSource?.updateLayout(animated: true, completion: nil) 65 | } 66 | } 67 | } 68 | 69 | private var cachedHeaderHeight: CGFloat? = nil 70 | 71 | // MARK: - Public 72 | 73 | open func setSpaceBetweenHeaderAndCells(_ value: CGFloat) { 74 | headerContainerView.layoutMargins.bottom = value 75 | } 76 | } 77 | #endif 78 | -------------------------------------------------------------------------------- /Sources/NativeUIKit/Controllers/Navigation/NativeNavigationController.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #if canImport(UIKit) && (os(iOS)) 23 | import UIKit 24 | import SparrowKit 25 | 26 | open class NativeNavigationController: SPNavigationController { 27 | 28 | // MARK: - Views 29 | 30 | open var mimicrateToolBarView: NativeMimicrateToolBarView? = nil { 31 | willSet { 32 | if let toolBarView = mimicrateToolBarView { 33 | toolBarView.removeFromSuperview() 34 | } 35 | } 36 | didSet { 37 | if let toolBarView = mimicrateToolBarView { 38 | view.addSubview(toolBarView) 39 | } 40 | } 41 | } 42 | 43 | open override func setToolbarHidden(_ hidden: Bool, animated: Bool) { 44 | if let barView = mimicrateToolBarView { 45 | if animated { 46 | 47 | if barView.isHidden && !hidden { 48 | barView.isHidden = false 49 | barView.alpha = .zero 50 | } 51 | 52 | UIView.animate(withDuration: 0.12, delay: .zero, options: [.beginFromCurrentState, .curveEaseInOut], animations: { 53 | barView.alpha = hidden ? .zero : 1 54 | }) { completed in 55 | barView.isHidden = hidden 56 | } 57 | } else { 58 | barView.isHidden = hidden 59 | } 60 | } else { 61 | super.setToolbarHidden(hidden, animated: animated) 62 | } 63 | } 64 | 65 | // MARK: - Layout 66 | 67 | open override func viewDidLayoutSubviews() { 68 | super.viewDidLayoutSubviews() 69 | if let toolBarView = mimicrateToolBarView { 70 | toolBarView.layoutMargins.bottom = systemSafeAreaInsets.bottom + 16 71 | toolBarView.setWidthAndFit(width: view.frame.width) 72 | toolBarView.frame.setMaxY(view.frame.height) 73 | let toolBarFrameFitHeight = toolBarView.frame.height - systemSafeAreaInsets.bottom 74 | if additionalSafeAreaInsets.bottom != toolBarFrameFitHeight { 75 | additionalSafeAreaInsets.bottom = toolBarFrameFitHeight 76 | } 77 | } 78 | } 79 | } 80 | #endif 81 | -------------------------------------------------------------------------------- /Sources/NativeUIKit/Controllers/Complex/Onboarding/Actinos/NativeOnboardingActionsController.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #if canImport(UIKit) && (os(iOS)) 23 | import UIKit 24 | import SparrowKit 25 | 26 | open class NativeOnboardingActionsController: NativeHeaderController { 27 | 28 | private var views: [NativeOnbiardingActionButton] = [] 29 | 30 | // MARK: - Init 31 | 32 | public init( 33 | iconImage: UIImage?, 34 | title: String, 35 | subtitle: String 36 | ) { 37 | super.init(image: iconImage, title: title, subtitle: subtitle) 38 | } 39 | 40 | public required init?(coder: NSCoder) { 41 | fatalError("init(coder:) has not been implemented") 42 | } 43 | 44 | // MARK: - Lifecycle 45 | 46 | open override func viewDidLoad() { 47 | super.viewDidLoad() 48 | if #available(iOS 13.0, *) { 49 | self.view.backgroundColor = .systemBackground 50 | } else { 51 | view.backgroundColor = .white 52 | } 53 | scrollView.addSubviews(views) 54 | } 55 | 56 | open func setActions(_ models: [NativeOnbiardingActionButton.ActionModel]) { 57 | // Clean old 58 | views.forEach({ $0.removeFromSuperview() }) 59 | views.removeAll() 60 | 61 | // Add new 62 | views = models.map({ NativeOnbiardingActionButton(with: $0) }) 63 | 64 | // Added like subviews 65 | if isViewLoaded { 66 | scrollView.addSubviews(views) 67 | } 68 | } 69 | 70 | // MARK: - Layout 71 | 72 | open override func viewDidLayoutSubviews() { 73 | super.viewDidLayoutSubviews() 74 | var currentYPosition = headerView.frame.maxY + NativeLayout.Spaces.default_double 75 | let elementWidth: CGFloat = min(scrollView.readableWidth, 320) 76 | for (_, itemView) in views.enumerated() { 77 | itemView.setWidthAndFit(width: elementWidth) 78 | itemView.setXCenter() 79 | itemView.frame.origin.y = currentYPosition 80 | currentYPosition = itemView.frame.maxY + NativeLayout.Spaces.default_half 81 | } 82 | scrollView.contentSize = .init(width: scrollView.frame.width, height: views.last?.frame.maxY ?? .zero) 83 | } 84 | } 85 | #endif 86 | -------------------------------------------------------------------------------- /Sources/NativeUIKit/Controls/Buttons/NativeSmallActionButton.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #if canImport(UIKit) && (os(iOS)) 23 | import UIKit 24 | import SparrowKit 25 | 26 | /** 27 | NativeUIKit: Small action button. 28 | Using inside cells. 29 | */ 30 | open class NativeSmallActionButton: SPDimmedButton { 31 | 32 | // MARK: - Init 33 | 34 | open override func commonInit() { 35 | super.commonInit() 36 | titleLabel?.font = UIFont.preferredFont(forTextStyle: .subheadline, weight: .bold, addPoints: -1) 37 | titleLabel?.numberOfLines = 1 38 | titleImageInset = 6 39 | contentEdgeInsets = .init(horizontal: 10, vertical: 6) 40 | } 41 | 42 | // MARK: - Public 43 | 44 | /** 45 | NativeUIKit: Wrapper of set content and color of button. 46 | 47 | - parameter title: Text which using like title. 48 | - parameter icon: Object of `UIImage`, using like icon. Usually Apple doesn't use icon in this button. 49 | - parameter colorise: Color of button in default state. 50 | */ 51 | public func set(title: String, icon: UIImage? = nil, colorise: SPDimmedButton.Colorise) { 52 | setTitle(title) 53 | if let icon = icon { 54 | setImage(icon.alwaysTemplate) 55 | } 56 | applyDefaultAppearance(with: colorise) 57 | } 58 | 59 | // MARK: - Layout 60 | 61 | open override func layoutSubviews() { 62 | super.layoutSubviews() 63 | roundMinimumSide() 64 | } 65 | 66 | open override func sizeThatFits(_ size: CGSize) -> CGSize { 67 | let superSize = super.sizeThatFits(size) 68 | var width = superSize.width 69 | let minimumWidth: CGFloat = 70 70 | if width < minimumWidth { width = minimumWidth } 71 | 72 | var height = superSize.height 73 | if let titleLabel = titleLabel, let imageView = imageView, let _ = imageView.image { 74 | if titleLabel.frame.height > .zero && imageView.frame.height > .zero { 75 | let imageCorrection = imageView.frame.height - titleLabel.frame.height 76 | height -= imageCorrection 77 | } 78 | } 79 | 80 | return CGSize(width: width, height: height) 81 | } 82 | 83 | // MARK: - Ovveride 84 | 85 | open override func setTitle(_ title: String?, for state: UIControl.State) { 86 | super.setTitle(title, for: state) 87 | } 88 | } 89 | #endif 90 | -------------------------------------------------------------------------------- /Sources/NativeUIKit/Controls/Buttons/NativeMediumActionButton.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #if canImport(UIKit) && (os(iOS)) 23 | import UIKit 24 | import SparrowKit 25 | 26 | /** 27 | NativeUIKit: Medium action button. 28 | You see it in Apply Music play and shuffle buttons. 29 | */ 30 | open class NativeMediumActionButton: SPDimmedButton { 31 | 32 | // MARK: - Init 33 | 34 | open override func commonInit() { 35 | super.commonInit() 36 | titleLabel?.font = UIFont.preferredFont(forTextStyle: .body, weight: .semibold, addPoints: .zero) 37 | titleLabel?.numberOfLines = 1 38 | titleImageInset = 8 39 | contentEdgeInsets = .init(horizontal: 15, vertical: 15) 40 | roundCorners(radius: 12) 41 | } 42 | 43 | // MARK: - Public 44 | 45 | /** 46 | NativeUIKit: Wrapper of set content and color of button. 47 | 48 | - parameter title: Text which using like title. 49 | - parameter icon: Object of `UIImage`, using like icon. Usually Apple doesn't use icon in this button. 50 | - parameter colorise: Color of button in default state. 51 | */ 52 | public func set(title: String, icon: UIImage? = nil, colorise: SPDimmedButton.Colorise) { 53 | setTitle(title) 54 | if let icon = icon { 55 | setImage(icon.alwaysTemplate) 56 | } 57 | applyDefaultAppearance(with: colorise) 58 | } 59 | 60 | open override func sizeThatFits(_ size: CGSize) -> CGSize { 61 | let superSize = super.sizeThatFits(size) 62 | let width = superSize.width 63 | 64 | var height = superSize.height 65 | if let titleLabel = titleLabel, let imageView = imageView, let _ = imageView.image { 66 | if titleLabel.frame.height > .zero && imageView.frame.height > .zero { 67 | let imageCorrection = imageView.frame.height - titleLabel.frame.height 68 | height -= imageCorrection 69 | } 70 | } 71 | 72 | return CGSize(width: width, height: height) 73 | } 74 | 75 | // MARK: - Wrappers 76 | 77 | public static var defaultCornerRadius: CGFloat { 78 | let button = NativeMediumActionButton() 79 | return button.layer.cornerRadius 80 | } 81 | 82 | public static var defaultHeight: CGFloat { 83 | let button = NativeMediumActionButton() 84 | button.setTitle(.space) 85 | button.sizeToFit() 86 | return button.frame.height 87 | } 88 | } 89 | #endif 90 | -------------------------------------------------------------------------------- /Sources/NativeUIKit/Views/NativeLargeHeaderView.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2020 Ivan Vorobei (hello@ivanvorobei.io) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #if canImport(UIKit) && (os(iOS)) 23 | import UIKit 24 | import SparrowKit 25 | import SPDiffable 26 | 27 | open class NativeLargeHeaderView: SPView { 28 | 29 | // MARK: - Views 30 | 31 | public let titleLabel = SPLabel().do { 32 | $0.font = UIFont.preferredFont(forTextStyle: .title2, weight: .semibold) 33 | if #available(iOS 13.0, *) { 34 | $0.textColor = .label 35 | } else { 36 | $0.textColor = .black 37 | } 38 | } 39 | 40 | public let button = SPDimmedButton().do { 41 | $0.applyDefaultAppearance(with: .tintedContent) 42 | } 43 | 44 | // MARK: - Init 45 | 46 | open override func commonInit() { 47 | super.commonInit() 48 | layoutMargins = .init(top: NativeLayout.Spaces.default_double, left: .zero, bottom: 14, right: .zero) 49 | insetsLayoutMarginsFromSafeArea = false 50 | preservesSuperviewLayoutMargins = false 51 | addSubviews(titleLabel, button) 52 | } 53 | 54 | // MARK: - Layout 55 | 56 | open func layout(y: CGFloat) { 57 | guard let superview = self.superview else { return } 58 | setWidthAndFit(width: superview.layoutWidth) 59 | setXCenter() 60 | frame.origin.y = y 61 | } 62 | 63 | open override func layoutSubviews() { 64 | super.layoutSubviews() 65 | titleLabel.layoutDynamicHeight( 66 | x: layoutMargins.left, 67 | y: layoutMargins.top, 68 | width: layoutWidth 69 | ) 70 | button.sizeToFit() 71 | button.setMaxXToSuperviewRightMargin() 72 | button.center.y = titleLabel.center.y 73 | } 74 | 75 | open override func sizeThatFits(_ size: CGSize) -> CGSize { 76 | let superSize = super.sizeThatFits(size) 77 | layoutSubviews() 78 | return .init(width: superSize.width, height: titleLabel.frame.maxY + layoutMargins.bottom) 79 | } 80 | 81 | // MARK: - Private 82 | 83 | var diffableButtonAction: (()->Void)? = nil { 84 | didSet { 85 | if let _ = diffableButtonAction { 86 | self.button.addTarget(self, action: #selector(self.diffableButtonTargetAction), for: .touchUpInside) 87 | } else { 88 | self.button.removeTargetsAndActions() 89 | } 90 | } 91 | } 92 | 93 | @objc func diffableButtonTargetAction() { 94 | self.diffableButtonAction?() 95 | } 96 | } 97 | #endif 98 | -------------------------------------------------------------------------------- /Sources/NativeUIKit/Controllers/Complex/Onboarding/NativeOnboardingController.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #if canImport(UIKit) && (os(iOS)) 23 | import UIKit 24 | import SparrowKit 25 | import SPPageController 26 | 27 | open class NativeOnboardingController: SPPageController, NativeOnboardingManagerDelegate { 28 | 29 | private let controllers: [UIViewController] 30 | private let completion: SPCompletion 31 | 32 | // MARK: - Init 33 | 34 | public init(controllers: [UIViewController], completion: @escaping SPCompletion) { 35 | 36 | self.controllers = controllers 37 | self.completion = completion 38 | 39 | var childControllers: [UIViewController] = [] 40 | for controller in controllers { 41 | let navigationController = NativeNavigationController(rootViewController: controller) 42 | navigationController.inheritLayoutMarginsForСhilds = true 43 | navigationController.inheritLayoutMarginsForNavigationBar = true 44 | navigationController.view.preservesSuperviewLayoutMargins = true 45 | childControllers.append(navigationController) 46 | } 47 | 48 | super.init(childControllers: childControllers, system: .page) 49 | 50 | controllers.forEach { controller in 51 | let onboardingChildController = controller as! NativeOnboardingChildInterface 52 | onboardingChildController.onboardingManagerDelegate = self 53 | } 54 | 55 | allowScroll = false 56 | allowDismissWithGester = false 57 | } 58 | 59 | public required init?(coder: NSCoder) { 60 | fatalError("init(coder:) has not been implemented") 61 | } 62 | 63 | // MARK: - OnboardingManagerDelegate 64 | 65 | public func onboardingActionComplete(for controller: UIViewController) { 66 | guard let index = controllers.firstIndex(of: controller) else { return } 67 | if index == (controllers.count - 1) { 68 | dismiss(animated: true) { [weak self] in 69 | self?.completion() 70 | } 71 | } else { 72 | safeScrollTo(index: index + 1, animated: true) 73 | } 74 | } 75 | } 76 | 77 | public protocol NativeOnboardingManagerDelegate: AnyObject { 78 | 79 | func onboardingActionComplete(for controller: UIViewController) 80 | } 81 | 82 | public protocol NativeOnboardingChildInterface: AnyObject { 83 | 84 | var onboardingManagerDelegate: NativeOnboardingManagerDelegate? { get set } 85 | } 86 | #endif 87 | -------------------------------------------------------------------------------- /Sources/NativeUIKit/Bars/NativeBorderedView.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #if canImport(UIKit) && (os(iOS)) 23 | import UIKit 24 | import SparrowKit 25 | 26 | /** 27 | NativeUIKit: View with porder on some position. 28 | For change position check property. 29 | */ 30 | open class NativeBorderedView: SPView { 31 | 32 | // MARK: - Data 33 | 34 | /** 35 | NativeUIKit: Border Position. 36 | */ 37 | open var position: Position = .top { 38 | didSet { 39 | layoutSubviews() 40 | } 41 | } 42 | 43 | // MARK: - Views 44 | 45 | private let borderView = SPView().do { 46 | if #available(iOS 13.0, *) { 47 | $0.backgroundColor = .separator 48 | } else { 49 | $0.backgroundColor = .systemGray 50 | } 51 | } 52 | 53 | // MARK: - Init 54 | 55 | public init(position: Position) { 56 | self.position = position 57 | super.init() 58 | } 59 | 60 | public required init?(coder aDecoder: NSCoder) { 61 | super.init(coder: aDecoder) 62 | } 63 | 64 | open override func commonInit() { 65 | super.commonInit() 66 | addSubview(borderView) 67 | } 68 | 69 | // MARK: - Actions 70 | 71 | /** 72 | NativeUIKit: Show or hide border. 73 | 74 | - parameter visible: New visible state of border. 75 | - parameter animated: State for apply changes animatable or not. 76 | */ 77 | open func setBorderVisible(_ visible: Bool, animated: Bool) { 78 | let work = { [weak self] in 79 | guard let self = self else { return } 80 | self.borderView.alpha = visible ? 1 : 0 81 | } 82 | if animated { 83 | UIView.animate(withDuration: 0.3, delay: 0, options: [.beginFromCurrentState, .allowUserInteraction], animations: { 84 | work() 85 | }) 86 | } else { 87 | work() 88 | } 89 | } 90 | 91 | // MARK: - Layout 92 | 93 | open override func layoutSubviews() { 94 | super.layoutSubviews() 95 | borderView.frame.setWidth(frame.width) 96 | borderView.frame.setHeight(0.5) 97 | borderView.frame.origin.x = 0 98 | switch position { 99 | case .top: 100 | borderView.frame.origin.y = 0 101 | case .bottom: 102 | borderView.frame.setMaxY(frame.height) 103 | } 104 | } 105 | 106 | // MARK: - Models 107 | 108 | public enum Position { 109 | 110 | case top 111 | case bottom 112 | } 113 | } 114 | #endif 115 | -------------------------------------------------------------------------------- /Sources/NativeUIKit/Bars/ToolBar/LargeAction/NativeLargeActionToolBarView.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #if canImport(UIKit) && (os(iOS)) 23 | import UIKit 24 | import SparrowKit 25 | 26 | open class NativeLargeActionToolBarView: NativeMimicrateToolBarView { 27 | 28 | // MARK: - Views 29 | 30 | public let activityIndicatorView = UIActivityIndicatorView() 31 | 32 | public let actionButton = NativeLargeActionButton().do { 33 | $0.applyDefaultAppearance(with: .tintedColorful) 34 | } 35 | 36 | public let footerLabel = SPLabel().do { 37 | $0.font = .preferredFont(forTextStyle: .footnote) 38 | $0.numberOfLines = .zero 39 | if #available(iOS 13.0, *) { 40 | $0.textColor = .secondaryLabel 41 | } else { 42 | $0.textColor = .black.alpha(0.5) 43 | } 44 | } 45 | 46 | // MARK: - Init 47 | 48 | open override func commonInit() { 49 | super.commonInit() 50 | addSubview(activityIndicatorView) 51 | addSubview(actionButton) 52 | addSubview(footerLabel) 53 | } 54 | 55 | // MARK: - Actions 56 | 57 | open func setLoading(_ state: Bool) { 58 | if state { 59 | activityIndicatorView.startAnimating() 60 | actionButton.isHidden = true 61 | footerLabel.isHidden = true 62 | } else { 63 | activityIndicatorView.stopAnimating() 64 | actionButton.isHidden = false 65 | footerLabel.isHidden = false 66 | } 67 | } 68 | 69 | // MARK: - Layout 70 | 71 | internal let footerLeftInset: CGFloat = 20 72 | 73 | open override func layoutSubviews() { 74 | super.layoutSubviews() 75 | actionButton.layout(y: layoutMargins.top) 76 | if footerLabel.text != nil { 77 | footerLabel.layoutDynamicHeight(x: layoutMargins.left + footerLeftInset, y: actionButton.frame.maxY + 12, width: layoutWidth - footerLeftInset) 78 | } 79 | 80 | let contentHeight: CGFloat = { 81 | if footerLabel.text == nil { 82 | return actionButton.frame.maxY 83 | } else { 84 | return footerLabel.frame.maxY 85 | } 86 | }() 87 | activityIndicatorView.setXCenter() 88 | activityIndicatorView.center.y = contentHeight / 2 89 | } 90 | 91 | open override func sizeThatFits(_ size: CGSize) -> CGSize { 92 | layoutSubviews() 93 | if footerLabel.text == nil { 94 | return .init(width: size.width, height: actionButton.frame.maxY + layoutMargins.bottom) 95 | } else { 96 | return .init(width: size.width, height: footerLabel.frame.maxY + layoutMargins.bottom) 97 | } 98 | } 99 | } 100 | #endif 101 | -------------------------------------------------------------------------------- /Sources/NativeUIKit/Controllers/Complex/Onboarding/Features/NativeOnboardingFeaturesController.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #if canImport(UIKit) && (os(iOS)) 23 | import UIKit 24 | import SparrowKit 25 | 26 | open class NativeOnboardingFeaturesController: NativeHeaderController { 27 | 28 | private var views: [NativeOnboardingFeatureView] = [] 29 | 30 | // MARK: - Init 31 | 32 | public init( 33 | iconImage: UIImage?, 34 | title: String, 35 | subtitle: String 36 | ) { 37 | super.init(image: iconImage, title: title, subtitle: subtitle) 38 | } 39 | 40 | public required init?(coder: NSCoder) { 41 | fatalError("init(coder:) has not been implemented") 42 | } 43 | 44 | // MARK: - Lifecycle 45 | 46 | open override func viewDidLoad() { 47 | super.viewDidLoad() 48 | if #available(iOS 13.0, *) { 49 | self.view.backgroundColor = .systemBackground 50 | } else { 51 | view.backgroundColor = .white 52 | } 53 | scrollView.addSubviews(views) 54 | } 55 | 56 | open func setFeatures(_ models: [NativeOnboardingFeatureView.FeatureModel]) { 57 | // Clean old 58 | views.forEach({ $0.removeFromSuperview() }) 59 | views.removeAll() 60 | 61 | // Add new 62 | views = models.map({ NativeOnboardingFeatureView(with: $0) }) 63 | 64 | // Added like subviews 65 | if isViewLoaded { 66 | scrollView.addSubviews(views) 67 | } 68 | } 69 | 70 | // MARK: - Layout 71 | 72 | open override func viewDidLayoutSubviews() { 73 | super.viewDidLayoutSubviews() 74 | var currentYPosition = headerView.frame.maxY + NativeLayout.Spaces.default_double 75 | for (_, itemView) in views.enumerated() { 76 | itemView.setWidthAndFit(width: scrollView.readableWidth) 77 | itemView.setXCenter() 78 | itemView.frame.origin.y = currentYPosition 79 | currentYPosition = itemView.frame.maxY + NativeLayout.Spaces.default_half 80 | } 81 | scrollView.contentSize = .init(width: scrollView.frame.width, height: views.last?.frame.maxY ?? .zero) 82 | } 83 | 84 | public func scrollViewDidScroll(_ scrollView: UIScrollView) { 85 | for itemView in views { 86 | let progress = progressForFeatureView(itemView) 87 | itemView.setProgress(progress) 88 | } 89 | } 90 | 91 | private func progressForFeatureView(_ view: NativeOnboardingFeatureView) -> CGFloat { 92 | let offsetY = scrollView.contentOffset.y + scrollView.safeAreaInsets.top + scrollView.frame.height - scrollView.safeAreaInsets.bottom 93 | let correction: CGFloat = NativeLayout.Spaces.Scroll.bottom_inset_reach_end 94 | let topSafeArea = scrollView.safeAreaInsets.top + correction 95 | let startPosition = view.frame.origin.y + topSafeArea 96 | let progress = (offsetY - startPosition) / view.frame.height 97 | if progress < .zero { return .zero } 98 | if progress > 1 { return 1 } 99 | return progress 100 | } 101 | } 102 | #endif 103 | -------------------------------------------------------------------------------- /Sources/NativeUIKit/Table/Empty/NativeEmptyTableViewCell.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #if canImport(UIKit) && (os(iOS)) 23 | import UIKit 24 | import SparrowKit 25 | 26 | open class NativeEmptyTableViewCell: SPTableViewCell { 27 | 28 | // MARK: - Data 29 | 30 | open var verticalMargins: Margins = .medium { 31 | didSet { 32 | contentView.layoutMargins.top = self.verticalMargins.value 33 | contentView.layoutMargins.bottom = self.verticalMargins.value 34 | } 35 | } 36 | 37 | // MARK: - Views 38 | 39 | public let placeholderView = NativePlaceholderView().do { 40 | $0.isUserInteractionEnabled = false 41 | } 42 | 43 | // MARK: - Init 44 | 45 | open override func commonInit() { 46 | super.commonInit() 47 | selectionStyle = .none 48 | contentView.addSubview(placeholderView) 49 | updateBackgroundColorByParent() 50 | } 51 | 52 | // MARK: - Lifecycle 53 | 54 | open override func tintColorDidChange() { 55 | super.tintColorDidChange() 56 | updateBackgroundColorByParent() 57 | } 58 | 59 | // MARK: - Layout 60 | 61 | open override func layoutSubviews() { 62 | super.layoutSubviews() 63 | placeholderView.frame.origin.y = contentView.layoutMargins.top 64 | placeholderView.setWidthAndFit(width: contentView.layoutWidth) 65 | placeholderView.setXCenter() 66 | } 67 | 68 | open override func sizeThatFits(_ size: CGSize) -> CGSize { 69 | let superSize = super.sizeThatFits(size) 70 | layoutSubviews() 71 | let calculatedHeight = placeholderView.frame.maxY + contentView.layoutMargins.bottom 72 | let bottomInset = calculatedHeight * (1 - 0.94) 73 | return .init(width: superSize.width, height: calculatedHeight + bottomInset) 74 | } 75 | 76 | // MARK: - Actions 77 | 78 | private func updateBackgroundColorByParent() { 79 | if #available(iOS 13.0, *) { 80 | backgroundColor = UIColor.systemDownedGroupedBackground 81 | } 82 | /*if let superView = self.superview?.superview { 83 | let superViewBackgroundColor = superView.backgroundColor ?? .clear 84 | if superViewBackgroundColor == .systemGroupedBackground { 85 | backgroundColor = UIColor.init( 86 | light: superViewBackgroundColor.mixWithColor(.darkGray, amount: 0.09).mixWithColor(.systemBlue, amount: 0.01), 87 | dark: .secondarySystemGroupedBackground.alpha(0.7) 88 | ) 89 | } else { 90 | // Here not implemented becouse not using never for now. 91 | backgroundColor = .red 92 | } 93 | }*/ 94 | } 95 | 96 | // MARK: - Models 97 | 98 | public enum Margins { 99 | 100 | case small 101 | case medium 102 | case large 103 | 104 | var value: CGFloat { 105 | switch self { 106 | case .small: return 16 107 | case .medium: return 32 108 | case .large: return 48 109 | } 110 | } 111 | } 112 | } 113 | #endif 114 | -------------------------------------------------------------------------------- /Example App/NativeUIKit.xcodeproj/xcshareddata/xcschemes/watchOS Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 46 | 47 | 57 | 61 | 67 | 68 | 69 | 70 | 76 | 80 | 86 | 87 | 88 | 89 | 95 | 96 | 97 | 98 | 100 | 101 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /Sources/NativeUIKit/Controls/Buttons/NativeLargeActionButton.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #if canImport(UIKit) && (os(iOS)) 23 | import UIKit 24 | import SparrowKit 25 | 26 | /** 27 | NativeUIKit: Large action button. 28 | Usually using at bottom of screen. 29 | */ 30 | open class NativeLargeActionButton: SPDimmedButton { 31 | 32 | // MARK: - Init 33 | 34 | open override func commonInit() { 35 | super.commonInit() 36 | insetsLayoutMarginsFromSafeArea = false 37 | preservesSuperviewLayoutMargins = false 38 | titleLabel?.font = UIFont.preferredFont(forTextStyle: .headline, addPoints: 1) 39 | titleLabel?.numberOfLines = 1 40 | highlightOpacity = NativeAppearance.actionable_element_highlight_opacity 41 | titleImageInset = 6 42 | contentEdgeInsets = .init(horizontal: 10, vertical: 12) 43 | roundCorners(radius: NativeAppearance.Corners.readable_area) 44 | } 45 | 46 | // MARK: - Public 47 | 48 | /** 49 | NativeUIKit: Wrapper of set content and color of button. 50 | 51 | - parameter title: Text which using like title. 52 | - parameter icon: Object of `UIImage`, using like icon. 53 | - parameter colorise: Color of button in default state. 54 | */ 55 | public func set(title: String, icon: UIImage?, colorise: SPDimmedButton.Colorise) { 56 | setTitle(title) 57 | if let icon = icon { 58 | setImage(icon.alwaysTemplate) 59 | } 60 | applyDefaultAppearance(with: colorise) 61 | } 62 | 63 | // MARK: - Layout 64 | 65 | /** 66 | NativeUIKit: Layout wrapper. Native way for layout button. 67 | */ 68 | open func layout(y: CGFloat) { 69 | guard let superview = self.superview else { return } 70 | sizeToFit() 71 | let width = min(superview.readableWidth, NativeLayout.Sizes.actionable_area_maximum_width) 72 | frame.setWidth(width) 73 | setXCenter() 74 | frame.origin.y = y 75 | } 76 | 77 | /** 78 | NativeUIKit: Layout wrapper. Native way for layout button. 79 | */ 80 | open func layout(maxY: CGFloat) { 81 | guard let superview = self.superview else { return } 82 | sizeToFit() 83 | let width = min(superview.readableWidth, NativeLayout.Sizes.actionable_area_maximum_width) 84 | frame.setWidth(width) 85 | setXCenter() 86 | frame.setMaxY(maxY) 87 | } 88 | 89 | open override func sizeThatFits(_ size: CGSize) -> CGSize { 90 | let superSize = super.sizeThatFits(size) 91 | let width = superSize.width 92 | 93 | var height = superSize.height 94 | if let titleLabel = titleLabel, let imageView = imageView, let _ = imageView.image { 95 | if titleLabel.frame.height > .zero && imageView.frame.height > .zero { 96 | let imageCorrection = imageView.frame.height - titleLabel.frame.height 97 | height -= imageCorrection 98 | } 99 | } 100 | 101 | return CGSize(width: width, height: height) 102 | } 103 | 104 | // MARK: - Wrappers 105 | 106 | public static var defaultCornerRadius: CGFloat { 107 | let button = NativeLargeActionButton() 108 | return button.layer.cornerRadius 109 | } 110 | 111 | public static var defaultHeight: CGFloat { 112 | let button = NativeLargeActionButton() 113 | button.setTitle(.space) 114 | button.sizeToFit() 115 | return button.frame.height 116 | } 117 | } 118 | #endif 119 | -------------------------------------------------------------------------------- /Sources/NativeUIKit/Controllers/Complex/Onboarding/Features/NativeOnboardingFeatureView.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #if canImport(UIKit) && (os(iOS)) 23 | import UIKit 24 | import SparrowKit 25 | 26 | public class NativeOnboardingFeatureView: SPView { 27 | 28 | // MARK: - Data 29 | 30 | let model: FeatureModel 31 | 32 | // MARK: - Views 33 | 34 | let iconView = SPImageView().do { 35 | $0.contentMode = .scaleAspectFit 36 | } 37 | 38 | let titleLabel = SPLabel().do { 39 | $0.font = .preferredFont(forTextStyle: .title3, weight: .semibold) 40 | if #available(iOS 13.0, *) { 41 | $0.textColor = .label 42 | } else { 43 | $0.textColor = .black 44 | } 45 | $0.numberOfLines = .zero 46 | } 47 | 48 | let descriptionLabel = SPLabel().do { 49 | $0.font = .preferredFont(forTextStyle: .body) 50 | if #available(iOS 13.0, *) { 51 | $0.textColor = .secondaryLabel 52 | } else { 53 | $0.textColor = .black 54 | } 55 | $0.numberOfLines = .zero 56 | } 57 | 58 | // MARK: - Init 59 | 60 | init(with model: FeatureModel) { 61 | self.model = model 62 | super.init() 63 | iconView.image = model.iconImage 64 | titleLabel.text = model.title 65 | descriptionLabel.text = model.description 66 | } 67 | 68 | required init?(coder aDecoder: NSCoder) { 69 | fatalError("init(coder:) has not been implemented") 70 | } 71 | 72 | public override func commonInit() { 73 | super.commonInit() 74 | backgroundColor = .clear 75 | roundCorners(radius: NativeLayout.Spaces.default_less) 76 | layoutMargins = .init(horizontal: NativeLayout.Spaces.default_more, vertical: NativeLayout.Spaces.default_more) 77 | addSubviews(iconView, titleLabel, descriptionLabel) 78 | } 79 | 80 | // MARK: - Layout 81 | 82 | public override func layoutSubviews() { 83 | super.layoutSubviews() 84 | iconView.frame = .init(side: 42) 85 | iconView.frame.origin.y = layoutMargins.top 86 | iconView.frame.origin.x = layoutMargins.left 87 | 88 | let labelWidth = layoutWidth - iconView.frame.width - NativeLayout.Spaces.default 89 | 90 | titleLabel.layoutDynamicHeight(width: labelWidth) 91 | titleLabel.setMaxXToSuperviewRightMargin() 92 | titleLabel.frame.origin.y = layoutMargins.top 93 | 94 | descriptionLabel.layoutDynamicHeight(width: labelWidth) 95 | descriptionLabel.frame.origin.x = titleLabel.frame.origin.x 96 | descriptionLabel.frame.origin.y = titleLabel.frame.maxY + NativeLayout.Spaces.step 97 | } 98 | 99 | public override func sizeThatFits(_ size: CGSize) -> CGSize { 100 | layoutSubviews() 101 | return .init(width: size.width, height: descriptionLabel.frame.maxY + layoutMargins.bottom) 102 | } 103 | 104 | // MARK: - Actions 105 | 106 | func setProgress(_ value: CGFloat) { 107 | if #available(iOS 13.0, *) { 108 | self.backgroundColor = .secondarySystemBackground.alpha(1 - value) 109 | } 110 | } 111 | 112 | // MARK: - Models 113 | 114 | /** 115 | Wrapper of data for feature model. 116 | 117 | - important: Recomended use custom tint color for icons like native. 118 | */ 119 | public class FeatureModel { 120 | 121 | let iconImage: UIImage 122 | let title: String 123 | let description: String 124 | 125 | public init(iconImage: UIImage, title: String, description: String) { 126 | self.iconImage = iconImage 127 | self.title = title 128 | self.description = description 129 | } 130 | } 131 | } 132 | #endif 133 | -------------------------------------------------------------------------------- /Sources/NativeUIKit/Labels/NativeModalHeaderView.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #if canImport(UIKit) && (os(iOS)) 23 | import UIKit 24 | import SparrowKit 25 | 26 | /** 27 | NativeUIKit: Usually use in modal screen with large title and subtitle. 28 | Image is optional. 29 | */ 30 | open class NativeModalHeaderView: SPView { 31 | 32 | // MARK: - Views 33 | 34 | public let iconImageView = SPImageView().do { 35 | $0.contentMode = .scaleAspectFit 36 | $0.tintColor = UIColor.tint 37 | } 38 | 39 | public let titleLabel = SPLabel().do { 40 | $0.numberOfLines = .zero 41 | $0.font = UIFont.preferredFont(forTextStyle: .largeTitle, weight: .bold) 42 | if #available(iOS 13.0, *) { 43 | $0.textColor = .label 44 | } else { 45 | $0.textColor = .black 46 | } 47 | $0.textAlignment = .center 48 | } 49 | 50 | public let subtitleLabel = SPLabel().do { 51 | $0.numberOfLines = .zero 52 | $0.font = UIFont.preferredFont(forTextStyle: .body) 53 | if #available(iOS 13.0, *) { 54 | $0.textColor = .secondaryLabel 55 | } else { 56 | $0.textColor = .black.alpha(0.5) 57 | } 58 | $0.textAlignment = .center 59 | } 60 | 61 | // MARK: - Init 62 | 63 | public override init() { 64 | super.init() 65 | } 66 | 67 | public init(image: UIImage?, title: String, subtitle: String) { 68 | super.init() 69 | titleLabel.text = title 70 | subtitleLabel.text = subtitle 71 | iconImageView.image = image 72 | } 73 | 74 | public required init?(coder aDecoder: NSCoder) { 75 | fatalError("init(coder:) has not been implemented") 76 | } 77 | 78 | open override func commonInit() { 79 | super.commonInit() 80 | backgroundColor = .clear 81 | layoutMargins = .zero 82 | addSubview(titleLabel) 83 | addSubview(subtitleLabel) 84 | addSubview(iconImageView) 85 | } 86 | 87 | // MARK: - Layout 88 | 89 | /** 90 | NativeUIKit: Layout wrapper. Native way for layout button. 91 | */ 92 | open func layout(y: CGFloat) { 93 | guard let superview = self.superview else { return } 94 | let width = min(superview.readableWidth, NativeLayout.Sizes.not_actionable_area_maximum_width) 95 | setWidthAndFit(width: width) 96 | setXCenter() 97 | frame.origin.y = y 98 | } 99 | 100 | /** 101 | NativeUIKit: Layout wrapper. Native way for layout button. 102 | */ 103 | open func layout(maxY: CGFloat) { 104 | guard let superview = self.superview else { return } 105 | let width = min(superview.readableWidth, NativeLayout.Sizes.not_actionable_area_maximum_width) 106 | setWidthAndFit(width: width) 107 | setXCenter() 108 | frame.setMaxY(maxY) 109 | } 110 | 111 | open override func layoutSubviews() { 112 | super.layoutSubviews() 113 | iconImageView.frame = .init(side: NativeLayout.Spaces.default_double * 2) 114 | iconImageView.setXCenter() 115 | iconImageView.frame.origin.y = layoutMargins.top 116 | 117 | if let _ = iconImageView.image { 118 | titleLabel.layoutDynamicHeight(x: .zero, y: iconImageView.frame.maxY + 12, width: layoutWidth) 119 | } else { 120 | titleLabel.layoutDynamicHeight(x: .zero, y: .zero, width: layoutWidth) 121 | } 122 | 123 | subtitleLabel.layoutDynamicHeight(x: .zero, y: titleLabel.frame.maxY + 4, width: titleLabel.frame.width) 124 | } 125 | 126 | open override func sizeThatFits(_ size: CGSize) -> CGSize { 127 | layoutSubviews() 128 | return .init(width: size.width, height: subtitleLabel.frame.maxY + layoutMargins.bottom) 129 | } 130 | } 131 | #endif 132 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | hello@ivanvorobei.io. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /Sources/NativeUIKit/Controllers/Complex/Profile/NativeProfileHeaderView.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #if canImport(UIKit) && (os(iOS)) 23 | import UIKit 24 | import SparrowKit 25 | 26 | @available(iOS 13.0, *) 27 | open class NativeProfileHeaderView: SPView { 28 | 29 | // MARK: - Public 30 | 31 | public let avatarView = NativeAvatarView().do { 32 | $0.avatarAppearance = .placeholder 33 | $0.isEditable = true 34 | } 35 | 36 | public let nameLabel = SPLabel().do { 37 | $0.font = UIFont.preferredFont(forTextStyle: .title2, weight: .semibold, addPoints: 2) 38 | $0.textColor = .label 39 | $0.numberOfLines = 1 40 | $0.textAlignment = .center 41 | // $0.adjustsFontSizeToFitWidth = true 42 | // $0.minimumScaleFactor = 0.5 43 | $0.text = nil 44 | } 45 | 46 | public let namePlaceholderLabel = SPLabel().do { 47 | $0.font = UIFont.preferredFont(forTextStyle: .title2, weight: .semibold, addPoints: 2) 48 | $0.textColor = .secondaryLabel 49 | $0.numberOfLines = 1 50 | $0.textAlignment = .center 51 | // $0.adjustsFontSizeToFitWidth = true 52 | // $0.minimumScaleFactor = 0.5 53 | $0.text = nil 54 | } 55 | 56 | // Add tap to clipboard 57 | public let emailButton = SPDimmedButton().do { 58 | $0.titleLabel?.font = UIFont.preferredFont(forTextStyle: .body, weight: .regular, addPoints: -2) 59 | // $0.titleLabel?.adjustsFontSizeToFitWidth = true 60 | // $0.titleLabel?.minimumScaleFactor = 0.5 61 | $0.titleImageInset = 2 62 | $0.titleLabel?.textAlignment = .center 63 | } 64 | 65 | // MARK: - Private 66 | 67 | private var extendView = SPView() 68 | 69 | var nameTextObserer: NSKeyValueObservation? 70 | 71 | // MARK: - Init 72 | 73 | open override func commonInit() { 74 | super.commonInit() 75 | backgroundColor = .clear 76 | addSubviews([extendView, avatarView, nameLabel, namePlaceholderLabel, emailButton]) 77 | 78 | layoutMargins = .init( 79 | top: NativeLayout.Spaces.default_half, 80 | left: NativeLayout.Spaces.default_double, 81 | bottom: NativeLayout.Spaces.default_half, 82 | right: NativeLayout.Spaces.default_double 83 | ) 84 | 85 | self.nameLabel.isHidden = !(self.nameLabel == self.usingNameLabel) 86 | self.namePlaceholderLabel.isHidden = !(self.namePlaceholderLabel == self.usingNameLabel) 87 | 88 | nameTextObserer = nameLabel.observe(\.text) { [weak self] _, _ in 89 | guard let self = self else { return } 90 | self.nameLabel.isHidden = !(self.nameLabel == self.usingNameLabel) 91 | self.namePlaceholderLabel.isHidden = !(self.namePlaceholderLabel == self.usingNameLabel) 92 | } 93 | } 94 | 95 | // MARK: - Ovveride 96 | 97 | open override var backgroundColor: UIColor? { 98 | didSet { 99 | extendView.backgroundColor = backgroundColor 100 | } 101 | } 102 | 103 | // MARK: - Layout 104 | 105 | private var usingNameLabel: SPLabel { 106 | if nameLabel.text?.isEmptyContent ?? true { 107 | return namePlaceholderLabel 108 | } else { 109 | return nameLabel 110 | } 111 | } 112 | 113 | open override func layoutSubviews() { 114 | super.layoutSubviews() 115 | extendView.frame = .init(x: .zero, maxY: .zero, width: frame.width, height: 1000) 116 | 117 | avatarView.sizeToFit() 118 | avatarView.setXCenter() 119 | avatarView.frame.origin.y = layoutMargins.top 120 | 121 | let textWidth: CGFloat = layoutWidth * 0.8 122 | 123 | nameLabel.layoutDynamicHeight(width: textWidth) 124 | nameLabel.setXCenter() 125 | nameLabel.frame.origin.y = avatarView.frame.maxY + NativeLayout.Spaces.default_half 126 | 127 | namePlaceholderLabel.layoutDynamicHeight(width: textWidth) 128 | namePlaceholderLabel.setXCenter() 129 | namePlaceholderLabel.frame.origin.y = nameLabel.frame.origin.y 130 | 131 | emailButton.sizeToFit() 132 | if emailButton.frame.width > textWidth { 133 | emailButton.frame.setWidth(textWidth) 134 | } 135 | emailButton.setXCenter() 136 | emailButton.frame.origin.y = usingNameLabel.frame.maxY 137 | } 138 | 139 | open override func sizeThatFits(_ size: CGSize) -> CGSize { 140 | layoutSubviews() 141 | return .init(width: frame.width, height: emailButton.frame.maxY + layoutMargins.bottom) 142 | } 143 | } 144 | #endif 145 | -------------------------------------------------------------------------------- /Sources/NativeUIKit/Views/NativePromoView.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #if canImport(UIKit) && (os(iOS)) 23 | import UIKit 24 | import SparrowKit 25 | import SPPerspective 26 | 27 | open class NativePromoView: SPView { 28 | 29 | // MARK: - Views 30 | 31 | public let titleLabel = SPLabel().do { 32 | $0.font = UIFont.preferredFont(forTextStyle: .title2, weight: .semibold) 33 | if #available(iOS 13.0, *) { 34 | $0.textColor = .label 35 | } else { 36 | $0.textColor = .black 37 | } 38 | $0.numberOfLines = .zero 39 | $0.textAlignment = .center 40 | } 41 | 42 | public let descriptionLabel = SPLabel().do { 43 | $0.font = UIFont.preferredFont(forTextStyle: .body, weight: .regular) 44 | if #available(iOS 13.0, *) { 45 | $0.textColor = .secondaryLabel 46 | } else { 47 | $0.textColor = .black.alpha(0.5) 48 | } 49 | $0.numberOfLines = .zero 50 | $0.textAlignment = .center 51 | } 52 | 53 | public let iconView = SPImageView().do { 54 | $0.backgroundColor = .clear 55 | } 56 | 57 | public let button = SPDimmedButton().do { 58 | $0.applyDefaultAppearance(with: .tintedContent) 59 | $0.titleLabel?.font = UIFont.preferredFont(forTextStyle: .body, weight: .semibold, addPoints: 2) 60 | } 61 | 62 | public let areaView = SPView().do { 63 | if #available(iOS 13.0, *) { 64 | $0.backgroundColor = UIColor.secondarySystemBackground 65 | } 66 | $0.layer.cornerRadius = 15 67 | } 68 | 69 | // MARK: - Init 70 | 71 | open override func commonInit() { 72 | super.commonInit() 73 | layoutMargins = .zero 74 | addSubview(areaView) 75 | addSubview(titleLabel) 76 | addSubview(descriptionLabel) 77 | addSubview(iconView) 78 | addSubview(button) 79 | areaView.layoutMargins = .init(horizontal: 24, vertical: 24) 80 | iconView.applyPerspective(.iOS14WidgetAnimatable) 81 | } 82 | 83 | // MARK: - Layout 84 | 85 | open func layout(y: CGFloat) { 86 | guard let superview = self.superview else { return } 87 | var width = min(superview.readableWidth, NativeLayout.Sizes.actionable_area_maximum_width) 88 | if (width == .zero) && (superview.layoutWidth != .zero) { width = superview.layoutWidth } 89 | setWidthAndFit(width: width) 90 | setXCenter() 91 | frame.origin.y = y 92 | } 93 | 94 | open override func layoutSubviews() { 95 | super.layoutSubviews() 96 | areaView.frame = .init(x: layoutMargins.left, y: layoutMargins.top, width: layoutWidth, height: areaView.frame.height) 97 | 98 | let labelsWidth = areaView.layoutWidth 99 | 100 | titleLabel.layoutDynamicHeight(width: labelsWidth) 101 | titleLabel.setXCenter() 102 | titleLabel.frame.origin.y = areaView.frame.origin.y + areaView.layoutMargins.top 103 | 104 | descriptionLabel.layoutDynamicHeight(width: labelsWidth) 105 | descriptionLabel.setXCenter() 106 | descriptionLabel.frame.origin.y = titleLabel.frame.maxY + 3 107 | 108 | let iconSideSize = min(titleLabel.frame.width * 0.5, 120) 109 | iconView.frame.setWidth(iconSideSize) 110 | iconView.frame.setHeight(iconSideSize) 111 | iconView.setXCenter() 112 | iconView.frame.origin.y = descriptionLabel.frame.maxY + 16 113 | 114 | areaView.frame.setHeight(iconView.frame.origin.y + iconView.frame.height / 2) 115 | 116 | button.sizeToFit() 117 | button.setXCenter() 118 | button.frame.origin.y = iconView.frame.maxY + 24 119 | } 120 | 121 | open override func sizeThatFits(_ size: CGSize) -> CGSize { 122 | layoutSubviews() 123 | return .init(width: size.width, height: button.frame.maxY + layoutMargins.bottom) 124 | } 125 | } 126 | 127 | open class NativePromoContainerView: SPView { 128 | 129 | // MARK: - Views 130 | 131 | public let promoView = NativePromoView() 132 | 133 | // MARK: - Init 134 | 135 | open override func commonInit() { 136 | super.commonInit() 137 | layoutMargins = .zero 138 | addSubview(promoView) 139 | } 140 | 141 | // MARK: - Layout 142 | 143 | open override func layoutSubviews() { 144 | super.layoutSubviews() 145 | promoView.layout(y: layoutMargins.top) 146 | } 147 | 148 | open override func sizeThatFits(_ size: CGSize) -> CGSize { 149 | layoutSubviews() 150 | return .init(width: size.width, height: promoView.frame.maxY + layoutMargins.bottom) 151 | } 152 | } 153 | #endif 154 | -------------------------------------------------------------------------------- /Sources/NativeUIKit/Controllers/Complex/Onboarding/Actinos/NativeOnbiardingActionButton.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #if canImport(UIKit) && (os(iOS)) 23 | import UIKit 24 | import SparrowKit 25 | 26 | public class NativeOnbiardingActionButton: SPDimmedButton { 27 | 28 | // MARK: - Data 29 | 30 | private let model: ActionModel 31 | 32 | // MARK: - Views 33 | 34 | let disclouserIndicator = SPDimmedButton().do { 35 | if #available(iOS 13, *) { 36 | $0.setImage(.system("chevron.right", font: .preferredFont(forTextStyle: .body, weight: .medium)).alwaysTemplate) 37 | $0.tintColor = .tertiaryLabel 38 | } 39 | } 40 | 41 | let actionIconView = SPImageView().do { 42 | $0.contentMode = .scaleAspectFit 43 | } 44 | 45 | let actionTitleLabel = SPLabel().do { 46 | $0.font = .preferredFont(forTextStyle: .body, weight: .semibold) 47 | if #available(iOS 13.0, *) { 48 | $0.textColor = .label 49 | } else { 50 | $0.textColor = .black 51 | } 52 | $0.numberOfLines = .zero 53 | } 54 | 55 | let actionDescriptionLabel = SPLabel().do { 56 | $0.font = .preferredFont(forTextStyle: .subheadline) 57 | if #available(iOS 13.0, *) { 58 | $0.textColor = .secondaryLabel 59 | } else { 60 | $0.textColor = .black 61 | } 62 | $0.numberOfLines = .zero 63 | } 64 | 65 | // MARK: - Init 66 | 67 | init(with model: ActionModel) { 68 | self.model = model 69 | super.init() 70 | actionIconView.image = model.iconImage 71 | actionTitleLabel.text = model.title 72 | actionDescriptionLabel.text = model.description 73 | 74 | addTarget(self, action: #selector(self.didTap), for: .touchUpInside) 75 | } 76 | 77 | required init?(coder aDecoder: NSCoder) { 78 | fatalError("init(coder:) has not been implemented") 79 | } 80 | 81 | public override func commonInit() { 82 | super.commonInit() 83 | higlightStyle = .background 84 | if #available(iOS 13.0, *) { 85 | applyDefaultAppearance(with: .init(content: .label, background: .secondarySystemBackground)) 86 | } 87 | roundCorners(radius: NativeLayout.Spaces.default_less) 88 | layoutMargins = .init(horizontal: NativeLayout.Spaces.default_more, vertical: NativeLayout.Spaces.default_more) 89 | addSubviews(actionIconView, actionTitleLabel, actionDescriptionLabel, disclouserIndicator) 90 | } 91 | 92 | // MARK: - Layout 93 | 94 | public override func layoutSubviews() { 95 | super.layoutSubviews() 96 | actionIconView.frame = .init(side: 28) 97 | actionIconView.frame.origin.x = layoutMargins.left 98 | 99 | disclouserIndicator.sizeToFit() 100 | disclouserIndicator.setMaxXToSuperviewRightMargin() 101 | 102 | let leftSpace: CGFloat = NativeLayout.Spaces.default_more 103 | let rightSpace: CGFloat = NativeLayout.Spaces.default 104 | let labelWidth = layoutWidth - actionIconView.frame.width - disclouserIndicator.frame.width - leftSpace - rightSpace 105 | 106 | actionTitleLabel.layoutDynamicHeight(width: labelWidth) 107 | actionTitleLabel.frame.origin.x = actionIconView.frame.maxX + leftSpace 108 | actionTitleLabel.frame.origin.y = layoutMargins.top 109 | 110 | actionDescriptionLabel.layoutDynamicHeight(width: labelWidth) 111 | actionDescriptionLabel.frame.origin.x = actionTitleLabel.frame.origin.x 112 | actionDescriptionLabel.frame.origin.y = actionTitleLabel.frame.maxY + NativeLayout.Spaces.step 113 | 114 | disclouserIndicator.setYCenter() 115 | actionIconView.setYCenter() 116 | } 117 | 118 | public override func sizeThatFits(_ size: CGSize) -> CGSize { 119 | layoutSubviews() 120 | return .init(width: size.width, height: actionDescriptionLabel.frame.maxY + layoutMargins.bottom) 121 | } 122 | 123 | // MARK: - Action 124 | 125 | @objc func didTap() { 126 | self.model.action() 127 | } 128 | 129 | // MARK: - Models 130 | 131 | /** 132 | Wrapper of data for action model. 133 | 134 | - important: Recomended save app tint color for icons like native. 135 | */ 136 | public class ActionModel { 137 | 138 | let iconImage: UIImage 139 | let title: String 140 | let description: String 141 | let action: ()->Void 142 | 143 | public init(iconImage: UIImage, title: String, description: String, action: @escaping ()->Void) { 144 | self.iconImage = iconImage 145 | self.title = title 146 | self.description = description 147 | self.action = action 148 | } 149 | } 150 | } 151 | #endif 152 | -------------------------------------------------------------------------------- /Sources/NativeUIKit/Views/NativePlaceholderView.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Ivan Vorobei (hello@ivanvorobei.io) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #if canImport(UIKit) && (os(iOS)) 23 | import UIKit 24 | import SparrowKit 25 | 26 | open class NativePlaceholderView: SPButton { 27 | 28 | // MARK: - Views 29 | 30 | public let iconImageView = SPImageView().do { 31 | $0.contentMode = .scaleAspectFit 32 | } 33 | 34 | public let headerLabel = SPLabel().do { 35 | $0.numberOfLines = .zero 36 | $0.textAlignment = .center 37 | $0.font = UIFont.preferredFont(forTextStyle: .title1, weight: .bold) 38 | } 39 | 40 | public let descriptionLabel = SPLabel().do { 41 | $0.numberOfLines = .zero 42 | $0.textAlignment = .center 43 | $0.font = UIFont.preferredFont(forTextStyle: .body, weight: .regular) 44 | } 45 | 46 | // MARK: - Init 47 | 48 | public override init() { 49 | super.init() 50 | } 51 | 52 | public init(icon: UIImage, title: String, subtitle: String) { 53 | super.init() 54 | iconImageView.image = icon.alwaysTemplate 55 | headerLabel.text = title 56 | descriptionLabel.text = subtitle 57 | } 58 | 59 | public required init?(coder aDecoder: NSCoder) { 60 | fatalError("init(coder:) has not been implemented") 61 | } 62 | 63 | open override func commonInit() { 64 | super.commonInit() 65 | layoutMargins = .zero 66 | if #available(iOS 13.0, *) { 67 | tintColor = .tertiaryLabel 68 | } else { 69 | tintColor = UIColor.black.alpha(0.3) 70 | } 71 | backgroundColor = .clear 72 | iconImageView.tintColor = tintColor 73 | addSubview(iconImageView) 74 | headerLabel.textColor = tintColor 75 | addSubview(headerLabel) 76 | descriptionLabel.textColor = tintColor 77 | addSubview(descriptionLabel) 78 | } 79 | 80 | // MARK: - Ovveride 81 | 82 | open override var isHighlighted: Bool { 83 | didSet { 84 | UIView.animate(withDuration: 0.1, delay: 0, options: [.beginFromCurrentState, .allowUserInteraction], animations: { 85 | self.alpha = self.isHighlighted ? NativeAppearance.actionable_element_highlight_opacity : 1 86 | }, completion: nil) 87 | } 88 | } 89 | 90 | // MARK: - Actions 91 | 92 | open func setVisible(_ state: Bool, animated: Bool) { 93 | let work = { [weak self] in 94 | guard let self = self else { return } 95 | self.alpha = state ? 1 : .zero 96 | } 97 | if animated { 98 | UIView.animate(withDuration: 0.45, delay: .zero, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: [.allowUserInteraction, .beginFromCurrentState], animations: { 99 | work() 100 | }, completion: nil) 101 | } else { 102 | work() 103 | } 104 | } 105 | 106 | // MARK: - Layout 107 | 108 | /** 109 | NativeUIKit: Layout wrapper. Native way for layout placeholder. 110 | */ 111 | open func layout(y: CGFloat) { 112 | guard let superview = self.superview else { return } 113 | sizeToFit() 114 | let width = min(superview.readableWidth, NativeLayout.Sizes.actionable_area_maximum_width) 115 | frame.setWidth(width) 116 | setXCenter() 117 | frame.origin.y = y 118 | } 119 | 120 | /** 121 | NativeUIKit: Layout wrapper. Native way for layout placeholder. 122 | */ 123 | open func layoutCenter() { 124 | guard let superview = self.superview else { return } 125 | let width = min(superview.readableWidth, NativeLayout.Sizes.actionable_area_maximum_width) 126 | setWidthAndFit(width: width) 127 | setXCenter() 128 | switch superview { 129 | case _ as UICollectionView: 130 | center.y = (superview.layoutHeight / 2) * 0.94 131 | case _ as UITableView: 132 | center.y = (superview.layoutHeight / 2) * 0.94 133 | default: 134 | center.y = (superview.frame.height / 2) * 0.94 135 | } 136 | 137 | } 138 | 139 | open override func layoutSubviews() { 140 | super.layoutSubviews() 141 | iconImageView.sizeToFit() 142 | iconImageView.setXCenter() 143 | iconImageView.frame.origin.y = layoutMargins.top 144 | headerLabel.layoutDynamicHeight(width: layoutWidth) 145 | headerLabel.frame.origin.y = iconImageView.frame.maxY + 12 146 | headerLabel.setXCenter() 147 | descriptionLabel.layoutDynamicHeight(width: layoutWidth) 148 | descriptionLabel.frame.origin.y = headerLabel.frame.maxY + 4 149 | descriptionLabel.setXCenter() 150 | } 151 | 152 | open override func sizeThatFits(_ size: CGSize) -> CGSize { 153 | layoutSubviews() 154 | return .init(width: size.width, height: descriptionLabel.frame.maxY + layoutMargins.bottom) 155 | } 156 | } 157 | #endif 158 | -------------------------------------------------------------------------------- /Assets/Readme/Elements/NativeLargeActionButton.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | NativeLargeActionButton 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | --------------------------------------------------------------------------------